]> git.saurik.com Git - apple/dyld.git/commitdiff
dyld-625.13.tar.gz macos-1014 v625.13
authorApple <opensource@apple.com>
Thu, 4 Oct 2018 22:11:26 +0000 (22:11 +0000)
committerApple <opensource@apple.com>
Thu, 4 Oct 2018 22:11:26 +0000 (22:11 +0000)
215 files changed:
bin/expand.pl [deleted file]
bin/expand.rb [new file with mode: 0755]
configs/dyld.xcconfig
configs/libdyld.xcconfig
configs/update_dyld_shared_cache.xcconfig
configs/update_dyld_sim_shared_cache.xcconfig
doc/tracing/dyld.codes [new file with mode: 0644]
doc/tracing/dyld.plist [new file with mode: 0644]
dyld.xcodeproj/project.pbxproj
dyld3/APIs.cpp
dyld3/APIs.h
dyld3/APIs_macOS.cpp
dyld3/AllImages.cpp
dyld3/AllImages.h
dyld3/Array.h [new file with mode: 0644]
dyld3/Closure.cpp [new file with mode: 0644]
dyld3/Closure.h [new file with mode: 0644]
dyld3/ClosureBuffer.cpp [deleted file]
dyld3/ClosureBuffer.h [deleted file]
dyld3/ClosureBuilder.cpp [new file with mode: 0644]
dyld3/ClosureBuilder.h [new file with mode: 0644]
dyld3/ClosureFileSystem.h [new file with mode: 0644]
dyld3/ClosureFileSystemPhysical.cpp [new file with mode: 0644]
dyld3/ClosureFileSystemPhysical.h [new file with mode: 0644]
dyld3/ClosurePrinter.cpp [new file with mode: 0644]
dyld3/ClosurePrinter.h [new file with mode: 0644]
dyld3/ClosureWriter.cpp [new file with mode: 0644]
dyld3/ClosureWriter.h [new file with mode: 0644]
dyld3/CodeSigningTypes.h
dyld3/Diagnostics.cpp
dyld3/Diagnostics.h
dyld3/DyldCacheParser.cpp [deleted file]
dyld3/DyldCacheParser.h [deleted file]
dyld3/JSONWriter.h [new file with mode: 0644]
dyld3/LaunchCache.h [deleted file]
dyld3/LaunchCacheFormat.h [deleted file]
dyld3/LaunchCachePrinter.cpp [deleted file]
dyld3/LaunchCacheReader.cpp [deleted file]
dyld3/LaunchCacheWriter.cpp [deleted file]
dyld3/LaunchCacheWriter.h [deleted file]
dyld3/Loading.cpp
dyld3/Loading.h
dyld3/MachOAnalyzer.cpp [new file with mode: 0644]
dyld3/MachOAnalyzer.h [new file with mode: 0644]
dyld3/MachOFile.cpp [new file with mode: 0644]
dyld3/MachOFile.h [new file with mode: 0644]
dyld3/MachOLoaded.cpp [new file with mode: 0644]
dyld3/MachOLoaded.h [new file with mode: 0644]
dyld3/MachOParser.cpp [deleted file]
dyld3/MachOParser.h [deleted file]
dyld3/PathOverrides.cpp
dyld3/PathOverrides.h
dyld3/SharedCacheRuntime.cpp
dyld3/SharedCacheRuntime.h
dyld3/StartGlue.h [new file with mode: 0644]
dyld3/SupportedArchs.h [new file with mode: 0644]
dyld3/Tracing.cpp
dyld3/Tracing.h
dyld3/closured/closured.cpp [deleted file]
dyld3/closured/closuredProtocol.defs [deleted file]
dyld3/closured/closured_entitlements.plist [deleted file]
dyld3/closured/closuredtypes.h [deleted file]
dyld3/closured/com.apple.dyld.closured.plist [deleted file]
dyld3/closured/com.apple.dyld.closured.sb [deleted file]
dyld3/dyld-potential-framework-overrides [deleted file]
dyld3/libclosured-stub.cpp [deleted file]
dyld3/libdyldEntryVector.cpp
dyld3/libdyldEntryVector.h
dyld3/shared-cache/AdjustDylibSegments.cpp
dyld3/shared-cache/BuilderUtils.h
dyld3/shared-cache/BuilderUtils.mm
dyld3/shared-cache/CacheBuilder.cpp
dyld3/shared-cache/CacheBuilder.h
dyld3/shared-cache/DyldSharedCache.cpp
dyld3/shared-cache/DyldSharedCache.h
dyld3/shared-cache/FileUtils.cpp
dyld3/shared-cache/FileUtils.h
dyld3/shared-cache/ImageProxy.cpp [deleted file]
dyld3/shared-cache/ImageProxy.h [deleted file]
dyld3/shared-cache/MachOFileAbstraction.hpp
dyld3/shared-cache/Manifest.h
dyld3/shared-cache/Manifest.mm
dyld3/shared-cache/ObjC2Abstraction.hpp
dyld3/shared-cache/OptimizerBranches.cpp
dyld3/shared-cache/OptimizerLinkedit.cpp
dyld3/shared-cache/OptimizerObjC.cpp
dyld3/shared-cache/StringUtils.h
dyld3/shared-cache/dyld_cache_format.h
dyld3/shared-cache/dyld_closure_util.cpp
dyld3/shared-cache/dyld_shared_cache_builder.mm
dyld3/shared-cache/make_ios_dyld_cache.cpp
dyld3/shared-cache/mrm_shared_cache_builder.cpp [new file with mode: 0644]
dyld3/shared-cache/mrm_shared_cache_builder.h [new file with mode: 0644]
dyld3/shared-cache/multi_dyld_shared_cache_builder.mm
dyld3/shared-cache/update_dyld_shared_cache.cpp
dyld3/shared-cache/update_dyld_sim_shared_cache.cpp
include/mach-o/dyld.h
include/mach-o/dyld_images.h
include/mach-o/dyld_priv.h
include/mach-o/dyld_process_info.h
include/objc-shared-cache.h
launch-cache/Architectures.hpp
launch-cache/CacheFileAbstraction.hpp
launch-cache/MachOFileAbstraction.hpp
launch-cache/dsc_extractor.cpp
launch-cache/dsc_iterator.cpp
launch-cache/dyld_cache_format.h
launch-cache/dyld_shared_cache_util.cpp
src/ImageLoader.cpp
src/ImageLoader.h
src/ImageLoaderMachO.cpp
src/ImageLoaderMachO.h
src/ImageLoaderMachOClassic.cpp
src/ImageLoaderMachOCompressed.cpp
src/ImageLoaderMachOCompressed.h
src/ImageLoaderMegaDylib.cpp
src/ImageLoaderMegaDylib.h
src/dyld.cpp
src/dyld.h
src/dyldAPIs.cpp
src/dyldAPIsInLibSystem.cpp
src/dyldExceptions.c
src/dyldInitialization.cpp
src/dyldLibSystemInterface.h
src/dyldNew.cpp
src/dyldStartup.s
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/dyld_usage.cpp [new file with mode: 0644]
src/glue.c
src/start_glue.h [deleted file]
src/threadLocalHelpers.s
testing/build_tests.py
testing/test-cases/LC_DYLD_ENV-DYLD_LIBRARY_PATH.dtest/foo.c [new file with mode: 0644]
testing/test-cases/LC_DYLD_ENV-DYLD_LIBRARY_PATH.dtest/main.c [new file with mode: 0644]
testing/test-cases/NSCreateObjectFileImageFromFile-stress.dtest/foo.c [new file with mode: 0644]
testing/test-cases/NSCreateObjectFileImageFromFile-stress.dtest/main.cpp [new file with mode: 0644]
testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/main.c
testing/test-cases/_dyld_images_for_addresses.dtest/foo.c [new file with mode: 0644]
testing/test-cases/_dyld_images_for_addresses.dtest/main.c [new file with mode: 0644]
testing/test-cases/_dyld_is_memory_immutable.dtest/main.c
testing/test-cases/_dyld_register_for_image_loads.dtest/foo.c [new file with mode: 0644]
testing/test-cases/_dyld_register_for_image_loads.dtest/main.cxx [new file with mode: 0644]
testing/test-cases/cwd-relative-load.dtest/foo.c [new file with mode: 0644]
testing/test-cases/cwd-relative-load.dtest/main.c [new file with mode: 0644]
testing/test-cases/dladdr-basic.dtest/main.c
testing/test-cases/dladdr-dylib.dtest/foo.c
testing/test-cases/dladdr-dylib.dtest/main.c
testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/foo1.c [new file with mode: 0644]
testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/foo2.c [new file with mode: 0644]
testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/foo3.c [new file with mode: 0644]
testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-RTLD_LOCAL-hides.dtest/bar.c [new file with mode: 0644]
testing/test-cases/dlopen-RTLD_LOCAL-hides.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlopen-RTLD_LOCAL-hides.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-RTLD_NODELETE.dtest/bar.c [new file with mode: 0644]
testing/test-cases/dlopen-RTLD_NODELETE.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlopen-RTLD_NODELETE.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-atpath-restricted.dtest/bar.c [new file with mode: 0644]
testing/test-cases/dlopen-atpath-restricted.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlopen-atpath-restricted.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-basic.dtest/main.c
testing/test-cases/dlopen-flat.dtest/main.c
testing/test-cases/dlopen-haswell.dtest/a.c [new file with mode: 0644]
testing/test-cases/dlopen-haswell.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-haswell/a.c [new file with mode: 0644]
testing/test-cases/dlopen-in-init.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlopen-in-init.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-realpath.dtest/main.c
testing/test-cases/dlopen-rpath-from-dylib.dtest/bar.c [new file with mode: 0644]
testing/test-cases/dlopen-rpath-from-dylib.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-rpath-from-dylib.dtest/test.c [new file with mode: 0644]
testing/test-cases/dlopen-rpath-implicit.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlopen-rpath-implicit.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-rpath-prev-override.dtest/bad.c [new file with mode: 0644]
testing/test-cases/dlopen-rpath-prev-override.dtest/dyn.c [new file with mode: 0644]
testing/test-cases/dlopen-rpath-prev-override.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlopen-rpath-prev-override.dtest/good.c [new file with mode: 0644]
testing/test-cases/dlopen-rpath-prev-override.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-rpath-prev.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlopen-rpath-prev.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-rpath-prev.dtest/sub1.c [new file with mode: 0644]
testing/test-cases/dlopen-rpath-prev.dtest/sub2.c [new file with mode: 0644]
testing/test-cases/dtrace.dtest/main.c
testing/test-cases/dyld_abort_payload.dtest/main.c
testing/test-cases/dyld_get_image_versions.dtest/main.c [new file with mode: 0644]
testing/test-cases/dyld_get_sdk_version.dtest/main.c
testing/test-cases/dyld_process_info.dtest/main.c
testing/test-cases/dyld_process_info_notify.dtest/main.c
testing/test-cases/dyld_version_spis.dtest/main.c [new file with mode: 0644]
testing/test-cases/dylib-re-export-old-format.dtest/main.c
testing/test-cases/dylib-static-link.dtest/present.c
testing/test-cases/dylib-static-weak-link.dtest/present.c
testing/test-cases/env-DYLD_IMAGE_SUFFIX.dtest/bar.c [new file with mode: 0644]
testing/test-cases/env-DYLD_IMAGE_SUFFIX.dtest/foo.c [new file with mode: 0644]
testing/test-cases/env-DYLD_IMAGE_SUFFIX.dtest/main.c [new file with mode: 0644]
testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/main.c [new file with mode: 0644]
testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/myzlib.c [new file with mode: 0644]
testing/test-cases/interpose-malloc.dtest/interposer.c
testing/test-cases/no-shared-cache.dtest/main.c [new file with mode: 0644]
testing/test-cases/operator-new.dtest/main.cxx
testing/test-cases/shared_cache_range.dtest/main.c
testing/test-cases/symbol-resolver-basic.dtest/foo.c [new file with mode: 0644]
testing/test-cases/symbol-resolver-basic.dtest/foo2.c [new file with mode: 0644]
testing/test-cases/symbol-resolver-basic.dtest/main.c [new file with mode: 0644]
testing/test-cases/weak-coalesce.dtest/Makefile [new file with mode: 0644]
testing/test-cases/weak-coalesce.dtest/base.c [new file with mode: 0644]
testing/test-cases/weak-coalesce.dtest/base.h [new file with mode: 0644]
testing/test-cases/weak-coalesce.dtest/foo1.c [new file with mode: 0644]
testing/test-cases/weak-coalesce.dtest/foo2.c [new file with mode: 0644]
testing/test-cases/weak-coalesce.dtest/foo3.c [new file with mode: 0644]
testing/test-cases/weak-coalesce.dtest/main.c [new file with mode: 0644]

diff --git a/bin/expand.pl b/bin/expand.pl
deleted file mode 100755 (executable)
index b21ef3f..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/bin/perl 
-
-use strict;
-
-
-my $sdk      = $ENV{"SDKROOT"};
-my $availCmd = $sdk . "/usr/local/libexec/availability.pl";
-
-sub expandVersions
-{
-       my $macroPrefix = shift;
-       my $availArg = shift;
-
-       my $cmd = $availCmd . " " . $availArg;
-       my $versionList   = `$cmd`;
-       my $tmp = $versionList;
-       while ($tmp =~ m/^\s*([\S]+)(.*)$/) {
-               my $vers = $1;
-               $tmp = $2;
-
-               my $major = 0;
-               my $minor = 0;
-               my $revision = 0;
-               my $uvers;
-
-               if ($vers =~ m/^(\d+)$/) {
-                       $major = $1;
-                       $uvers = sprintf("%d_0", $major);
-               } elsif ($vers =~ m/^(\d+).(\d+)$/) {
-                       $major = $1;
-                       $minor = $2;
-                       $uvers = sprintf("%d_%d", $major, $minor);
-               } elsif ($vers =~ m/^(\d+).(\d+).(\d+)$/) {
-                       $major = $1;
-                       $minor = $2;
-                       $revision = $3;
-                       if ($revision == 0) {
-                               $uvers = sprintf("%d_%d", $major, $minor);
-                       }
-                       else {
-                               $uvers = sprintf("%d_%d_%d", $major, $minor, $revision);
-                       }
-               }
-               printf "#define %s%-18s 0x00%02X%02X%02X\n", $macroPrefix, $uvers, $major, $minor, $revision;
-       }
-}
-
-
-
-
-while(<STDIN>)
-{
-       if(m/^\/\/\@MAC_VERSION_DEFS\@$/) {
-               expandVersions("DYLD_MACOSX_VERSION_", "--macosx");
-       }
-       elsif(m/^\/\/\@IOS_VERSION_DEFS\@$/) {
-               expandVersions("DYLD_IOS_VERSION_", "--ios");
-       }
-       elsif(m/^\/\/\@WATCHOS_VERSION_DEFS\@$/) {
-               expandVersions("DYLD_WATCHOS_VERSION_", "--watchos");
-       }
-       else {
-               print $_;
-       }
-}
-
diff --git a/bin/expand.rb b/bin/expand.rb
new file mode 100755 (executable)
index 0000000..8eff336
--- /dev/null
@@ -0,0 +1,94 @@
+#!/usr/bin/env ruby
+
+require 'yaml'
+
+$availCmd = ENV["SDKROOT"] + "/usr/local/libexec/availability.pl";
+
+def versionString(vers)
+    uvers = ""
+    if vers =~ /^(\d+)$/
+        uvers = "#{$1}_0"
+    elsif vers =~ /^(\d+).(\d+)$/
+        uvers = "#{$1}_#{$2}"
+    elsif vers =~ /^(\d+).(\d+).(\d+)$/
+        if $3 == 0
+            uvers = "#{$1}_#{$2}"
+        else
+            uvers = "#{$1}_#{$2}_#{$3}"
+        end
+    end
+    uvers
+end
+
+def versionHex(vers)
+    major = 0;
+    minor = 0;
+    revision = 0;
+
+    if vers =~ /^(\d+)$/
+        major = $1.to_i;
+    elsif vers =~ /^(\d+).(\d+)$/
+        major = $1.to_i;
+        minor = $2.to_i;
+    elsif vers =~ /^(\d+).(\d+).(\d+)$/
+        major = $1.to_i;
+        minor = $2.to_i;
+        revision = $3.to_i;
+    end
+    "0x00#{major.to_s(16).rjust(2, '0')}#{minor.to_s(16).rjust(2, '0')}#{revision.to_s(16).rjust(2, '0')}"
+end
+
+def expandVersions(prefix, arg)
+    versionList = `#{$availCmd} #{arg}`.gsub(/\s+/m, ' ').strip.split(" ")
+    versionList.each { |version|
+        puts "#define #{prefix}#{versionString(version)}".ljust(48, ' ') + versionHex(version)
+    }
+end
+
+def expandPlatformVersions(prefix, platform, arg)
+    versionList = `#{$availCmd} #{arg}`.gsub(/\s+/m, ' ').strip.split(" ")
+    versionList.each { |version|
+        puts "static dyld_build_version_t dyld_platform_version_#{prefix}_#{versionString(version)}".ljust(72, ' ') + "= { .platform = #{platform}, .version = #{versionHex(version)} };"
+    }
+end
+
+def versionSetsForOSes(versionSets, key, platform, target)
+    puts "#if #{target}"
+    versionSets.each { |k,v|
+        puts "#define dyld_#{k}_os_versions dyld_platform_version_#{platform}_#{versionString(v[key].to_s)}"
+    }
+    puts "#endif /* #{target} */"
+end
+
+def expandVersionSets()
+    versionSets = YAML.load(`#{$availCmd} --sets`)
+    versionSetsForOSes(versionSets, "macos", "macOS", "TARGET_OS_OSX")
+    versionSetsForOSes(versionSets, "ios", "iOS", "TARGET_OS_IOS")
+    versionSetsForOSes(versionSets, "tvos", "tvOS", "TARGET_OS_TV")
+    versionSetsForOSes(versionSets, "watchos", "watchOS", "TARGET_OS_WATCH")
+    versionSetsForOSes(versionSets, "bridgeos", "bridgeOS", "TARGET_OS_BRIDGE")
+end
+
+ARGF.each do |line|
+    if line =~ /^\/\/\@MAC_VERSION_DEFS\@$/
+        expandVersions("DYLD_MACOSX_VERSION_", "--macosx")
+    elsif line =~ /^\/\/\@IOS_VERSION_DEFS\@$/
+        expandVersions("DYLD_IOS_VERSION_", "--ios")
+    elsif line =~ /^\/\/\@WATCHOS_VERSION_DEFS\@$/
+        expandVersions("DYLD_WATCHOS_VERSION_", "--watchos")
+    elsif line =~ /^\/\/\@MACOS_PLATFORM_VERSION_DEFS\@$/
+        expandPlatformVersions("macOS", "PLATFORM_MACOS", "--macosx")
+    elsif line =~ /^\/\/\@IOS_PLATFORM_VERSION_DEFS\@$/
+        expandPlatformVersions("iOS", "PLATFORM_IOS", "--ios")
+    elsif line =~ /^\/\/\@WATCHOS_PLATFORM_VERSION_DEFS\@$/
+        expandPlatformVersions("watchOS", "PLATFORM_WATCHOS", "--watchos")
+    elsif line =~ /^\/\/\@TVOS_PLATFORM_VERSION_DEFS\@$/
+        expandPlatformVersions("tvOS", "PLATFORM_TVOS", "--appletvos")
+    elsif line =~ /^\/\/\@BRIDGEOS_PLATFORM_VERSION_DEFS\@$/
+        expandPlatformVersions("bridgeOS", "PLATFORM_BRIDGEOS", "--bridgeos")
+    elsif line =~ /^\/\/\@VERSION_SET_DEFS\@$/
+        expandVersionSets()
+    else
+        puts line
+    end
+end
index d1ec099584ca0c9b721f682cbf02fd66e65e9518..bbae5605d652adb8855f68de2a4af5335fea7412 100644 (file)
@@ -15,7 +15,7 @@ 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
+GCC_PREPROCESSOR_DEFINITIONS = 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
+GCC_PREPROCESSOR_DEFINITIONS = DYLD_VERSION=$(RC_ProjectSourceVersion) BUILDING_DYLD=1
index 8f06e5ae65b931a68b0cdf579b31b818a80c1e96..3e66a3cca1d7864004fb8e6483133edc2b100185 100644 (file)
@@ -1,14 +1,9 @@
 
-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
+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-lcompiler_rt
+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-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-lcompiler_rt
 
 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
+IS_ZIPPERED = YES
 
index e45c7141c92595ec0e10a066417f661e54d43ddb..8b137891791fe96927ad78e64b0aad7bded08bdc 100644 (file)
@@ -1,4 +1 @@
 
-
-CODE_SIGN_ENTITLEMENTS = dyld3/shared-cache/update_dyld_shared_cache_entitlements.plist
-
index e4f7bfaf6c0f9301424f5711d1f7c48cafd0bfad..1a04e7b026cfeb96a452ad6eed8f7ab8c3eae8fe 100644 (file)
@@ -1,3 +1,9 @@
 
 #include "<DEVELOPER_DIR>/AppleInternal/XcodeConfig/PlatformSupportHost.xcconfig"
 
+
+//:configuration = Debug
+GCC_PREPROCESSOR_DEFINITIONS = BUILDING_CACHE_BUILDER=1 DEBUG=1
+
+//:configuration = Release
+GCC_PREPROCESSOR_DEFINITIONS = BUILDING_CACHE_BUILDER=1
diff --git a/doc/tracing/dyld.codes b/doc/tracing/dyld.codes
new file mode 100644 (file)
index 0000000..f69124c
--- /dev/null
@@ -0,0 +1,22 @@
+0x1f070000  dyld.static_intializer
+0x1f070004  dyld.launch_executable
+0x1f070008  dyld.map_file
+0x1f07000c  dyld.apply_fixups
+0x1f070010  dyld.attach_codesignature
+0x1f070014  dyld.build_closure
+0x1f070018  dyld.add_image_callback
+0x1f07001c  dyld.remove_image_callback
+0x1f070020  dyld.objc_image_init
+0x1f070024  dyld.objc_images_map
+0x1f070028  dyld.apply_interposing
+0x1f07002c  dyld.gdb_image_notifier
+0x1f070030  dyld.remote_image_notifier
+0x1f080000  dyld.dlopen
+0x1f080004  dyld.dlopen_preflight
+0x1f080008  dyld.dlclose
+0x1f08000c  dyld.dlsym
+0x1f080010  dyld.dladdr
+0x1f090000  dyld.DEBUG.vm_remap
+0x1f090004  dyld.DEBUG.vm_dealloc
+0x1f090008  dyld.DEBUG.map_loop
+0x1f09000c  dyld.DEBUG.marker
diff --git a/doc/tracing/dyld.plist b/doc/tracing/dyld.plist
new file mode 100644 (file)
index 0000000..225d316
--- /dev/null
@@ -0,0 +1,127 @@
+<?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">
+<array>
+       <dict>
+               <key>Name</key>
+               <string>dyld</string>
+               <key>Children</key>
+               <array>
+                       <dict>
+                               <key>Name</key>
+                               <string>dlopen</string>
+                               <key>Type</key>
+                               <string>Interval</string>
+                               <key>EventsMatchedBy</key>
+                               <string>Arg1</string>
+                               <key>KTraceCodeBegin</key>
+                               <string>0x1f070005</string>
+                               <key>KTraceCodeEnd</key>
+                               <string>0x1f070006</string>
+                               <key>ArgValueTypesBegin</key>
+                               <dict>
+                                       <key>Arg2</key>
+                                       <string>String</string>
+                               </dict>
+                               <key>ArgNamesBegin</key>
+                               <dict>
+                                       <key>Arg2</key>
+                                       <string>Path</string>
+                               </dict>
+                       </dict>
+                       <dict>
+                               <key>Name</key>
+                               <string>dlopen_preflight</string>
+                               <key>Type</key>
+                               <string>Interval</string>
+                               <key>EventsMatchedBy</key>
+                               <string>Arg1</string>
+                               <key>KTraceCodeBegin</key>
+                               <string>0x1f070009</string>
+                               <key>KTraceCodeEnd</key>
+                               <string>0x1f07000a</string>
+                               <key>ArgValueTypesBegin</key>
+                               <dict>
+                                       <key>Arg2</key>
+                                       <string>String</string>
+                               </dict>
+                               <key>ArgNamesBegin</key>
+                               <dict>
+                                       <key>Arg2</key>
+                                       <string>Path</string>
+                               </dict>
+                       </dict>
+                       <dict>
+                               <key>Name</key>
+                               <string>map_file</string>
+                               <key>Type</key>
+                               <string>Interval</string>
+                               <key>EventsMatchedBy</key>
+                               <string>Arg1</string>
+                               <key>KTraceCodeBegin</key>
+                               <string>0x1f070005</string>
+                               <key>KTraceCodeEnd</key>
+                               <string>0x1f070006</string>
+                               <key>ArgValueTypesBegin</key>
+                               <dict>
+                                       <key>Arg2</key>
+                                       <string>String</string>
+                               </dict>
+                               <key>ArgNamesBegin</key>
+                               <dict>
+                                       <key>Arg2</key>
+                                       <string>Path</string>
+                               </dict>
+                       </dict>
+                       <dict>
+                               <key>Name</key>
+                               <string>dlsym</string>
+                               <key>Type</key>
+                               <string>Interval</string>
+                               <key>EventsMatchedBy</key>
+                               <string>Arg1</string>
+                               <key>KTraceCodeBegin</key>
+                               <string>0x1f07000d</string>
+                               <key>KTraceCodeEnd</key>
+                               <string>0x1f07000e</string>
+                               <key>ArgValueTypesBegin</key>
+                               <dict>
+                                       <key>Arg2</key>
+                                       <string>String</string>
+                               </dict>
+                               <key>ArgNamesBegin</key>
+                               <dict>
+                                       <key>Arg2</key>
+                                       <string>Path</string>
+                               </dict>
+                       </dict>
+                       <dict>
+                               <key>Name</key>
+                               <string>Static Initializer</string>
+                               <key>Type</key>
+                               <string>Interval</string>
+                               <key>EventsMatchedBy</key>
+                               <string>Arg1</string>
+                               <key>KTraceCodeBegin</key>
+                               <string>0x1f070001</string>
+                               <key>KTraceCodeEnd</key>
+                               <string>0x1f070002</string>
+                               <key>ArgValueTypesBegin</key>
+                               <dict>
+                                       <key>Arg2</key>
+                                       <string>Hex</string>
+                                       <key>Arg3</key>
+                                       <string>Hex</string>
+                               </dict>
+                               <key>ArgNamesBegin</key>
+                               <dict>
+                                       <key>Arg2</key>
+                                       <string>Mach Header</string>
+                                       <key>Arg3</key>
+                                       <string>Initializer Address</string>
+                               </dict>
+                       </dict>
+               </array>
+       </dict>
+</array>
+</plist>
index 121b7fac78bda25bd7cb4fdb956ed11423edd61b..eabdcd336dd6c8922457eabe805b496093bf388f 100644 (file)
@@ -18,6 +18,7 @@
                                F94182D81E60F0BE00D8EF25 /* PBXTargetDependency */,
                                F94182DA1E60F0C000D8EF25 /* PBXTargetDependency */,
                                F94182DC1E60F16900D8EF25 /* PBXTargetDependency */,
+                               C187B90C1FE067590042D3B7 /* PBXTargetDependency */,
                        );
                        name = update_dyld_shared_cache;
                        productName = update_dyld_shared_cache;
@@ -70,7 +71,6 @@
                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 */; };
                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 */; };
                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 */; };
+               37918AC52058915E00F39A77 /* dyld.codes in install ktrace codes file */ = {isa = PBXBuildFile; fileRef = 37918AC42058913800F39A77 /* dyld.codes */; };
                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 */; };
+               37F597D52061ED0B00F9B6F9 /* dyld_usage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37F597D42061ECFF00F9B6F9 /* dyld_usage.cpp */; };
+               37F597D72061ED3200F9B6F9 /* libktrace.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F597D62061ED3200F9B6F9 /* libktrace.tbd */; };
+               C1436B2C203BE67D00028AF1 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
+               C172C9DD20252CB500159311 /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
+               C17984D61FE9E9160057D002 /* mrm_shared_cache_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D2682E1FE08918009F115B /* mrm_shared_cache_builder.cpp */; };
+               C187B90D1FE067C70042D3B7 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
+               C187B90E1FE067CD0042D3B7 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
+               C187B90F1FE067D30042D3B7 /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
+               C187B9101FE067D90042D3B7 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+               C187B9111FE067E10042D3B7 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
+               C187B9121FE067E60042D3B7 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
+               C187B9131FE067F10042D3B7 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; };
+               C187B9141FE067FA0042D3B7 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
+               C187B9151FE068000042D3B7 /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; };
+               C187B9161FE0680A0042D3B7 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
+               C187B9171FE068180042D3B7 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+               C187B9181FE068260042D3B7 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+               C187B9191FE0682C0042D3B7 /* BuilderUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */; };
+               C187B91B1FE0683F0042D3B7 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
+               C187B91E1FE0684C0042D3B7 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
+               C1960ECF2090D9E5007E3E6B /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+               C1960ED02090D9F0007E3E6B /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+               C1960ED12090D9F6007E3E6B /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
+               C1960ED22090D9FA007E3E6B /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
+               C1960ED32090D9FF007E3E6B /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+               C1960ED42090DA09007E3E6B /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
+               C1D268311FE0891C009F115B /* mrm_shared_cache_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D2682E1FE08918009F115B /* mrm_shared_cache_builder.cpp */; };
+               C1D268351FE0A77B009F115B /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
+               C1D268371FE0BC5F009F115B /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
+               C1D268391FE0BC94009F115B /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
+               C1D2683A1FE0BCF3009F115B /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
+               C1D2683F1FE98D4F009F115B /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
+               C1D268401FE9B464009F115B /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.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 */; };
                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 */; };
+               F92756811F68AF4D000820EE /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
+               F92756821F68AF4D000820EE /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
+               F92756831F68AF4D000820EE /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
+               F92756841F68AF4D000820EE /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+               F92756851F68AF4D000820EE /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
+               F92756861F68AF4D000820EE /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
                F9280B7B1AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */; };
+               F936BF9720323F0F00568B23 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
+               F93D733D1F82F03F007D9413 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
+               F93D733E1F82F03F007D9413 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
+               F93D733F1F82F03F007D9413 /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
+               F93D73401F8404A2007D9413 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+               F93D73411F8404FA007D9413 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
+               F93D73421F8421CC007D9413 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
+               F93D73431F842CBF007D9413 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
+               F93D73441F8475C3007D9413 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+               F93D73451F8475C3007D9413 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
+               F93D73461F8475C3007D9413 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
+               F93D73471F8C4E55007D9413 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
+               F93D73481F8FF780007D9413 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
+               F93D73491F8FF780007D9413 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
+               F93D734A1F8FF780007D9413 /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
+               F93D734B1F8FF79E007D9413 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+               F93D734C1F8FF79E007D9413 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
+               F93D734D1F8FF79E007D9413 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
+               F93D734E1F8FF7C2007D9413 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
+               F93D734F1F8FF7C2007D9413 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
+               F93D73501F8FF7C2007D9413 /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
+               F93D73511F8FF7C2007D9413 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+               F93D73521F8FF7C2007D9413 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
+               F93D73531F8FF7C2007D9413 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
+               F93F46521FA420850060D9F9 /* execserver.defs in Sources */ = {isa = PBXBuildFile; fileRef = F93F46511FA420630060D9F9 /* execserver.defs */; settings = {ATTRIBUTES = (Server, ); }; };
                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"; }; };
                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 */; };
+               F9653F8E1FAE51C9008B5D93 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
+               F9653F8F1FAE51C9008B5D93 /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
+               F9653F901FAE51C9008B5D93 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
+               F9653F911FAE51C9008B5D93 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+               F9653F921FAE51C9008B5D93 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
+               F9653F941FAE51ED008B5D93 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.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, ); }; };
                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 */; };
+               F9A5E6171F5C967C0030C490 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.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 */; };
+               F9C2755A1DA71CE8007A5D8A /* Loading.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C275581DA71A13007A5D8A /* Loading.cpp */; settings = {COMPILER_FLAGS = "-fvisibility=hidden"; }; };
                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 */; };
+               F9CC10D71F5F1D480021BFE2 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
+               F9CC10D81F5F1D4E0021BFE2 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
                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 */; };
                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 */; };
+               F9DFEA6C1F50DD16003BF8A7 /* Closure.h in Headers */ = {isa = PBXBuildFile; fileRef = F9DFEA6B1F50DD16003BF8A7 /* Closure.h */; };
+               F9DFEA701F50FDE5003BF8A7 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
+               F9DFEA721F54BD83003BF8A7 /* ClosureWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = F9DFEA711F54BD83003BF8A7 /* ClosureWriter.h */; };
+               F9DFEA741F54DB25003BF8A7 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
+               F9DFEA761F54FAAB003BF8A7 /* ClosureBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = F9DFEA751F54FAAB003BF8A7 /* ClosureBuilder.h */; };
+               F9DFEA781F54FACF003BF8A7 /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
+               F9DFEA791F55DDC0003BF8A7 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
+               F9DFEA7A1F55DDC4003BF8A7 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
+               F9DFEA7B1F55DDC7003BF8A7 /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
+               F9DFEA7D1F588506003BF8A7 /* ClosurePrinter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA7C1F588506003BF8A7 /* ClosurePrinter.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 */; };
                F9ED4CDB0630A7F100DF4E74 /* dyldInitialization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CCB0630A7F100DF4E74 /* dyldInitialization.cpp */; };
                F9ED4CDE0630A7F100DF4E74 /* dyldNew.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CCE0630A7F100DF4E74 /* dyldNew.cpp */; };
                F9ED4CDF0630A7F100DF4E74 /* dyldStartup.s in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CCF0630A7F100DF4E74 /* dyldStartup.s */; };
-               F9ED4CE00630A7F100DF4E74 /* glue.c in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD00630A7F100DF4E74 /* glue.c */; };
+               F9ED4CE00630A7F100DF4E74 /* glue.c in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD00630A7F100DF4E74 /* glue.c */; settings = {COMPILER_FLAGS = "-fno-builtin"; }; };
                F9ED4CE10630A7F100DF4E74 /* ImageLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD10630A7F100DF4E74 /* ImageLoader.cpp */; };
                F9ED4CE30630A7F100DF4E74 /* ImageLoaderMachO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD30630A7F100DF4E74 /* ImageLoaderMachO.cpp */; };
                F9ED4CE50630A7F100DF4E74 /* stub_binding_helper.s in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */; };
                        remoteGlobalIDString = 37A0AD0A1C15FFF500731E50;
                        remoteInfo = update_dyld_shared_cache;
                };
+               C187B90B1FE067590042D3B7 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = C187B8FF1FE063A40042D3B7;
+                       remoteInfo = libslc_builder;
+               };
                D8668ACF1ECE335F005E7D31 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        remoteGlobalIDString = F9ED4C9E0630A76B00DF4E74;
                        remoteInfo = libdyld.dylib;
                };
-               F922C8111F33B62700D8F479 /* PBXContainerItemProxy */ = {
-                       isa = PBXContainerItemProxy;
-                       containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
-                       proxyType = 1;
-                       remoteGlobalIDString = F9AB02B71F329FAA00EE96C4;
-                       remoteInfo = libclosured;
-               };
-               F922C81D1F33B96300D8F479 /* PBXContainerItemProxy */ = {
-                       isa = PBXContainerItemProxy;
-                       containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
-                       proxyType = 1;
-                       remoteGlobalIDString = F922C8161F33B73800D8F479;
-                       remoteInfo = "libclosured-stub";
-               };
                F94182D71E60F0BE00D8EF25 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                };
+               37918AC32058912100F39A77 /* install ktrace codes file */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 8;
+                       dstPath = /usr/local/share/misc;
+                       dstSubfolderSpec = 0;
+                       files = (
+                               37918AC52058915E00F39A77 /* dyld.codes in install ktrace codes file */,
+                       );
+                       name = "install ktrace codes file";
+                       runOnlyForDeploymentPostprocessing = 1;
+               };
+               37F597CB2061EB4200F9B6F9 /* CopyFiles */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 2147483647;
+                       dstPath = /usr/share/man/man1/;
+                       dstSubfolderSpec = 0;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 1;
+               };
+               C187B9041FE063A40042D3B7 /* usr|local|include|mach-o */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 8;
+                       dstPath = "$(INSTALL_PATH_PREFIX)$(INSTALL_LOCATION)/usr/local/include";
+                       dstSubfolderSpec = 0;
+                       files = (
+                       );
+                       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;
                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>"; };
+               37918AC0205890D700F39A77 /* dyld.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = dyld.plist; sourceTree = "<group>"; };
+               37918AC42058913800F39A77 /* dyld.codes */ = {isa = PBXFileReference; lastKnownFileType = text; path = dyld.codes; 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>"; };
+               37F597CD2061EB4200F9B6F9 /* dyld_usage */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_usage; sourceTree = BUILT_PRODUCTS_DIR; };
+               37F597D42061ECFF00F9B6F9 /* dyld_usage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_usage.cpp; path = src/dyld_usage.cpp; sourceTree = "<group>"; };
+               37F597D62061ED3200F9B6F9 /* libktrace.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libktrace.tbd; path = usr/lib/libktrace.tbd; sourceTree = SDKROOT; };
                37F7A5961BB363820039043A /* Bom.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Bom.framework; path = System/Library/PrivateFrameworks/Bom.framework; sourceTree = SDKROOT; };
+               C187B90A1FE063A40042D3B7 /* slc_builder.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = slc_builder.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
+               C18A75F5209940A500DC01BB /* JSONWriter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = JSONWriter.h; path = dyld3/JSONWriter.h; sourceTree = "<group>"; };
+               C19D50142087E4BC00563DAF /* SupportedArchs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SupportedArchs.h; path = dyld3/SupportedArchs.h; sourceTree = "<group>"; };
+               C1D2682E1FE08918009F115B /* mrm_shared_cache_builder.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = mrm_shared_cache_builder.cpp; path = "dyld3/shared-cache/mrm_shared_cache_builder.cpp"; sourceTree = "<group>"; };
+               C1D2682F1FE08918009F115B /* mrm_shared_cache_builder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = mrm_shared_cache_builder.h; path = "dyld3/shared-cache/mrm_shared_cache_builder.h"; sourceTree = "<group>"; };
+               C1D268321FE09843009F115B /* ClosureFileSystem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ClosureFileSystem.h; path = dyld3/ClosureFileSystem.h; sourceTree = "<group>"; };
+               C1D268331FE0A21F009F115B /* ClosureFileSystemPhysical.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ClosureFileSystemPhysical.h; path = dyld3/ClosureFileSystemPhysical.h; sourceTree = "<group>"; };
+               C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ClosureFileSystemPhysical.cpp; path = dyld3/ClosureFileSystemPhysical.cpp; sourceTree = "<group>"; };
                EF799FE9070D27BB00F78484 /* dyld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = dyld.1; path = doc/man/man1/dyld.1; sourceTree = SOURCE_ROOT; };
                EF799FEB070D27BB00F78484 /* dladdr.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dladdr.3; path = doc/man/man3/dladdr.3; sourceTree = SOURCE_ROOT; };
                EF799FEC070D27BB00F78484 /* dlclose.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlclose.3; path = doc/man/man3/dlclose.3; sourceTree = SOURCE_ROOT; };
                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; };
                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>"; };
+               F92756871F7098FB000820EE /* Array.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Array.h; path = dyld3/Array.h; sourceTree = "<group>"; };
+               F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMegaDylib.cpp; path = src/ImageLoaderMegaDylib.cpp; sourceTree = "<group>"; usesTabs = 1; };
+               F9280B7A1AB9DCA000B18AEC /* ImageLoaderMegaDylib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMegaDylib.h; path = src/ImageLoaderMegaDylib.h; sourceTree = "<group>"; usesTabs = 1; };
                F93937320A94FAF700070A07 /* update_dyld_shared_cache */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = update_dyld_shared_cache; sourceTree = BUILT_PRODUCTS_DIR; };
                F939373E0A94FC4700070A07 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = Architectures.hpp; sourceTree = "<group>"; };
                F939373F0A94FC4700070A07 /* CacheFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = CacheFileAbstraction.hpp; sourceTree = "<group>"; };
                F93937400A94FC4700070A07 /* dyld_cache_format.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = dyld_cache_format.h; sourceTree = "<group>"; };
                F93937410A94FC4700070A07 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = FileAbstraction.hpp; sourceTree = "<group>"; };
                F93937430A94FC4700070A07 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOFileAbstraction.hpp; sourceTree = "<group>"; };
+               F93F46511FA420630060D9F9 /* execserver.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = execserver.defs; path = testing/nocr/execserver.defs; 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>"; };
-               F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOCompressed.h; path = src/ImageLoaderMachOCompressed.h; sourceTree = "<group>"; };
+               F94DB9000F0A9B1700323715 /* ImageLoaderMachOClassic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOClassic.cpp; path = src/ImageLoaderMachOClassic.cpp; sourceTree = "<group>"; usesTabs = 1; };
+               F94DB9010F0A9B1700323715 /* ImageLoaderMachOClassic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOClassic.h; path = src/ImageLoaderMachOClassic.h; sourceTree = "<group>"; usesTabs = 1; };
+               F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOCompressed.cpp; path = src/ImageLoaderMachOCompressed.cpp; sourceTree = "<group>"; usesTabs = 1; };
+               F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOCompressed.h; path = src/ImageLoaderMachOCompressed.h; sourceTree = "<group>"; usesTabs = 1; };
                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>"; };
                F95C95160E994796007B7CB8 /* MachOTrie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachOTrie.hpp; sourceTree = "<group>"; };
                F963542E1DCD736000895049 /* update_dyld_sim_shared_cache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = update_dyld_sim_shared_cache.cpp; path = "dyld3/shared-cache/update_dyld_sim_shared_cache.cpp"; sourceTree = "<group>"; usesTabs = 0; };
                F96354451DCD74A400895049 /* update_dyld_sim_shared_cache */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = update_dyld_sim_shared_cache; sourceTree = BUILT_PRODUCTS_DIR; };
-               F963546A1DD8D8D300895049 /* ImageProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageProxy.h; path = "dyld3/shared-cache/ImageProxy.h"; sourceTree = "<group>"; usesTabs = 0; };
-               F963546B1DD8F2A800895049 /* ImageProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageProxy.cpp; path = "dyld3/shared-cache/ImageProxy.cpp"; sourceTree = "<group>"; usesTabs = 0; };
-               F96D19711D7F63EE007AF3CE /* expand.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; name = expand.pl; path = bin/expand.pl; sourceTree = "<group>"; };
+               F96D19711D7F63EE007AF3CE /* expand.rb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.ruby; name = expand.rb; path = bin/expand.rb; 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>"; };
                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>"; };
                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>"; };
                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; };
                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>"; };
-               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>"; };
                F99EE6AE06B48D4200BF1992 /* dlfcn.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dlfcn.h; path = include/dlfcn.h; sourceTree = "<group>"; };
                F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = dyld_stub_binder.s; path = src/dyld_stub_binder.s; sourceTree = "<group>"; };
                F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dyldLibSystemGlue.c; path = src/dyldLibSystemGlue.c; sourceTree = "<group>"; };
+               F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = MachOLoaded.cpp; path = dyld3/MachOLoaded.cpp; sourceTree = "<group>"; };
+               F9A5E6161F5C967C0030C490 /* MachOLoaded.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MachOLoaded.h; path = dyld3/MachOLoaded.h; sourceTree = "<group>"; };
+               F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = MachOAnalyzer.cpp; path = dyld3/MachOAnalyzer.cpp; sourceTree = "<group>"; };
+               F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = MachOFile.cpp; path = dyld3/MachOFile.cpp; sourceTree = "<group>"; };
+               F9A5E61A1F5F1BFA0030C490 /* MachOFile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MachOFile.h; path = dyld3/MachOFile.h; sourceTree = "<group>"; };
+               F9A5E61B1F5F1BFB0030C490 /* MachOAnalyzer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MachOAnalyzer.h; path = dyld3/MachOAnalyzer.h; 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; };
                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; };
+               F9DFEA6B1F50DD16003BF8A7 /* Closure.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Closure.h; path = dyld3/Closure.h; sourceTree = "<group>"; };
+               F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Closure.cpp; path = dyld3/Closure.cpp; sourceTree = "<group>"; };
+               F9DFEA711F54BD83003BF8A7 /* ClosureWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClosureWriter.h; path = dyld3/ClosureWriter.h; sourceTree = "<group>"; };
+               F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClosureWriter.cpp; path = dyld3/ClosureWriter.cpp; sourceTree = "<group>"; };
+               F9DFEA751F54FAAB003BF8A7 /* ClosureBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ClosureBuilder.h; path = dyld3/ClosureBuilder.h; sourceTree = "<group>"; };
+               F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ClosureBuilder.cpp; path = dyld3/ClosureBuilder.cpp; sourceTree = "<group>"; };
+               F9DFEA7C1F588506003BF8A7 /* ClosurePrinter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClosurePrinter.cpp; path = dyld3/ClosurePrinter.cpp; sourceTree = "<group>"; };
+               F9DFEA7E1F588558003BF8A7 /* ClosurePrinter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ClosurePrinter.h; path = dyld3/ClosurePrinter.h; sourceTree = "<group>"; };
                F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = dlopen_preflight.3; 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; };
+               F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_gdb.cpp; path = src/dyld_gdb.cpp; sourceTree = SOURCE_ROOT; usesTabs = 0; };
                F9ED4CC70630A7F100DF4E74 /* dyld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld.cpp; path = src/dyld.cpp; sourceTree = SOURCE_ROOT; usesTabs = 1; };
                F9ED4CC80630A7F100DF4E74 /* dyld.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld.h; path = src/dyld.h; sourceTree = SOURCE_ROOT; };
-               F9ED4CC90630A7F100DF4E74 /* dyldAPIs.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldAPIs.cpp; path = src/dyldAPIs.cpp; sourceTree = SOURCE_ROOT; };
+               F9ED4CC90630A7F100DF4E74 /* dyldAPIs.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldAPIs.cpp; path = src/dyldAPIs.cpp; sourceTree = SOURCE_ROOT; usesTabs = 1; };
                F9ED4CCA0630A7F100DF4E74 /* dyldExceptions.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = dyldExceptions.c; path = src/dyldExceptions.c; sourceTree = SOURCE_ROOT; };
                F9ED4CCB0630A7F100DF4E74 /* dyldInitialization.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldInitialization.cpp; path = src/dyldInitialization.cpp; sourceTree = SOURCE_ROOT; };
                F9ED4CCC0630A7F100DF4E74 /* dyldLock.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldLock.cpp; path = src/dyldLock.cpp; sourceTree = SOURCE_ROOT; };
                F9ED4CCE0630A7F100DF4E74 /* dyldNew.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldNew.cpp; path = src/dyldNew.cpp; sourceTree = SOURCE_ROOT; };
                F9ED4CCF0630A7F100DF4E74 /* dyldStartup.s */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.asm; name = dyldStartup.s; path = src/dyldStartup.s; sourceTree = SOURCE_ROOT; tabWidth = 8; usesTabs = 1; };
                F9ED4CD00630A7F100DF4E74 /* glue.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = glue.c; path = src/glue.c; sourceTree = SOURCE_ROOT; };
-               F9ED4CD10630A7F100DF4E74 /* ImageLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoader.cpp; path = src/ImageLoader.cpp; sourceTree = SOURCE_ROOT; };
-               F9ED4CD20630A7F100DF4E74 /* ImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ImageLoader.h; path = src/ImageLoader.h; sourceTree = SOURCE_ROOT; };
-               F9ED4CD30630A7F100DF4E74 /* ImageLoaderMachO.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachO.cpp; path = src/ImageLoaderMachO.cpp; sourceTree = SOURCE_ROOT; };
-               F9ED4CD40630A7F100DF4E74 /* ImageLoaderMachO.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachO.h; path = src/ImageLoaderMachO.h; sourceTree = SOURCE_ROOT; };
+               F9ED4CD10630A7F100DF4E74 /* ImageLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoader.cpp; path = src/ImageLoader.cpp; sourceTree = SOURCE_ROOT; usesTabs = 1; };
+               F9ED4CD20630A7F100DF4E74 /* ImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ImageLoader.h; path = src/ImageLoader.h; sourceTree = SOURCE_ROOT; usesTabs = 1; };
+               F9ED4CD30630A7F100DF4E74 /* ImageLoaderMachO.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachO.cpp; path = src/ImageLoaderMachO.cpp; sourceTree = SOURCE_ROOT; usesTabs = 1; };
+               F9ED4CD40630A7F100DF4E74 /* ImageLoaderMachO.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachO.h; path = src/ImageLoaderMachO.h; sourceTree = SOURCE_ROOT; usesTabs = 1; };
                F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.asm; name = stub_binding_helper.s; path = src/stub_binding_helper.s; sourceTree = SOURCE_ROOT; tabWidth = 8; usesTabs = 1; };
                F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_gdb.h; path = "include/mach-o/dyld_gdb.h"; sourceTree = SOURCE_ROOT; };
                F9ED4CE90630A80600DF4E74 /* dyld_priv.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_priv.h; path = "include/mach-o/dyld_priv.h"; sourceTree = SOURCE_ROOT; };
                F9ED4CEA0630A80600DF4E74 /* dyld.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld.h; path = "include/mach-o/dyld.h"; sourceTree = SOURCE_ROOT; };
                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>"; };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
-               F922C8141F33B73800D8F479 /* Frameworks */ = {
+               37F597CA2061EB4200F9B6F9 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               37F597D72061ED3200F9B6F9 /* libktrace.tbd in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               C187B9031FE063A40042D3B7 /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
                        );
                        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;
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
+               37918ABF2058908000F39A77 /* tracing */ = {
+                       isa = PBXGroup;
+                       children = (
+                               37918AC0205890D700F39A77 /* dyld.plist */,
+                               37918AC42058913800F39A77 /* dyld.codes */,
+                       );
+                       name = tracing;
+                       path = doc/tracing;
+                       sourceTree = SOURCE_ROOT;
+               };
                EF799FE7070D27BB00F78484 /* man */ = {
                        isa = PBXGroup;
                        children = (
                F94C22231E513CA90079E5DD /* Frameworks */ = {
                        isa = PBXGroup;
                        children = (
-                               F90F7C071E9C6B8B00535722 /* libCrashReporterClient.a */,
+                               37F597D62061ED3200F9B6F9 /* libktrace.tbd */,
                                37F7A5961BB363820039043A /* Bom.framework */,
                                376ED1D71C46F2710051DD54 /* Metabom.framework */,
                                F94C22241E513CA90079E5DD /* CoreFoundation.framework */,
                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 */,
+                               F96D19A71D9363D6007AF3CE /* AllImages.h */,
+                               F96D19A61D9363D6007AF3CE /* AllImages.cpp */,
+                               F96D19C11D95C6D6007AF3CE /* APIs.h */,
+                               F96D19A51D9363D6007AF3CE /* APIs.cpp */,
+                               F9C15A491E1F7D960006E570 /* APIs_macOS.cpp */,
+                               F92756871F7098FB000820EE /* Array.h */,
+                               F98692221DC4028B00CBEDE6 /* CodeSigningTypes.h */,
+                               F9DFEA6B1F50DD16003BF8A7 /* Closure.h */,
+                               F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */,
+                               F9DFEA751F54FAAB003BF8A7 /* ClosureBuilder.h */,
+                               F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */,
+                               C1D268321FE09843009F115B /* ClosureFileSystem.h */,
+                               C1D268331FE0A21F009F115B /* ClosureFileSystemPhysical.h */,
+                               C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */,
+                               F9DFEA7E1F588558003BF8A7 /* ClosurePrinter.h */,
+                               F9DFEA7C1F588506003BF8A7 /* ClosurePrinter.cpp */,
+                               F9DFEA711F54BD83003BF8A7 /* ClosureWriter.h */,
+                               F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */,
+                               F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */,
+                               F98692001DC3EF4800CBEDE6 /* Diagnostics.h */,
+                               C18A75F5209940A500DC01BB /* JSONWriter.h */,
+                               F97C619D1D96C5BE00A84CD7 /* libdyldEntryVector.h */,
+                               F97C619E1D98292700A84CD7 /* libdyldEntryVector.cpp */,
                                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 */,
+                               F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */,
+                               F9A5E61A1F5F1BFA0030C490 /* MachOFile.h */,
+                               F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */,
+                               F9A5E6161F5C967C0030C490 /* MachOLoaded.h */,
+                               F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */,
+                               F9A5E61B1F5F1BFB0030C490 /* MachOAnalyzer.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 */,
+                               F977DDC91E53BEA700609230 /* SharedCacheRuntime.cpp */,
+                               F977DDCA1E53BEA700609230 /* SharedCacheRuntime.h */,
+                               37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */,
+                               37D7DAFF1E96F0ED00D52CEA /* Tracing.h */,
+                               C19D50142087E4BC00563DAF /* SupportedArchs.h */,
                        );
                        name = dyld3;
                        sourceTree = "<group>";
                                F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */,
                                F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */,
                                F94182DE1E60FFDC00D8EF25 /* update_dyld_sim_shared_cache.xcconfig */,
-                               F9EDC0A01F0481B400B030F4 /* closured.xcconfig */,
                        );
                        path = configs;
                        sourceTree = SOURCE_ROOT;
                };
-               F97FF3571C23638F000ACDD2 /* nocr */ = {
-                       isa = PBXGroup;
-                       children = (
-                               F97FF3581C23638F000ACDD2 /* main.c */,
-                       );
-                       path = nocr;
-                       sourceTree = "<group>";
-               };
                F98692161DC3EF7700CBEDE6 /* shared-cache */ = {
                        isa = PBXGroup;
                        children = (
                                F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */,
                                F986920E1DC3EF6C00CBEDE6 /* FileUtils.h */,
                                F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */,
-                               F963546A1DD8D8D300895049 /* ImageProxy.h */,
-                               F963546B1DD8F2A800895049 /* ImageProxy.cpp */,
                                37908A2C1E3A85A4009613FA /* Manifest.h */,
                                37908A281E3A853E009613FA /* Manifest.mm */,
                                F986920F1DC3EF6C00CBEDE6 /* ObjC1Abstraction.hpp */,
                                F963542E1DCD736000895049 /* update_dyld_sim_shared_cache.cpp */,
                                F98692151DC3EF6C00CBEDE6 /* update_dyld_shared_cache.cpp */,
                                F9EDC09E1F04767300B030F4 /* update_dyld_shared_cache_entitlements.plist */,
+                               C1D2682E1FE08918009F115B /* mrm_shared_cache_builder.cpp */,
+                               C1D2682F1FE08918009F115B /* mrm_shared_cache_builder.h */,
                        );
                        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 */,
                                F9ED4CC30630A7BE00DF4E74 /* doc */,
                                F9ED4CBE0630A7B100DF4E74 /* include */,
-                               F97FF3571C23638F000ACDD2 /* nocr */,
                                F9ED4C990630A76000DF4E74 /* Products */,
                                F96D19A41D9363B7007AF3CE /* dyld3 */,
                                F939373D0A94FC4700070A07 /* launch-cache */,
                                F97FF3561C23638F000ACDD2 /* nocr */,
                                F97C61A71DBAD1A900A84CD7 /* dyld_closure_util */,
                                F96354451DCD74A400895049 /* update_dyld_sim_shared_cache */,
-                               F9DDEDB21E2878CA00A753DC /* closured */,
-                               F9AB02B81F329FAA00EE96C4 /* libclosured.dylib */,
-                               F922C8171F33B73800D8F479 /* libclosured.dylib */,
+                               C187B90A1FE063A40042D3B7 /* slc_builder.dylib */,
+                               37F597CD2061EB4200F9B6F9 /* dyld_usage */,
                        );
                        name = Products;
                        sourceTree = "<group>";
                F9ED4CBB0630A7AA00DF4E74 /* src */ = {
                        isa = PBXGroup;
                        children = (
+                               F93F46511FA420630060D9F9 /* execserver.defs */,
                                F97FF35F1C236402000ACDD2 /* nocr.c */,
+                               37F597D42061ECFF00F9B6F9 /* dyld_usage.cpp */,
                                F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */,
                                F9ED4CC70630A7F100DF4E74 /* dyld.cpp */,
                                F9ED4CC80630A7F100DF4E74 /* dyld.h */,
                F9ED4CBE0630A7B100DF4E74 /* include */ = {
                        isa = PBXGroup;
                        children = (
-                               F96D19711D7F63EE007AF3CE /* expand.pl */,
+                               F96D19711D7F63EE007AF3CE /* expand.rb */,
                                F95090D01C5AB89A0031F81D /* dyld_process_info.h */,
                                F98D274C0AA79D7400416316 /* dyld_images.h */,
                                F918691408B16D2500E0F9DB /* dyld-interposing.h */,
                F9ED4CC30630A7BE00DF4E74 /* doc */ = {
                        isa = PBXGroup;
                        children = (
+                               37918ABF2058908000F39A77 /* tracing */,
                                EF799FE7070D27BB00F78484 /* man */,
                        );
                        name = doc;
 /* End PBXGroup section */
 
 /* Begin PBXHeadersBuildPhase section */
-               F922C8151F33B73800D8F479 /* Headers */ = {
-                       isa = PBXHeadersBuildPhase;
-                       buildActionMask = 2147483647;
-                       files = (
-                       );
-                       runOnlyForDeploymentPostprocessing = 0;
-               };
                F98F1FBB1E4029CA00EF868D /* Headers */ = {
                        isa = PBXHeadersBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               F9DFEA6C1F50DD16003BF8A7 /* Closure.h in Headers */,
                                F99006DD1E411BA70013456D /* dyld_images.h in Headers */,
                                F99006DE1E411BBC0013456D /* dyld.h in Headers */,
                                F98F1FBD1E4029E400EF868D /* dyld_priv.h in Headers */,
+                               F9DFEA761F54FAAB003BF8A7 /* ClosureBuilder.h in Headers */,
                                F960A78A1E40569400840176 /* dyld-interposing.h in Headers */,
+                               F9DFEA721F54BD83003BF8A7 /* ClosureWriter.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 */
                        productReference = 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */;
                        productType = "com.apple.product-type.tool";
                };
-               F922C8161F33B73800D8F479 /* libclosured-stub */ = {
+               37F597CC2061EB4200F9B6F9 /* dyld_usage */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 37F597D32061EB4200F9B6F9 /* Build configuration list for PBXNativeTarget "dyld_usage" */;
+                       buildPhases = (
+                               37F597C92061EB4200F9B6F9 /* Sources */,
+                               37F597CA2061EB4200F9B6F9 /* Frameworks */,
+                               37F597CB2061EB4200F9B6F9 /* CopyFiles */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = dyld_usage;
+                       productName = dyld_usage;
+                       productReference = 37F597CD2061EB4200F9B6F9 /* dyld_usage */;
+                       productType = "com.apple.product-type.tool";
+               };
+               C187B8FF1FE063A40042D3B7 /* libslc_builder.dylib */ = {
                        isa = PBXNativeTarget;
-                       buildConfigurationList = F922C8181F33B73800D8F479 /* Build configuration list for PBXNativeTarget "libclosured-stub" */;
+                       buildConfigurationList = C187B9071FE063A40042D3B7 /* Build configuration list for PBXNativeTarget "libslc_builder.dylib" */;
                        buildPhases = (
-                               F922C8131F33B73800D8F479 /* Sources */,
-                               F922C8141F33B73800D8F479 /* Frameworks */,
-                               F922C8151F33B73800D8F479 /* Headers */,
+                               C187B9001FE063A40042D3B7 /* Sources */,
+                               C187B9031FE063A40042D3B7 /* Frameworks */,
+                               C187B9041FE063A40042D3B7 /* usr|local|include|mach-o */,
                        );
                        buildRules = (
                        );
                        dependencies = (
                        );
-                       name = "libclosured-stub";
-                       productName = "libclosured-stub";
-                       productReference = F922C8171F33B73800D8F479 /* libclosured.dylib */;
+                       name = libslc_builder.dylib;
+                       productName = dsc;
+                       productReference = C187B90A1FE063A40042D3B7 /* slc_builder.dylib */;
                        productType = "com.apple.product-type.library.dynamic";
                };
                F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */ = {
                        productReference = F99B8E670FEC121100701838 /* dyld_shared_cache_util */;
                        productType = "com.apple.product-type.tool";
                };
-               F9AB02B71F329FAA00EE96C4 /* libclosured */ = {
-                       isa = PBXNativeTarget;
-                       buildConfigurationList = F9AB02C21F329FAA00EE96C4 /* Build configuration list for PBXNativeTarget "libclosured" */;
-                       buildPhases = (
-                               F9AB02B41F329FAA00EE96C4 /* Sources */,
-                               F9AB02B51F329FAA00EE96C4 /* Frameworks */,
-                               F9AB02B61F329FAA00EE96C4 /* Headers */,
-                       );
-                       buildRules = (
-                       );
-                       dependencies = (
-                       );
-                       name = libclosured;
-                       productName = libclosured;
-                       productReference = F9AB02B81F329FAA00EE96C4 /* libclosured.dylib */;
-                       productType = "com.apple.product-type.library.dynamic";
-               };
                F9D1001114D8D0BA00099D91 /* dsc_extractor */ = {
                        isa = PBXNativeTarget;
                        buildConfigurationList = F9D1001714D8D0F100099D91 /* Build configuration list for PBXNativeTarget "dsc_extractor" */;
                        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" */;
                                F907E2490FA6469000BFEDBD /* install iPhone file */,
                                F9213B3F18BFC9CB001CB6E8 /* simulator entitlement */,
                                F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */,
+                               371C117D208ADFC700FD9036 /* Suppress simulator dyld_usage */,
                        );
                        buildRules = (
                                F921D318070376B0000D1056 /* PBXBuildRule */,
                                F921D3160703769A000D1056 /* PBXBuildRule */,
                        );
                        dependencies = (
-                               F922C8121F33B62700D8F479 /* PBXTargetDependency */,
                                F99B8EB20FEC220C00701838 /* PBXTargetDependency */,
                                F96543A11E343601003C5540 /* PBXTargetDependency */,
                        );
                                F98F1FBB1E4029CA00EF868D /* Headers */,
                                F960A78C1E405E2300840176 /* expand dyld_priv.h macros */,
                                F99006DF1E411C500013456D /* install dlfcn.h */,
+                               37918AC32058912100F39A77 /* install ktrace codes file */,
                        );
                        buildRules = (
                                F921D31E070376F1000D1056 /* PBXBuildRule */,
                                F9574C4906C94DA700142BFA /* PBXBuildRule */,
                        );
                        dependencies = (
-                               F922C81E1F33B96300D8F479 /* PBXTargetDependency */,
                        );
                        name = libdyld.dylib;
                        productName = libdyld;
                F9ED4C8B0630A72300DF4E74 /* Project object */ = {
                        isa = PBXProject;
                        attributes = {
-                               LastUpgradeCheck = 0900;
+                               LastUpgradeCheck = 1000;
                                TargetAttributes = {
                                        37A0AD0A1C15FFF500731E50 = {
                                                CreatedOnToolsVersion = 8.0;
                                        };
-                                       F922C8161F33B73800D8F479 = {
-                                               CreatedOnToolsVersion = 9.0;
+                                       37F597CC2061EB4200F9B6F9 = {
+                                               CreatedOnToolsVersion = 10.0;
+                                               ProvisioningStyle = Automatic;
                                        };
                                        F97C61A61DBAD1A900A84CD7 = {
                                                CreatedOnToolsVersion = 8.0;
                                        F97FF3551C23638F000ACDD2 = {
                                                CreatedOnToolsVersion = 8.0;
                                        };
-                                       F9AB02B71F329FAA00EE96C4 = {
-                                               CreatedOnToolsVersion = 9.0;
-                                       };
-                                       F9DDEDB11E2878CA00A753DC = {
-                                               CreatedOnToolsVersion = 8.2;
-                                               DevelopmentTeam = 59GAB85EFG;
-                                               ProvisioningStyle = Automatic;
-                                       };
                                        F9F6F4271C1FB0A700BD8FED = {
                                                CreatedOnToolsVersion = 8.0;
                                        };
                                37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */,
                                F908134211D3ED0B00626CC1 /* libdyld */,
                                F9ED4C970630A76000DF4E74 /* dyld */,
-                               F9DDEDB11E2878CA00A753DC /* closured */,
                                F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */,
                                F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */,
                                377685F21AC4B27D00026E6C /* multi_dyld_shared_cache_builder */,
                                F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */,
                                F9F2A5580F7AEE9800B7C9EB /* libdsc */,
                                F9D1001114D8D0BA00099D91 /* dsc_extractor */,
+                               C187B8FF1FE063A40042D3B7 /* libslc_builder.dylib */,
                                F9F6F4271C1FB0A700BD8FED /* dyld_tests */,
                                F97FF3551C23638F000ACDD2 /* nocr */,
-                               F9AB02B71F329FAA00EE96C4 /* libclosured */,
-                               F922C8161F33B73800D8F479 /* libclosured-stub */,
+                               37F597CC2061EB4200F9B6F9 /* dyld_usage */,
                        );
                };
 /* End PBXProject section */
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        shellPath = /bin/bash;
-                       shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n    # if iOS SDK not available, use MacOSX SDK\n    ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n    echo -n \"#define ARM_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n    echo -n \"#define ARM_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n    echo -n \"#define ARM64_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_BASE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n    echo -n \"#define ARM64_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_SIZE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nelse\n    echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n    exit 1\nfi\n\nif [ -r \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" ]; then\n    mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n    cp \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\"  \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\nif [ -r \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" ]; then\n    mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n    cp \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\"  \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\n\n";
+                       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[ \\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_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_SIZE_ARM64[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n    grep SHARED_REGION_BASE_ARM64_32 \"${SHARED_REGION_FILE}\" > /dev/null 2>&1\n    if [ \"$?\" -eq \"0\" ]; then\n        echo -n \"#define ARM64_32_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        awk '/define SHARED_REGION_BASE_ARM64_32/ { 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_32_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        awk '/define SHARED_REGION_SIZE_ARM64_32/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    fi\nelse\n    echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n    exit 1\nfi\n\nif [ -r \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" ]; then\n    mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n    cp \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\"  \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\nif [ -r \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" ]; then\n    mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n    cp \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\"  \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\n\n";
+                       showEnvVarsInLog = 0;
+               };
+               371C117D208ADFC700FD9036 /* Suppress simulator dyld_usage */ = {
+                       isa = PBXShellScriptBuildPhase;
+                       buildActionMask = 8;
+                       files = (
+                       );
+                       inputPaths = (
+                       );
+                       name = "Suppress simulator dyld_usage";
+                       outputPaths = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 1;
+                       shellPath = /bin/sh;
+                       shellScript = "# dyld_usage requires libktrace which is not available in the simualtor\n# The target builds a dummy app that we delete\nif [ \"${PRODUCT_NAME}\" != \"dyld_sim\" ]\nthen\nxcodebuild install -target dyld_usage SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\nfi";
                        showEnvVarsInLog = 0;
                };
                377685F31AC4B27D00026E6C /* make dyld_cache_config.h */ = {
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        shellPath = /bin/bash;
-                       shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n    # if iOS SDK not available, use MacOSX SDK\n    ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n    echo -n \"#define ARM_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n    echo -n \"#define ARM_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n    echo -n \"#define ARM64_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_BASE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n    echo -n \"#define ARM64_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_SIZE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nelse\n    echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n    exit 1\nfi\n\nif [ -r \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" ]; then\n    mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n    cp \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\"  \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\nif [ -r \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" ]; then\n    mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n    cp \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\"  \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\n\n";
+                       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[ \\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_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_SIZE_ARM64[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n    grep SHARED_REGION_BASE_ARM64_32 \"${SHARED_REGION_FILE}\" > /dev/null 2>&1\n    if [ \"$?\" -eq \"0\" ]; then\n        echo -n \"#define ARM64_32_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        awk '/define SHARED_REGION_BASE_ARM64_32/ { 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_32_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        awk '/define SHARED_REGION_SIZE_ARM64_32/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    fi\nelse\n    echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n    exit 1\nfi\n\nif [ -r \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" ]; then\n    mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n    cp \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\"  \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\nif [ -r \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" ]; then\n    mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n    cp \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\"  \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\n\n";
                        showEnvVarsInLog = 0;
                };
                F907E2490FA6469000BFEDBD /* install iPhone file */ = {
                        );
                        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;
-               };
-               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";
+                       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[ \\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_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_SIZE_ARM64[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n    grep SHARED_REGION_BASE_ARM64_32 \"${SHARED_REGION_FILE}\" > /dev/null 2>&1\n    if [ \"$?\" -eq \"0\" ]; then\n        echo -n \"#define ARM64_32_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        awk '/define SHARED_REGION_BASE_ARM64_32/ { 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_32_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        awk '/define SHARED_REGION_SIZE_ARM64_32/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    fi\nelse\n    echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n    exit 1\nfi\n\n";
                        showEnvVarsInLog = 0;
                };
                F9213B3F18BFC9CB001CB6E8 /* simulator entitlement */ = {
                        );
                        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";
+                       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\tif [ \"${RC_BRIDGE}\" != \"YES\" ]\n\tthen\n\t\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}\"\n\tfi\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;
                };
                F959621018849DF20003E4D4 /* add dyld symlink */ = {
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/bash;
-                       shellScript = "if [[ \"${PLATFORM_NAME}\"  == *simulator* ]]\nthen\n\tcd ${DSTROOT}/${INSTALL_PATH}\n\tln -s libdyld.dylib libdyld_sim.dylib\nfi\n";
+                       shellScript = "if [[ \"${PLATFORM_NAME}\"  == *simulator* ]]\nthen\n\tcd ${DSTROOT}\n    mkdir -p usr/lib/system/\n\tcd ${DSTROOT}/${INSTALL_PATH}\n\tln -s libdyld.dylib libdyld_sim.dylib\nfi\n";
                        showEnvVarsInLog = 0;
                };
                F960A78C1E405E2300840176 /* expand dyld_priv.h macros */ = {
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
-                       shellScript = "${SRCROOT}/bin/expand.pl < ${SRCROOT}/include/mach-o/dyld_priv.h >  ${DSTROOT}/usr/local/include/mach-o/dyld_priv.h\n";
+                       shellScript = "${SRCROOT}/bin/expand.rb < ${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 */ = {
                        );
                        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";
+                       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[ \\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_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_SIZE_ARM64[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n    grep SHARED_REGION_BASE_ARM64_32 \"${SHARED_REGION_FILE}\" > /dev/null 2>&1\n    if [ \"$?\" -eq \"0\" ]; then\n        echo -n \"#define ARM64_32_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        awk '/define SHARED_REGION_BASE_ARM64_32/ { 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_32_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        awk '/define SHARED_REGION_SIZE_ARM64_32/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n        echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    fi\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 */ = {
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        shellPath = /bin/sh;
-                       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";
+                       shellScript = "mkdir -p ${DERIVED_FILE_DIR}/mach-o\n${SRCROOT}/bin/expand.rb < ${SRCROOT}/include/mach-o/dyld_priv.h >  ${DERIVED_FILE_DIR}/mach-o/dyld_priv.h\n";
                        showEnvVarsInLog = 0;
                };
                F981C8C21F058F8200452F35 /* mkdir /var/db/dyld */ = {
                        );
                        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";
+                       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\nrm -rf ${DSTROOT}/usr/local/bin/dyld_shared_cache_util\nrm -rf ${DSTROOT}/usr/local/bin/dyld_closure_util\nfi\n";
                        showEnvVarsInLog = 0;
                };
                F9D050C811DD701A00FB0A29 /* configure archives */ = {
                        );
                        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";
+                       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# link with crypto archive if it exists\nif [ -f ${SDKROOT}/usr/local/lib/libcorecrypto_static.a ]\nthen\n  echo \\\"${SDKROOT}/usr/local/lib/libcorecrypto_static.a\\\" >> ${DERIVED_SOURCES_DIR}/archives.txt\nfi\n";
                        showEnvVarsInLog = 0;
                };
                F9F6F42B1C1FB0AE00BD8FED /* build */ = {
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               37554F521E3F78EB00407388 /* ImageProxy.cpp in Sources */,
+                               F93D734E1F8FF7C2007D9413 /* Closure.cpp in Sources */,
+                               F93D734F1F8FF7C2007D9413 /* ClosureWriter.cpp in Sources */,
+                               F93D73501F8FF7C2007D9413 /* ClosureBuilder.cpp in Sources */,
+                               F93D73511F8FF7C2007D9413 /* MachOFile.cpp in Sources */,
+                               C17984D61FE9E9160057D002 /* mrm_shared_cache_builder.cpp in Sources */,
+                               F93D73521F8FF7C2007D9413 /* MachOLoaded.cpp in Sources */,
+                               F93D73531F8FF7C2007D9413 /* MachOAnalyzer.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 */,
+                               C1D268351FE0A77B009F115B /* ClosureFileSystemPhysical.cpp in Sources */,
                                37554F4B1E3F76E900407388 /* AdjustDylibSegments.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               37554F511E3F78EB00407388 /* ImageProxy.cpp in Sources */,
+                               F93D734B1F8FF79E007D9413 /* MachOFile.cpp in Sources */,
+                               F93D734C1F8FF79E007D9413 /* MachOLoaded.cpp in Sources */,
+                               F93D734D1F8FF79E007D9413 /* MachOAnalyzer.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 */,
+                               F93D73481F8FF780007D9413 /* Closure.cpp in Sources */,
+                               F93D73491F8FF780007D9413 /* ClosureWriter.cpp in Sources */,
+                               F93D734A1F8FF780007D9413 /* ClosureBuilder.cpp in Sources */,
+                               C1D268401FE9B464009F115B /* ClosureFileSystemPhysical.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
-               F922C8131F33B73800D8F479 /* Sources */ = {
+               37F597C92061EB4200F9B6F9 /* Sources */ = {
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               F922C81C1F33B88400D8F479 /* libclosured-stub.cpp in Sources */,
+                               37F597D52061ED0B00F9B6F9 /* dyld_usage.cpp in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               C187B9001FE063A40042D3B7 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               C187B9181FE068260042D3B7 /* DyldSharedCache.cpp in Sources */,
+                               C1436B2C203BE67D00028AF1 /* FileUtils.cpp in Sources */,
+                               C187B91B1FE0683F0042D3B7 /* OptimizerLinkedit.cpp in Sources */,
+                               C187B90E1FE067CD0042D3B7 /* ClosureWriter.cpp in Sources */,
+                               C187B91E1FE0684C0042D3B7 /* AdjustDylibSegments.cpp in Sources */,
+                               C187B9191FE0682C0042D3B7 /* BuilderUtils.mm in Sources */,
+                               C187B90F1FE067D30042D3B7 /* ClosureBuilder.cpp in Sources */,
+                               C187B9131FE067F10042D3B7 /* CacheBuilder.cpp in Sources */,
+                               C187B9121FE067E60042D3B7 /* MachOAnalyzer.cpp in Sources */,
+                               C187B9161FE0680A0042D3B7 /* PathOverrides.cpp in Sources */,
+                               C187B9171FE068180042D3B7 /* Diagnostics.cpp in Sources */,
+                               C187B9151FE068000042D3B7 /* OptimizerObjC.cpp in Sources */,
+                               C187B9101FE067D90042D3B7 /* MachOFile.cpp in Sources */,
+                               C187B9111FE067E10042D3B7 /* MachOLoaded.cpp in Sources */,
+                               C187B90D1FE067C70042D3B7 /* Closure.cpp in Sources */,
+                               C1D268311FE0891C009F115B /* mrm_shared_cache_builder.cpp in Sources */,
+                               C187B9141FE067FA0042D3B7 /* OptimizerBranches.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               F93D73471F8C4E55007D9413 /* PathOverrides.cpp in Sources */,
+                               F92756811F68AF4D000820EE /* Closure.cpp in Sources */,
+                               F92756821F68AF4D000820EE /* ClosureWriter.cpp in Sources */,
+                               C1D2683F1FE98D4F009F115B /* ClosureFileSystemPhysical.cpp in Sources */,
+                               F92756831F68AF4D000820EE /* ClosureBuilder.cpp in Sources */,
+                               F92756841F68AF4D000820EE /* MachOFile.cpp in Sources */,
+                               F92756851F68AF4D000820EE /* MachOLoaded.cpp in Sources */,
+                               F92756861F68AF4D000820EE /* MachOAnalyzer.cpp 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;
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               F9653F941FAE51ED008B5D93 /* MachOAnalyzer.cpp in Sources */,
+                               F9653F8E1FAE51C9008B5D93 /* Closure.cpp in Sources */,
+                               F9653F8F1FAE51C9008B5D93 /* ClosureBuilder.cpp in Sources */,
+                               C172C9DD20252CB500159311 /* ClosureFileSystemPhysical.cpp in Sources */,
+                               F9653F901FAE51C9008B5D93 /* ClosureWriter.cpp in Sources */,
+                               F9653F911FAE51C9008B5D93 /* MachOFile.cpp in Sources */,
+                               F9653F921FAE51C9008B5D93 /* MachOLoaded.cpp in Sources */,
                                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;
                        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 */,
+                               C1D268371FE0BC5F009F115B /* ClosureFileSystemPhysical.cpp in Sources */,
+                               F9DFEA791F55DDC0003BF8A7 /* Closure.cpp in Sources */,
+                               F9DFEA7A1F55DDC4003BF8A7 /* ClosureWriter.cpp in Sources */,
+                               F9DFEA7B1F55DDC7003BF8A7 /* ClosureBuilder.cpp in Sources */,
+                               F9CC10D81F5F1D4E0021BFE2 /* MachOFile.cpp in Sources */,
+                               F9A5E6171F5C967C0030C490 /* MachOLoaded.cpp in Sources */,
+                               F9CC10D71F5F1D480021BFE2 /* MachOAnalyzer.cpp in Sources */,
+                               F9DFEA7D1F588506003BF8A7 /* ClosurePrinter.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               F93F46521FA420850060D9F9 /* execserver.defs in Sources */,
                                F97FF3611C23640C000ACDD2 /* nocr.c in Sources */,
-                               F988F0BB1E2FDF5B003AED79 /* execserver.defs in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               C1960ED02090D9F0007E3E6B /* Diagnostics.cpp in Sources */,
+                               C1960ED42090DA09007E3E6B /* Closure.cpp in Sources */,
+                               C1960ECF2090D9E5007E3E6B /* DyldSharedCache.cpp in Sources */,
+                               C1960ED32090D9FF007E3E6B /* MachOFile.cpp in Sources */,
+                               C1960ED22090D9FA007E3E6B /* MachOAnalyzer.cpp in Sources */,
                                F99B8EA30FEC1C4200701838 /* dsc_iterator.cpp in Sources */,
+                               C1960ED12090D9F6007E3E6B /* MachOLoaded.cpp in Sources */,
                                F99B8E630FEC11B400701838 /* dyld_shared_cache_util.cpp in Sources */,
                        );
                        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 */,
+                               C1D2683A1FE0BCF3009F115B /* ClosureFileSystemPhysical.cpp in Sources */,
                                F9ED4CD90630A7F100DF4E74 /* dyldAPIs.cpp in Sources */,
                                F9ED4CDA0630A7F100DF4E74 /* dyldExceptions.c in Sources */,
                                F9ED4CD60630A7F100DF4E74 /* dyld_gdb.cpp in Sources */,
+                               37D7DB001E96F0ED00D52CEA /* Tracing.cpp in Sources */,
                                F9ED4CE00630A7F100DF4E74 /* glue.c in Sources */,
                                F9280B7B1AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp in Sources */,
                                F9ED4CE10630A7F100DF4E74 /* ImageLoader.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 */,
+                               F936BF9720323F0F00568B23 /* FileUtils.cpp in Sources */,
+                               F93D73411F8404FA007D9413 /* MachOLoaded.cpp in Sources */,
+                               F93D73401F8404A2007D9413 /* MachOFile.cpp in Sources */,
+                               F93D73431F842CBF007D9413 /* MachOAnalyzer.cpp in Sources */,
+                               F93D733D1F82F03F007D9413 /* Closure.cpp in Sources */,
+                               F93D733E1F82F03F007D9413 /* ClosureWriter.cpp in Sources */,
+                               F93D733F1F82F03F007D9413 /* ClosureBuilder.cpp in Sources */,
+                               F93D73421F8421CC007D9413 /* PathOverrides.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        files = (
                                F9D49CCC1458B95200F86ADD /* start_glue.s in Sources */,
                                37D7DB011E96F3EB00D52CEA /* Tracing.cpp in Sources */,
+                               F9DFEA701F50FDE5003BF8A7 /* Closure.cpp in Sources */,
                                F9F256360639DBCC00A7427D /* dyldLock.cpp in Sources */,
                                F9BA514B0ECE4F4200D1D62E /* dyld_stub_binder.s in Sources */,
+                               C1D268391FE0BC94009F115B /* ClosureFileSystemPhysical.cpp in Sources */,
                                F9A221E70F3A6D7C00D15F73 /* dyldLibSystemGlue.c in Sources */,
                                F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */,
                                F9A6D6E4116F9DF20051CC16 /* threadLocalVariables.c in Sources */,
                                F9A6D70C116FBBD10051CC16 /* threadLocalHelpers.s in Sources */,
                                F95090E51C5AD1E80031F81D /* dyld_process_info.cpp in Sources */,
                                F958D4771C7FCE6700A0B199 /* dyld_process_info_notify.cpp in Sources */,
-                               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 */,
                                F9C15A4A1E1F7DAC0006E570 /* APIs_macOS.cpp in Sources */,
                                F97C61A21D9CAE3500A84CD7 /* Logging.cpp in Sources */,
                                F9C2755A1DA71CE8007A5D8A /* Loading.cpp in Sources */,
+                               F93D73441F8475C3007D9413 /* MachOFile.cpp in Sources */,
+                               F93D73451F8475C3007D9413 /* MachOLoaded.cpp in Sources */,
+                               F93D73461F8475C3007D9413 /* MachOAnalyzer.cpp in Sources */,
                                F90108611E2AD96000870568 /* PathOverrides.cpp in Sources */,
-                               F9D862431DC90A4F000A199A /* LaunchCacheReader.cpp in Sources */,
-                               F9E5E2C71EB00AAA0013A0BB /* ClosureBuffer.cpp in Sources */,
-                               F9C86B651E2B16C600FD8669 /* closuredProtocol.defs in Sources */,
+                               F9DFEA781F54FACF003BF8A7 /* ClosureBuilder.cpp in Sources */,
+                               F9DFEA741F54DB25003BF8A7 /* ClosureWriter.cpp in Sources */,
                                F97C619F1D9829AA00A84CD7 /* libdyldEntryVector.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        target = 37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */;
                        targetProxy = 37A0AD0E1C16000F00731E50 /* PBXContainerItemProxy */;
                };
+               C187B90C1FE067590042D3B7 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = C187B8FF1FE063A40042D3B7 /* libslc_builder.dylib */;
+                       targetProxy = C187B90B1FE067590042D3B7 /* PBXContainerItemProxy */;
+               };
                D8668AD01ECE335F005E7D31 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        target = F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */;
                        target = F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */;
                        targetProxy = F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */;
                };
-               F922C8121F33B62700D8F479 /* PBXTargetDependency */ = {
-                       isa = PBXTargetDependency;
-                       target = F9AB02B71F329FAA00EE96C4 /* libclosured */;
-                       targetProxy = F922C8111F33B62700D8F479 /* PBXContainerItemProxy */;
-               };
-               F922C81E1F33B96300D8F479 /* PBXTargetDependency */ = {
-                       isa = PBXTargetDependency;
-                       target = F922C8161F33B73800D8F479 /* libclosured-stub */;
-                       targetProxy = F922C81D1F33B96300D8F479 /* PBXContainerItemProxy */;
-               };
                F94182D81E60F0BE00D8EF25 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */;
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
-                               CLANG_CXX_LANGUAGE_STANDARD = "c++14";
                                CLANG_CXX_LIBRARY = "libc++";
                                CLANG_ENABLE_MODULES = NO;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                MACOSX_DEPLOYMENT_TARGET = 10.11;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                ONLY_ACTIVE_ARCH = YES;
-                               OTHER_CFLAGS = "-DBOM_SUPPORT=1";
+                               OTHER_CFLAGS = (
+                                       "-DBOM_SUPPORT=1",
+                                       "-DBUILDING_EMBEDDED_SHARED_CACHE_BUILDER=1",
+                               );
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SDKROOT = macosx.internal;
                                SUPPORTED_PLATFORMS = macosx;
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
-                               CLANG_CXX_LANGUAGE_STANDARD = "c++14";
                                CLANG_CXX_LIBRARY = "libc++";
                                CLANG_ENABLE_MODULES = NO;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
                                MACOSX_DEPLOYMENT_TARGET = 10.11;
                                MTL_ENABLE_DEBUG_INFO = NO;
-                               OTHER_CFLAGS = "-DBOM_SUPPORT=1";
+                               OTHER_CFLAGS = (
+                                       "-DBOM_SUPPORT=1",
+                                       "-DBUILDING_EMBEDDED_SHARED_CACHE_BUILDER=1",
+                               );
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SDKROOT = macosx.internal;
                                SUPPORTED_PLATFORMS = macosx;
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
-                               CLANG_CXX_LANGUAGE_STANDARD = "c++14";
                                CLANG_CXX_LIBRARY = "libc++";
                                CLANG_ENABLE_MODULES = NO;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                MACOSX_DEPLOYMENT_TARGET = 10.11;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                ONLY_ACTIVE_ARCH = YES;
-                               OTHER_CFLAGS = "-DBOM_SUPPORT=1";
+                               OTHER_CFLAGS = (
+                                       "-DBOM_SUPPORT=1",
+                                       "-DBUILDING_EMBEDDED_SHARED_CACHE_BUILDER=1",
+                               );
                                PRODUCT_NAME = multi_dyld_shared_cache_builder;
                                SDKROOT = macosx.internal;
                                STRIP_INSTALLED_PRODUCT = NO;
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
-                               CLANG_CXX_LANGUAGE_STANDARD = "c++14";
                                CLANG_CXX_LIBRARY = "libc++";
                                CLANG_ENABLE_MODULES = NO;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
                                MACOSX_DEPLOYMENT_TARGET = 10.11;
                                MTL_ENABLE_DEBUG_INFO = NO;
-                               OTHER_CFLAGS = "-DBOM_SUPPORT=1";
+                               OTHER_CFLAGS = (
+                                       "-DBOM_SUPPORT=1",
+                                       "-DBUILDING_EMBEDDED_SHARED_CACHE_BUILDER=1",
+                               );
                                PRODUCT_NAME = multi_dyld_shared_cache_builder;
                                SDKROOT = macosx.internal;
                                STRIP_INSTALLED_PRODUCT = NO;
                        };
                        name = Release;
                };
-               F908134311D3ED0C00626CC1 /* Debug */ = {
-                       isa = XCBuildConfiguration;
-                       buildSettings = {
-                               COPY_PHASE_STRIP = NO;
-                               GCC_DYNAMIC_NO_PIC = NO;
-                               GCC_OPTIMIZATION_LEVEL = 0;
-                               INSTALLHDRS_COPY_PHASE = YES;
-                               PRODUCT_NAME = libdyld;
-                       };
-                       name = Debug;
-               };
-               F908134411D3ED0C00626CC1 /* Release */ = {
-                       isa = XCBuildConfiguration;
-                       buildSettings = {
-                               COPY_PHASE_STRIP = YES;
-                               DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
-                               INSTALLHDRS_COPY_PHASE = YES;
-                               PRODUCT_NAME = libdyld;
-                               ZERO_LINK = NO;
-                       };
-                       name = Release;
-               };
-               F922C8191F33B73800D8F479 /* Debug */ = {
+               37F597D12061EB4200F9B6F9 /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                CLANG_CXX_LIBRARY = "libc++";
                                CLANG_ENABLE_MODULES = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
-                               CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
-                               CLANG_WARN_COMMA = YES;
+                               CLANG_ENABLE_OBJC_WEAK = YES;
+                               CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_IMPLICIT_RETAIN_SELF = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
-                               CLANG_WARN_STRICT_PROTOTYPES = YES;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CODE_SIGN_IDENTITY = "-";
+                               CODE_SIGN_STYLE = Automatic;
                                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_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;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx.internal;
+                               SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
+                               VALID_ARCHS = "arm64 arm64e x86_64";
                        };
                        name = Debug;
                };
-               F922C81A1F33B73800D8F479 /* Release */ = {
+               37F597D22061EB4200F9B6F9 /* Release */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                CLANG_CXX_LIBRARY = "libc++";
                                CLANG_ENABLE_MODULES = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
-                               CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
-                               CLANG_WARN_COMMA = YES;
+                               CLANG_ENABLE_OBJC_WEAK = YES;
+                               CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = 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_IMPLICIT_RETAIN_SELF = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
-                               CLANG_WARN_STRICT_PROTOTYPES = YES;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CODE_SIGN_IDENTITY = "-";
+                               CODE_SIGN_STYLE = Automatic;
                                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;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx.internal;
+                               SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
+                               VALID_ARCHS = "arm64 arm64e x86_64";
+                       };
+                       name = Release;
+               };
+               C187B9081FE063A40042D3B7 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_CXX_LIBRARY = "libc++";
+                               COMBINE_HIDPI_IMAGES = YES;
+                               COPY_PHASE_STRIP = NO;
+                               DEAD_CODE_STRIPPING = YES;
+                               FRAMEWORK_SEARCH_PATHS = (
+                                       "$(inherited)",
+                                       "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+                                       "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+                                       "$(SDKROOT)$(APPLE_INTERNAL_LIBRARY_DIR)/Frameworks",
+                               );
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_ENABLE_CPP_EXCEPTIONS = YES;
+                               GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+                               GCC_MODEL_TUNING = G5;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "DEBUG=1",
+                                       "$(inherited)",
+                                       "BUILDING_CACHE_BUILDER=1",
+                               );
+                               GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+                               GCC_WARN_SIGN_COMPARE = YES;
+                               INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/lib";
+                               MACH_O_TYPE = mh_dylib;
+                               MACOSX_DEPLOYMENT_TARGET = 10.11;
+                               OTHER_CFLAGS = "-DBOM_SUPPORT=1";
+                               PRODUCT_NAME = slc_builder;
+                               SDKROOT = macosx.internal;
+                               STRIP_INSTALLED_PRODUCT = NO;
+                               STRIP_STYLE = "non-global";
+                               USER_HEADER_SEARCH_PATHS = "../launch-cache/";
+                               VALID_ARCHS = "x86_64 x86_64h";
+                       };
+                       name = Debug;
+               };
+               C187B9091FE063A40042D3B7 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               CLANG_CXX_LIBRARY = "libc++";
+                               COMBINE_HIDPI_IMAGES = YES;
+                               COPY_PHASE_STRIP = NO;
+                               DEAD_CODE_STRIPPING = YES;
+                               FRAMEWORK_SEARCH_PATHS = (
+                                       "$(inherited)",
+                                       "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+                                       "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
+                                       "$(SDKROOT)$(APPLE_INTERNAL_LIBRARY_DIR)/Frameworks",
+                               );
+                               GCC_ENABLE_CPP_EXCEPTIONS = YES;
+                               GCC_ENABLE_CPP_RTTI = YES;
+                               GCC_ENABLE_OBJC_EXCEPTIONS = NO;
+                               GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+                               GCC_MODEL_TUNING = G5;
+                               GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_CACHE_BUILDER=1";
+                               GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+                               GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES;
+                               GCC_WARN_PEDANTIC = NO;
+                               GCC_WARN_SHADOW = NO;
+                               GCC_WARN_SIGN_COMPARE = YES;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               INSTALLHDRS_COPY_PHASE = YES;
+                               INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/lib";
+                               MACH_O_TYPE = mh_dylib;
+                               MACOSX_DEPLOYMENT_TARGET = 10.11;
+                               OTHER_CFLAGS = "-DBOM_SUPPORT=1";
+                               PRODUCT_NAME = slc_builder;
+                               SDKROOT = macosx.internal;
+                               STRIP_STYLE = "non-global";
+                               USER_HEADER_SEARCH_PATHS = "../launch-cache/";
+                               VALID_ARCHS = "x86_64 x86_64h";
+                               ZERO_LINK = NO;
+                       };
+                       name = Release;
+               };
+               F908134311D3ED0C00626CC1 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ARCHS = "$(ARCHS_STANDARD)";
+                               COPY_PHASE_STRIP = NO;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               INSTALLHDRS_COPY_PHASE = YES;
+                               PRODUCT_NAME = libdyld;
+                       };
+                       name = Debug;
+               };
+               F908134411D3ED0C00626CC1 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ARCHS = "$(ARCHS_STANDARD)";
+                               COPY_PHASE_STRIP = YES;
+                               DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+                               INSTALLHDRS_COPY_PHASE = YES;
+                               PRODUCT_NAME = libdyld;
+                               ZERO_LINK = NO;
                        };
                        name = Release;
                };
                F93937350A94FB2900070A07 /* Debug */ = {
                        isa = XCBuildConfiguration;
+                       baseConfigurationReference = F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */;
                        buildSettings = {
-                               CLANG_CXX_LANGUAGE_STANDARD = "c++14";
                                CLANG_CXX_LIBRARY = "libc++";
                                CLANG_ENABLE_OBJC_ARC = YES;
                                COPY_PHASE_STRIP = NO;
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "BUILDING_CACHE_BUILDER=1",
+                                       "BUILDING_UPDATE_DYLD_CACHE_BUILDER=1",
+                                       "DEBUG=1",
+                               );
                                GCC_THREADSAFE_STATICS = NO;
                                GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
                                GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
                                        "$(SRCROOT)/dyld3/shared-cache",
                                );
                                INSTALL_PATH = /usr/bin;
+                               MACOSX_DEPLOYMENT_TARGET = 10.13;
                                OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
                                OTHER_LDFLAGS = "-stdlib=libc++";
                                PRODUCT_NAME = update_dyld_shared_cache;
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */;
                        buildSettings = {
-                               CLANG_CXX_LANGUAGE_STANDARD = "c++14";
                                CLANG_CXX_LIBRARY = "libc++";
                                CLANG_ENABLE_OBJC_ARC = YES;
+                               CODE_SIGN_ENTITLEMENTS = "dyld3/shared-cache/update_dyld_shared_cache_entitlements.plist";
                                CODE_SIGN_IDENTITY = "-";
                                COPY_PHASE_STRIP = NO;
                                CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
                                );
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_OPTIMIZATION_LEVEL = s;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "BUILDING_CACHE_BUILDER=1",
+                                       "BUILDING_UPDATE_DYLD_CACHE_BUILDER=1",
+                               );
                                GCC_THREADSAFE_STATICS = NO;
                                GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
                                GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
                                        "$(SRCROOT)/dyld3/shared-cache",
                                );
                                INSTALL_PATH = /usr/bin;
+                               MACOSX_DEPLOYMENT_TARGET = 10.13;
                                OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
                                OTHER_LDFLAGS = "-stdlib=libc++";
                                PRODUCT_NAME = update_dyld_shared_cache;
                };
                F96354431DCD74A400895049 /* Debug */ = {
                        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;
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
                                GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_CACHE_BUILDER=1";
                                GCC_THREADSAFE_STATICS = NO;
                                GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
                                GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
                                SDKROOT = macosx.internal;
                                SIMULATOR_DIR_NAME = "$(PLATFORM_FAMILY_NAME_$(DEVICE_PLATFORM_NAME)) $(IPHONE_SDK_MARKETING_VERSION)";
                                USE_HEADERMAP = NO;
-                               VALID_ARCHS = x86_64;
+                               VALID_ARCHS = "x86_64 x86_64h";
                        };
                        name = Debug;
                };
                        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;
                                );
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_OPTIMIZATION_LEVEL = s;
+                               GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_CACHE_BUILDER=1";
                                GCC_THREADSAFE_STATICS = NO;
                                GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
                                GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
                                STRIP_INSTALLED_PRODUCT = YES;
                                STRIP_STYLE = debugging;
                                USE_HEADERMAP = NO;
-                               VALID_ARCHS = x86_64;
+                               VALID_ARCHS = "x86_64 x86_64h";
                                VERSIONING_SYSTEM = "apple-generic";
                        };
                        name = Release;
                        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;
                                GCC_WARN_SHADOW = YES;
                                GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+                               MACOSX_DEPLOYMENT_TARGET = 10.13;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                ONLY_ACTIVE_ARCH = YES;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SDKROOT = macosx.internal;
+                               SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
                        };
                        name = Debug;
                };
                        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;
                                GCC_WARN_SHADOW = YES;
                                GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+                               MACOSX_DEPLOYMENT_TARGET = 10.13;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SDKROOT = macosx.internal;
+                               SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
                        };
                        name = Release;
                };
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
-                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
                                CLANG_CXX_LIBRARY = "libc++";
                                CLANG_ENABLE_MODULES = NO;
                                CLANG_ENABLE_OBJC_ARC = YES;
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
-                               CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
                                CLANG_CXX_LIBRARY = "libc++";
                                CLANG_ENABLE_MODULES = NO;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_C_LANGUAGE_STANDARD = gnu99;
                                GCC_NO_COMMON_BLOCKS = YES;
+                               GCC_PREPROCESSOR_DEFINITIONS = "";
                                GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
                                GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
                                GCC_WARN_UNDECLARED_SELECTOR = YES;
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
-                               CLANG_CXX_LANGUAGE_STANDARD = "c++14";
                                COPY_PHASE_STRIP = NO;
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_MODEL_TUNING = G5;
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
                                PRODUCT_NAME = dyld_shared_cache_util;
                                SDKROOT = macosx.internal;
-                               SUPPORTED_PLATFORMS = macosx;
+                               SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
                        };
                        name = Debug;
                };
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
-                               CLANG_CXX_LANGUAGE_STANDARD = "c++14";
                                COPY_PHASE_STRIP = YES;
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                GCC_DYNAMIC_NO_PIC = NO;
                                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;
+                               SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
                        };
                        name = Release;
                };
                        baseConfigurationReference = F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
-                               CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
                                CLANG_CXX_LIBRARY = "libc++";
                                CLANG_WARN_EMPTY_BODY = YES;
                                CODE_SIGN_IDENTITY = "-";
                                        "$(ENTRY)",
                                );
                                STRIPFLAGS = "-S";
+                               SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos iphonesimulatornano iphonesimulator appletvsimulator";
                                UNSTRIPPED_PRODUCT = NO;
                                VERSIONING_SYSTEM = "apple-generic";
                                WARNING_CFLAGS = (
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */;
                        buildSettings = {
-                               CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
                                CLANG_CXX_LIBRARY = "libc++";
                                CLANG_WARN_EMPTY_BODY = YES;
                                CODE_SIGN_IDENTITY = "-";
                                        "$(ALIGNMENT)",
                                        "$(ENTRY)",
                                        "-Wl,-no_data_const",
-                                       "-Wl,-section_order,__DATA,__all_image_info:__nl_symbol_ptr:__got:__const:__crash_info:__data:__bss:__common",
+                                       "-Wl,-section_order,__DATA,__all_image_info:__nl_symbol_ptr:__got:__auth_ptr:__const:__crash_info:__data:__bss:__common",
                                );
                                STRIPFLAGS = "-S";
+                               SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos iphonesimulatornano iphonesimulator appletvsimulator";
                                UNSTRIPPED_PRODUCT = NO;
                                VERSIONING_SYSTEM = "apple-generic";
                                WARNING_CFLAGS = (
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */;
                        buildSettings = {
-                               CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
                                CLANG_WARN_EMPTY_BODY = YES;
                                CODE_SIGN_IDENTITY = "-";
                                COMBINE_HIDPI_IMAGES = YES;
                                GCC_ENABLE_CPP_EXCEPTIONS = NO;
                                GCC_ENABLE_CPP_RTTI = NO;
                                GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "BUILDING_LIBDYLD=1",
+                                       "DEBUG=1",
+                               );
                                GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
                                GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
                                GCC_WARN_ABOUT_RETURN_TYPE = YES;
                                OTHER_CFLAGS = "";
                                OTHER_CPLUSPLUSFLAGS = (
                                        "$(OTHER_CFLAGS)",
-                                       "-Wglobal-constructors",
+                                       "-fno-exceptions",
                                );
                                OTHER_LDFLAGS = (
                                        "-Wl,-no_inits",
                                        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 ";
+                               OTHER_TAPI_FLAGS = "-extra-public-header ./include/dlfcn.h -extra-private-header ./dyld3/libdyldEntryVector.h -ObjC++ -std=c++11 -umbrella System";
                                PRIVATE_HEADERS_FOLDER_PATH = "/usr/local/include/mach-o";
                                PRODUCT_NAME = dyld;
                                PUBLIC_HEADERS_FOLDER_PATH = "/usr//include/mach-o";
                                SKIP_INSTALL = NO;
+                               SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos iphonesimulatornano iphonesimulator appletvsimulator";
                                SUPPORTS_TEXT_BASED_API = YES;
                                TAPI_VERIFY_MODE = Pedantic;
                                VERSIONING_SYSTEM = "apple-generic";
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */;
                        buildSettings = {
-                               CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
                                CLANG_WARN_EMPTY_BODY = YES;
                                CODE_SIGN_IDENTITY = "-";
                                COMBINE_HIDPI_IMAGES = YES;
                                GCC_ENABLE_CPP_EXCEPTIONS = NO;
                                GCC_ENABLE_CPP_RTTI = NO;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+                               GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_LIBDYLD=1";
                                GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
                                GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
                                GCC_WARN_ABOUT_RETURN_TYPE = YES;
                                INSTALLHDRS_SCRIPT_PHASE = YES;
                                OTHER_CFLAGS = "";
                                OTHER_CPLUSPLUSFLAGS = (
-                                       "-fno-exceptions",
                                        "$(OTHER_CFLAGS)",
+                                       "-fno-exceptions",
                                );
                                OTHER_LDFLAGS = (
                                        "-Wl,-no_inits",
                                        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 ";
+                               OTHER_TAPI_FLAGS = "-extra-public-header ./include/dlfcn.h -extra-private-header ./dyld3/libdyldEntryVector.h -ObjC++ -std=c++11 -umbrella System";
                                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;
+                               SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos iphonesimulatornano iphonesimulator appletvsimulator";
                                SUPPORTS_TEXT_BASED_API = YES;
                                TAPI_VERIFY_MODE = Pedantic;
                                VERSIONING_SYSTEM = "apple-generic";
                        baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+                               CLANG_CXX_LANGUAGE_STANDARD = "c++14";
                                CLANG_CXX_LIBRARY = "compiler-default";
+                               CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
+                               CLANG_WARN_COMMA = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
+                               CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_INT_CONVERSION = YES;
+                               CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+                               CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+                               CLANG_WARN_STRICT_PROTOTYPES = YES;
                                CLANG_WARN_SUSPICIOUS_MOVE = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
                        baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+                               CLANG_CXX_LANGUAGE_STANDARD = "c++14";
                                CLANG_CXX_LIBRARY = "compiler-default";
+                               CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
+                               CLANG_WARN_COMMA = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
+                               CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_INT_CONVERSION = YES;
+                               CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+                               CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+                               CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+                               CLANG_WARN_STRICT_PROTOTYPES = YES;
                                CLANG_WARN_SUSPICIOUS_MOVE = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
                        };
                        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;
-               };
                F9F2A55A0F7AEE9900B7C9EB /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                PRODUCT_NAME = "$(TARGET_NAME)";
+                               SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos iphonesimulatornano iphonesimulator appletvsimulator";
                        };
                        name = Debug;
                };
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                PRODUCT_NAME = "$(TARGET_NAME)";
+                               SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos iphonesimulatornano iphonesimulator appletvsimulator";
                        };
                        name = Release;
                };
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
-               F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */ = {
+               37F597D32061EB4200F9B6F9 /* Build configuration list for PBXNativeTarget "dyld_usage" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
-                               F908134311D3ED0C00626CC1 /* Debug */,
-                               F908134411D3ED0C00626CC1 /* Release */,
+                               37F597D12061EB4200F9B6F9 /* Debug */,
+                               37F597D22061EB4200F9B6F9 /* Release */,
                        );
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
-               F922C8181F33B73800D8F479 /* Build configuration list for PBXNativeTarget "libclosured-stub" */ = {
+               C187B9071FE063A40042D3B7 /* Build configuration list for PBXNativeTarget "libslc_builder.dylib" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
-                               F922C8191F33B73800D8F479 /* Debug */,
-                               F922C81A1F33B73800D8F479 /* Release */,
+                               C187B9081FE063A40042D3B7 /* Debug */,
+                               C187B9091FE063A40042D3B7 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               F908134311D3ED0C00626CC1 /* Debug */,
+                               F908134411D3ED0C00626CC1 /* Release */,
                        );
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
-               F9AB02C21F329FAA00EE96C4 /* Build configuration list for PBXNativeTarget "libclosured" */ = {
-                       isa = XCConfigurationList;
-                       buildConfigurations = (
-                               F9AB02C01F329FAA00EE96C4 /* Debug */,
-                               F9AB02C11F329FAA00EE96C4 /* Release */,
-                       );
-                       defaultConfigurationIsVisible = 0;
-                       defaultConfigurationName = Release;
-               };
                F9D1001714D8D0F100099D91 /* Build configuration list for PBXNativeTarget "dsc_extractor" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
                        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 = (
index 360a1cdd9e50f28177717f6fa389c890db01910c..111b8f6d857ae3ea8b3909f59e86addc4c93116b 100644 (file)
@@ -24,7 +24,6 @@
 
 #include <string.h>
 #include <stdint.h>
-#include <_simple.h>
 #include <sys/errno.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <TargetConditionals.h>
 #include <CommonCrypto/CommonDigest.h>
 #include <dispatch/dispatch.h>
+#include <_simple.h>
 
+#include <array>
 #include <algorithm>
 
 #include "dlfcn.h"
+#include "dyld.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"
-
+#include "Closure.h"
+#include "MachOLoaded.h"
+#include "ClosureBuilder.h"
+#include "ClosureFileSystemPhysical.h"
 
-
-extern "C" {
-    #include "closuredProtocol.h"
-}
+#if __has_feature(ptrauth_calls)
+#include <ptrauth.h>
+#endif
 
 
 namespace dyld {
@@ -64,6 +66,20 @@ namespace dyld {
 namespace dyld3 {
 
 
+static const void *stripPointer(const void *ptr) {
+#if __has_feature(ptrauth_calls)
+    return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
+#else
+    return ptr;
+#endif
+}
+
+pthread_mutex_t RecursiveAutoLock::_sMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
+
+// forward declaration
+static void dyld_get_image_versions_internal(const struct mach_header* mh, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version));
+
+
 uint32_t _dyld_image_count(void)
 {
     log_apis("_dyld_image_count()\n");
@@ -74,27 +90,25 @@ uint32_t _dyld_image_count(void)
 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;
+    return gAllImages.imageLoadAddressByIndex(imageIndex);
 }
 
 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();
+    const MachOLoaded* mf = (MachOLoaded*)mh;
+    if ( !mf->hasMachOMagic() )
+        return 0;
+
+    return mf->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);
+    const mach_header* mh = gAllImages.imageLoadAddressByIndex(imageIndex);
     if ( mh != nullptr )
         return dyld3::_dyld_get_image_slide(mh);
     return 0;
@@ -103,12 +117,7 @@ intptr_t _dyld_get_image_vmaddr_slide(uint32_t imageIndex)
 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;
+    return gAllImages.imagePathByIndex(imageIndex);
 }
 
 
@@ -154,8 +163,7 @@ 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) {
+    gAllImages.mainExecutable()->forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t compatVersion, uint32_t currentVersion, bool& stop) {
         if ( nameMatch(loadPath, libraryName) )
             result = currentVersion;
     });
@@ -175,170 +183,239 @@ int32_t NSVersionOfLinkTimeLibrary(const char* libraryName)
 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;
-            }
+    __block int32_t result = -1;
+    gAllImages.forEachImage(^(const dyld3::LoadedImage& loadedImage, bool &stop) {
+        const char* installName;
+        uint32_t currentVersion;
+        uint32_t compatVersion;
+        if ( loadedImage.loadedAddress()->getDylibInstallName(&installName, &compatVersion, &currentVersion) && nameMatch(installName, libraryName) ) {
+            result = currentVersion;
+            stop = true;
         }
-    }
-    log_apis("   NSVersionOfRunTimeLibrary() => -1\n");
-    return -1;
+    });
+    log_apis("   NSVersionOfRunTimeLibrary() => 0x%08X\n", result);
+    return result;
 }
 
 
-#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;
+    __block uint32_t retval = 0;
+    __block bool versionFound = false;
+    dyld3::dyld_get_image_versions_internal(gAllImages.mainExecutable(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+        if (versionFound) return;
 
-    MachOParser parser(gAllImages.mainExecutable());
-    if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
-        if ( platform == Platform::watchOS )
-            return sdk;
-    }
-    return 0;
+        if (dyld_get_base_platform(platform) == PLATFORM_WATCHOS) {
+            versionFound = true;
+            retval = sdk_version;
+        }
+    });
+
+    return retval;
 }
 
 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;
+    __block uint32_t retval = 0;
+    __block bool versionFound = false;
+    dyld3::dyld_get_image_versions_internal(gAllImages.mainExecutable(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+        if (versionFound) return;
 
-    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;
+        if (dyld_get_base_platform(platform) == PLATFORM_WATCHOS) {
+            versionFound = true;
+            retval = min_version;
+        }
+    });
+
+    return retval;
 }
-#endif
 
+uint32_t dyld_get_program_sdk_bridge_os_version()
+{
+   log_apis("dyld_get_program_sdk_bridge_os_version()\n");
 
-#if TARGET_OS_BRIDGE
+    __block uint32_t retval = 0;
+    __block bool versionFound = false;
+    dyld3::dyld_get_image_versions_internal(gAllImages.mainExecutable(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+        if (versionFound) return;
 
-static uint32_t bridgeVersToIOSVers(uint32_t vers)
-{
-    return vers + 0x00090000;
+        if (dyld_get_base_platform(platform) == PLATFORM_BRIDGEOS) {
+            versionFound = true;
+            retval = sdk_version;
+        }
+    });
+
+    return retval;
 }
 
-uint32_t dyld_get_program_sdk_bridge_os_version()
+uint32_t dyld_get_program_min_bridge_os_version()
+{
+    log_apis("dyld_get_program_min_bridge_os_version()\n");
+
+    __block uint32_t retval = 0;
+    __block bool versionFound = false;
+    dyld3::dyld_get_image_versions_internal(gAllImages.mainExecutable(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+        if (versionFound) return;
+
+        if (dyld_get_base_platform(platform) == PLATFORM_BRIDGEOS) {
+            versionFound = true;
+            retval = min_version;
+        }
+    });
+
+    return retval;
+ }
+
+//
+// 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_program_sdk_bridge_os_version()\n");
+    log_apis("dyld_get_sdk_version(%p)\n", mh);
+    __block bool versionFound = false;
+    __block uint32_t retval = 0;
+    dyld3::dyld_get_image_versions(mh, ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+        if (versionFound) return;
+
+        if (platform == ::dyld_get_active_platform()) {
+            versionFound = true;
+            switch (dyld3::dyld_get_base_platform(platform)) {
+                case PLATFORM_BRIDGEOS: retval = sdk_version + 0x00090000; return;
+                case PLATFORM_WATCHOS:  retval = sdk_version + 0x00070000; return;
+                default: retval = sdk_version; return;
+            }
+        } else if (platform == PLATFORM_IOSSIMULATOR && ::dyld_get_active_platform() == PLATFORM_IOSMAC) {
+            //FIXME bringup hack
+            versionFound = true;
+            retval = 0x000C0000;
+        }
+    });
 
-    Platform platform;
-    uint32_t minOS;
-    uint32_t sdk;
+    return retval;
+}
 
-    MachOParser parser(gAllImages.mainExecutable());
-    if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
-        if ( platform == Platform::bridgeOS )
-            return sdk;
+uint32_t dyld_get_program_sdk_version()
+{
+       log_apis("dyld_get_program_sdk_version()\n");
+    static uint32_t sProgramSDKVersion = 0;
+    if (sProgramSDKVersion  == 0) {
+        sProgramSDKVersion = dyld3::dyld_get_sdk_version(gAllImages.mainExecutable());
     }
-    return 0;
+    return sProgramSDKVersion;
 }
 
-uint32_t dyld_get_program_min_bridge_os_version()
+uint32_t dyld_get_min_os_version(const mach_header* mh)
 {
-    log_apis("dyld_get_program_min_bridge_os_version()\n");
+    log_apis("dyld_get_min_os_version(%p)\n", mh);
+    __block bool versionFound = false;
+    __block uint32_t retval = 0;
+    dyld3::dyld_get_image_versions(mh, ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+        if (versionFound) return;
+
+        if (platform == ::dyld_get_active_platform()) {
+            versionFound = true;
+            switch (dyld3::dyld_get_base_platform(platform)) {
+                case PLATFORM_BRIDGEOS: retval = min_version + 0x00090000; return;
+                case PLATFORM_WATCHOS:  retval = min_version + 0x00070000; return;
+                default: retval = min_version; return;
+            }
+        } else if (platform == PLATFORM_IOSSIMULATOR && ::dyld_get_active_platform() == PLATFORM_IOSMAC) {
+            //FIXME bringup hack
+            versionFound = true;
+            retval = 0x000C0000;
+        }
+    });
 
-    Platform platform;
-    uint32_t minOS;
-    uint32_t sdk;
+    return retval;
+}
 
-    MachOParser parser(gAllImages.mainExecutable());
-    if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) {
-        if ( platform == Platform::bridgeOS )
-            return minOS;  // return raw minOS (not mapped to iOS version)
+dyld_platform_t dyld_get_active_platform(void) {
+    return gAllImages.platform();
+}
+
+dyld_platform_t dyld_get_base_platform(dyld_platform_t platform) {
+    switch (platform) {
+        case PLATFORM_IOSMAC:               return PLATFORM_IOS;
+        case PLATFORM_IOSSIMULATOR:         return PLATFORM_IOS;
+        case PLATFORM_WATCHOSSIMULATOR:     return PLATFORM_WATCHOS;
+        case PLATFORM_TVOSSIMULATOR:        return PLATFORM_TVOS;
+        default:                            return platform;
     }
-    return 0;
 }
 
-#endif
+bool dyld_is_simulator_platform(dyld_platform_t platform) {
+    switch(platform) {
+        case PLATFORM_IOSSIMULATOR:
+        case PLATFORM_WATCHOSSIMULATOR:
+        case PLATFORM_TVOSSIMULATOR:
+            return true;
+        default:
+            return false;
+    }
+}
 
+bool dyld_sdk_at_least(const struct mach_header* mh, dyld_build_version_t version) {
+    __block bool retval = false;
+    dyld3::dyld_get_image_versions(mh, ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+        if (dyld3::dyld_get_base_platform(platform) == version.platform && sdk_version >= version.version) {
+            retval = true;
+        }
+    });
+    return retval;
+}
 
-#if !__WATCH_OS_VERSION_MIN_REQUIRED && !__TV_OS_VERSION_MIN_REQUIRED && !TARGET_OS_BRIDGE
+bool dyld_minos_at_least(const struct mach_header* mh, dyld_build_version_t version) {
+    __block bool retval = false;
+    dyld3::dyld_get_image_versions(mh, ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+        if (dyld3::dyld_get_base_platform(platform) == version.platform && min_version >= version.version) {
+            retval = true;
+        }
+    });
+    return retval;
+}
 
-#define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff))
+bool dyld_program_sdk_at_least(dyld_build_version_t version) {
+    return dyld3::dyld_sdk_at_least(gAllImages.mainExecutable(), version);
+}
 
-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;
+bool dyld_program_minos_at_least(dyld_build_version_t version) {
+    return dyld3::dyld_minos_at_least(gAllImages.mainExecutable(), version);
+}
+
+static
+uint32_t linkedDylibVersion(const mach_header* mh, const char *installname) {
+    __block uint32_t retval = 0;
+    ((MachOLoaded*)mh)->forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t compatVersion, uint32_t currentVersion, bool& stop) {
+        if (strcmp(loadPath, installname) == 0) {
+            retval = currentVersion;
+            stop = true;
+        }
     });
+    return retval;
+}
 
+#define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff))
+
+static uint32_t deriveVersionFromDylibs(const struct mach_header* mh) {
+    // This is a binary without a version load command, we need to infer things
     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[] = {
+    uint32_t linkedVersion = 0;
+#if TARGET_OS_OSX
+    linkedVersion = linkedDylibVersion(mh, "/usr/lib/libSystem.B.dylib");
+    static const DylibToOSMapping versionMapping[] = {
         { PACKED_VERSION(88,1,3),   0x000A0400 },
         { PACKED_VERSION(111,0,0),  0x000A0500 },
         { PACKED_VERSION(123,0,0),  0x000A0600 },
@@ -349,159 +426,107 @@ static uint32_t deriveSDKVersFromDylibs(const mach_header* mh)
         // We don't need to expand this table because all recent
         // binaries have LC_VERSION_MIN_ load command.
     };
-
-    if ( libSystemVers != 0 ) {
+#elif TARGET_OS_IOS
+        linkedVersion = linkedDylibVersion(mh, "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation");
+        static const DylibToOSMapping versionMapping[] = {
+            { 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.
+    };
+#else
+    static const DylibToOSMapping versionMapping[] = {};
+#endif
+    if ( linkedVersion != 0 ) {
         uint32_t lastOsVersion = 0;
-        for (const DylibToOSMapping* p=libSystemMapping; ; ++p) {
-            if ( p->dylibVersion == 0 )
+        for (const DylibToOSMapping* p=versionMapping; ; ++p) {
+            if ( p->dylibVersion == 0 ) {
                 return p->osVersion;
-            if ( libSystemVers < p->dylibVersion )
+            }
+            if ( linkedVersion < p->dylibVersion ) {
                 return lastOsVersion;
+            }
             lastOsVersion = p->osVersion;
         }
     }
-  #endif
-  return 0;
+    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)
+// assumes mh has already been validated
+static void dyld_get_image_versions_internal(const struct mach_header* mh, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version))
 {
-    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;
+    const MachOFile* mf = (MachOFile*)mh;
+    __block bool lcFound = false;
+    mf->forEachSupportedPlatform(^(dyld3::Platform platform, uint32_t minOS, uint32_t sdk) {
+        lcFound = true;
+        // If SDK field is empty then derive the value from library linkages
+        if (sdk == 0) {
+            sdk = deriveVersionFromDylibs(mh);
         }
-    }
+        callback((const dyld_platform_t)platform, sdk, minOS);
+    });
 
-#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;
+    // No load command was found, so again, fallback to deriving it from library linkages
+    if (!lcFound) {
+#if TARGET_OS_IOS
+#if __x86_64__ || __x86__
+        dyld_platform_t platform = PLATFORM_IOSSIMULATOR;
 #else
-    // MacOSX and iOS have old binaries without version load commmand.
-    return deriveSDKVersFromDylibs(mh);
+        dyld_platform_t platform = PLATFORM_IOS;
 #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;
+#elif TARGET_OS_OSX
+        dyld_platform_t platform = PLATFORM_MACOS;
 #else
-            case Platform::macOS:
-                return minOS;
+        dyld_platform_t platform = 0;
 #endif
-            default:
-                // wrong binary for this platform
-                break;
+        uint32_t derivedVersion = deriveVersionFromDylibs(mh);
+        if ( platform != 0 && derivedVersion != 0 ) {
+            callback(platform, derivedVersion, 0);
         }
     }
-    return 0;
 }
 
+void dyld_get_image_versions(const struct mach_header* mh, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version))
+{
+    Diagnostics diag;
+    const MachOFile* mf = (MachOFile*)mh;
+    if ( mf->isMachO(diag, mh->sizeofcmds + sizeof(mach_header_64)) )
+        dyld_get_image_versions_internal(mh, callback);
+}
 
 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());
+    log_apis("dyld_get_program_min_os_version()\n");
+    static uint32_t sProgramMinVersion = 0;
+    if (sProgramMinVersion  == 0) {
+        sProgramMinVersion = dyld3::dyld_get_min_os_version(gAllImages.mainExecutable());
+    }
+    return sProgramMinVersion;
 }
 
-
 bool _dyld_get_image_uuid(const mach_header* mh, uuid_t uuid)
 {
-     log_apis("_dyld_get_image_uuid(%p, %p)\n", mh, uuid);
+    log_apis("_dyld_get_image_uuid(%p, %p)\n", mh, uuid);
 
-    if ( !MachOParser::wellFormedMachHeaderAndLoadCommands(mh) )
+    const MachOFile* mf = (MachOFile*)mh;
+    if ( !mf->hasMachOMagic() )
         return false;
-    MachOParser parser(mh);
-    return parser.getUuid(uuid);
+
+    return mf->getUuid(uuid);
 }
 
 //
@@ -512,18 +537,16 @@ bool _dyld_get_image_uuid(const mach_header* mh, uuid_t uuid)
 //
 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;
+    log_apis("_NSGetExecutablePath(%p, %p)\n", buf, bufsize);
+
+    const closure::Image* mainImage = gAllImages.mainExecutableImage();
+    const char* path = gAllImages.imagePath(mainImage);
+    size_t pathSize = strlen(path) + 1;
+    if ( *bufsize >= pathSize ) {
+        strcpy(buf, path);
+        return 0;
     }
+    *bufsize = (uint32_t)pathSize;
     return -1;
 }
 
@@ -555,10 +578,12 @@ 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;
+    addr = stripPointer(addr);
+
+    const MachOLoaded* ml;
+    if ( gAllImages.infoForImageMappedAt(addr, &ml, nullptr, nullptr) )
+        return ml;
+
     return nullptr;
 }
 
@@ -567,51 +592,18 @@ 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;
+    addr = stripPointer(addr);
+    const char* result = gAllImages.pathForImageMappedAt(addr);
+
+    log_apis("   dyld_image_path_containing_address() => %s\n", result);
+    return result;
 }
 
+    
+
 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);
+    return gAllImages.immutableMemory(addr, length);
 }
 
 
@@ -619,37 +611,49 @@ 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 ) {
+    // <rdar://problem/42171466> calling dladdr(xx,NULL) crashes
+    if ( info == NULL )
+        return 0; // failure
+
+    addr = stripPointer(addr);
+
+    __block int         result = 0;
+    const MachOLoaded*  ml     = nullptr;
+    const char*         path   = nullptr;
+    if ( gAllImages.infoForImageMappedAt(addr, &ml, nullptr, &path) ) {
+        info->dli_fname = path;
+        info->dli_fbase = (void*)ml;
+
+        uint64_t symbolAddr;
+        if ( addr == info->dli_fbase ) {
+            // special case lookup of header
+            info->dli_sname = "__dso_handle";
+            info->dli_saddr = info->dli_fbase;
+        }
+        else if ( ml->findClosestSymbol((long)addr, &(info->dli_sname), &symbolAddr) ) {
+            info->dli_saddr = (void*)(long)symbolAddr;
+            // 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;
         }
-        // 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;
+        result = 1;
     }
-    log_apis("   dladdr() => 1, { \"%s\", %p, \"%s\", %p }\n", info->dli_fname, info->dli_fbase, info->dli_sname, info->dli_saddr);
-    return 1;
+
+    if ( result == 0 )
+        log_apis("   dladdr() => 0\n");
+    else
+        log_apis("   dladdr() => 1, { \"%s\", %p, \"%s\", %p }\n", info->dli_fname, info->dli_fbase, info->dli_sname, info->dli_saddr);
+    return result;
 }
 
 
@@ -731,21 +735,6 @@ char* dlerror()
 #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);
@@ -753,14 +742,15 @@ static void* makeDlHandle(const mach_header* mh, bool dontContinue)
 }
 
 VIS_HIDDEN
-void parseDlHandle(void* h, const mach_header** mh, bool* dontContinue)
+void parseDlHandle(void* h, const MachOLoaded** mh, bool* dontContinue)
 {
     *dontContinue = (((uintptr_t)h) & 1);
-    *mh           = (const mach_header*)((((uintptr_t)h) & (-2)) << 5);
+    *mh           = (const MachOLoaded*)((((uintptr_t)h) & (-2)) << 5);
 }
 
 int dlclose(void* handle)
 {
+    DYLD_LOAD_LOCK_THIS_BLOCK
     log_apis("dlclose(%p)\n", handle);
 
     // silently accept magic handles for main executable
@@ -769,17 +759,22 @@ int dlclose(void* handle)
     if ( handle == RTLD_DEFAULT )
         return 0;
     
-   // from here on, serialize all dlopen()s
-    RecursiveAutoLock dlopenSerializer;
-
-    const mach_header*  mh;
+    const MachOLoaded*  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);
+
+    __block bool unloadable = false;
+    __block bool validHandle = false;
+    gAllImages.infoForImageMappedAt(mh, ^(const LoadedImage& foundImage, uint8_t permissions) {
+        validHandle = true;
+        if ( !foundImage.image()->neverUnload() )
+            unloadable = true;
+    });
+    if ( unloadable ) {
+        gAllImages.decRefCount(mh);  // removes image if reference count went to zero
+    }
+
+    if ( validHandle ) {
         clearErrorString();
         return 0;
     }
@@ -790,92 +785,9 @@ int dlclose(void* handle)
 }
 
 
-
-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)
+void* dlopen_internal(const char* path, int mode, void* callerAddress)
 {    
+    DYLD_LOAD_LOCK_THIS_BLOCK
     log_apis("dlopen(\"%s\", 0x%08X)\n", ((path==NULL) ? "NULL" : path), mode);
 
     clearErrorString();
@@ -889,293 +801,145 @@ void* dlopen(const char* path, int mode)
             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 __IPHONE_OS_VERSION_MIN_REQUIRED
+    // <rdar://problem/40235395> dyld3: dlopen() not working with non-canonical paths
+    char canonicalPath[PATH_MAX];
+    if ( leafName != path ) {
+        // make path canonical if it contains a // or ./
+        if ( (strstr(path, "//") != NULL) || (strstr(path, "./") != NULL) ) {
+            const char* lastSlash = strrchr(path, '/');
+            char dirPath[PATH_MAX];
+            if ( strlcpy(dirPath, path, sizeof(dirPath)) < sizeof(dirPath) ) {
+                dirPath[lastSlash-path] = '\0';
+                if ( realpath(dirPath, canonicalPath) ) {
+                    strlcat(canonicalPath, "/", sizeof(canonicalPath));
+                    if ( strlcat(canonicalPath, lastSlash+1, sizeof(canonicalPath)) < sizeof(canonicalPath) ) {
+                        // if all fit in buffer, use new canonical path
+                        path = canonicalPath;
+                    }
+                }
+            }
         }
-    });
-    if ( alreadyLoadMH != nullptr) {
-        void* result = makeDlHandle(alreadyLoadMH, dontContinue);
-        log_apis("   dlopen(%s) => %p\n", leafName, result);
-        return result;
     }
+#endif
 
-    // 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;
-        }
-    }
+    // RTLD_FIRST means when dlsym() is called with handle, only search the image and not those loaded after it
+    const bool firstOnly = (mode & RTLD_FIRST);
 
-    // 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;
-        });
-    }
+    // RTLD_LOCAL means when flat searches of all images (e.g. RTLD_DEFAULT) is done, this image should be skipped. But dlsym(handle, xx) can find symbols
+    const bool rtldLocal = (mode & RTLD_LOCAL);
 
-    // 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_NODELETE means don't unmap image during dlclose(). Leave the memory mapped, but orphan (leak) it.
+    // Note: this is a weird state and it slightly different semantics that other OSs
+    const bool rtldNoDelete = (mode & RTLD_NODELETE);
 
     // RTLD_NOLOAD means do nothing if image not already loaded
-    if ( mode & RTLD_NOLOAD ) {
-        log_apis("   dlopen(%s) => NULL\n", leafName);
+    const bool rtldNoLoad = (mode & RTLD_NOLOAD);
+
+    // try to load image from specified path
+    Diagnostics diag;
+    const mach_header* topLoadAddress = gAllImages.dlopen(diag, path, rtldNoLoad, rtldLocal, rtldNoDelete, false, callerAddress);
+    if ( diag.hasError() ) {
+        setErrorString("dlopen(%s, 0x%04X): %s", path, mode, diag.errorMessage());
+        log_apis("   dlopen: closure creation error: %s\n", diag.errorMessage());
         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;
+    if ( topLoadAddress == nullptr ) {
+        log_apis("   dlopen(%s) => NULL\n", leafName);
+        return nullptr;
     }
-    for (int i=0; i < closuredErrorMessagesCount;++i)
-        free((void*)closuredErrorMessages[i]);
-
-    log_apis("   dlopen(%s) => NULL\n", leafName);
+    void* result = makeDlHandle(topLoadAddress, firstOnly);
+    log_apis("   dlopen(%s) => %p\n", leafName, result);
+    return result;
 
-    return nullptr;
 }
 
-bool dlopen_preflight(const char* path)
+bool dlopen_preflight_internal(const char* path)
 {
+    DYLD_LOAD_LOCK_THIS_BLOCK
     log_apis("dlopen_preflight(%s)\n", path);
 
-    if ( gAllImages.alreadyLoaded(path, false) != nullptr )
+    // check if path is in dyld shared cache
+    if ( gAllImages.dyldCacheHasPath(path) )
         return true;
 
-    if ( gAllImages.findImageInKnownGroups(path) != nullptr )
+    // check if file is loadable
+    Diagnostics diag;
+    closure::FileSystemPhysical fileSystem;
+    closure::LoadedFileInfo loadedFileInfo = MachOAnalyzer::load(diag, fileSystem, path, MachOFile::currentArchName(), MachOFile::currentPlatform());
+    if ( loadedFileInfo.fileContent != nullptr ) {
+        fileSystem.unloadFile(loadedFileInfo);
         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;
+    return false;
 }
 
-static void* dlsym_search(const char* symName, const mach_header* startImageLoadAddress, const launch_cache::Image& startImage, bool searchStartImage, MachOParser::DependentFinder reExportFollower)
+static void* dlsym_search(const char* symName, const LoadedImage& start, bool searchStartImage, MachOLoaded::DependentToMachOLoaded reExportHelper,
+                          bool* resultPointsToInstructions)
 {
-    // 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);
+    MachOLoaded::DependentToMachOLoaded finder = ^(const MachOLoaded* mh, uint32_t depIndex) {
+        return gAllImages.findDependent(mh, depIndex);
+    };
+    //fprintf(stderr, "dlsym_search: %s, start=%s\n", symName, start.image()->path());
 
+    // walk all dependents of 'start' in order looking for symbol
     __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) )
+    gAllImages.visitDependentsTopDown(start, ^(const LoadedImage& aLoadedImage, bool& stop) {
+        //fprintf(stderr, "    search: %s\n", aLoadedImage.image()->path());
+        if ( !searchStartImage && aLoadedImage.image() == start.image() )
             return;
-        if ( loadAddress != nullptr ) {
-            MachOParser parser(loadAddress);
-            if ( parser.hasExportedSymbol(symName, reExportFollower, &result) ) {
-                stop = true;
-            }
+        if ( aLoadedImage.loadedAddress()->hasExportedSymbol(symName, finder, &result, resultPointsToInstructions) ) {
+            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)
+
+void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress)
 {
     log_apis("dlsym(%p, \"%s\")\n", handle, symbolName);
 
     clearErrorString();
 
+    MachOLoaded::DependentToMachOLoaded finder = ^(const MachOLoaded* mh, uint32_t depIndex) {
+        return gAllImages.findDependent(mh, depIndex);
+    };
+
     // 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];
+    BLOCK_ACCCESSIBLE_ARRAY(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);
-    };
-
+    __block void* result = nullptr;
+    __block bool resultPointsToInstructions = false;
     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;
-                }
+        gAllImages.forEachImage(^(const LoadedImage& loadedImage, bool& stop) {
+            if ( loadedImage.hideFromFlatSearch() )
+                return;
+            if ( loadedImage.loadedAddress()->hasExportedSymbol(underscoredName, finder, &result, &resultPointsToInstructions) ) {
+                stop = true;
             }
+        });
+        if ( result != nullptr ) {
+#if __has_feature(ptrauth_calls)
+            if (resultPointsToInstructions)
+                result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
+#endif
+            log_apis("   dlsym() => %p\n", result);
+            return result;
         }
         setErrorString("dlsym(RTLD_DEFAULT, %s): symbol not found", symbolName);
         log_apis("   dlsym() => NULL\n");
@@ -1183,63 +947,73 @@ void* dlsym(void* handle, const char* symbolName)
     }
     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) ) {
+        if ( gAllImages.mainExecutable()->hasExportedSymbol(underscoredName, finder, &result, &resultPointsToInstructions) ) {
             log_apis("   dlsym() => %p\n", result);
+#if __has_feature(ptrauth_calls)
+            if (resultPointsToInstructions)
+                result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
+#endif
             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() ) {
+        __block bool foundCaller = false;
+        gAllImages.infoForImageMappedAt(callerAddress, ^(const LoadedImage& foundImage, uint8_t permissions) {
+            foundCaller = true;
+            result = dlsym_search(underscoredName, foundImage, false, finder, &resultPointsToInstructions);
+        });
+        if ( !foundCaller ) {
             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() ) {
+        __block bool foundCaller = false;
+        gAllImages.infoForImageMappedAt(callerAddress, ^(const LoadedImage& foundImage, uint8_t permissions) {
+            foundCaller = true;
+            result = dlsym_search(underscoredName, foundImage, true, finder, &resultPointsToInstructions);
+        });
+        if ( !foundCaller ) {
             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() ) {
+        const MachOLoaded*  mh;
+        bool                dontContinue;
+        parseDlHandle(handle, &mh, &dontContinue);
+
+        __block bool foundCaller = false;
+        gAllImages.infoForImageWithLoadAddress(mh, ^(const LoadedImage& foundImage) {
+            foundCaller = true;
+            if ( dontContinue ) {
+                // RTLD_FIRST only searches one place
+                // we go through infoForImageWithLoadAddress() to validate the handle
+                mh->hasExportedSymbol(underscoredName, finder, &result, &resultPointsToInstructions);
+            }
+            else {
+                result = dlsym_search(underscoredName, foundImage, true, finder, &resultPointsToInstructions);
+            }
+        });
+        if ( !foundCaller ) {
             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 ) {
+#if __has_feature(ptrauth_calls)
+        if (resultPointsToInstructions)
+            result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
+#endif
         log_apis("   dlsym() => %p\n", result);
         return result;
     }
@@ -1286,44 +1060,73 @@ const void* _dyld_get_shared_cache_range(size_t* mappedSize)
     return NULL;
 }
 
-bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info)
+void _dyld_images_for_addresses(unsigned count, const void* addresses[], dyld_image_uuid_offset infos[])
 {
-    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;
+    log_apis("_dyld_images_for_addresses(%u, %p, %p)\n", count, addresses, infos);
+
+    // in stack crawls, common for contiguous fames to be in same image, so cache
+    // last lookup and check if next addresss in in there before doing full search
+    const MachOLoaded*    ml       = nullptr;
+    uint64_t              textSize = 0;
+    const void*           end      = (void*)ml;
+    for (unsigned i=0; i < count; ++i) {
+        const void* addr = stripPointer(addresses[i]);
+        bzero(&infos[i], sizeof(dyld_image_uuid_offset));
+        if ( (ml == nullptr) || (addr < (void*)ml) || (addr > end) ) {
+            if ( gAllImages.infoForImageMappedAt(addr, &ml, &textSize, nullptr) ) {
+                end = (void*)((uint8_t*)ml + textSize);
             }
-            else if ( strcmp(sectName, "__unwind_info") == 0 ) {
-                info->compact_unwind_section         = content;
-                info->compact_unwind_section_length  = sectSize;
+            else {
+                ml       = nullptr;
+                textSize = 0;
             }
         }
-    });
+        if ( ml != nullptr ) {
+            infos[i].image         = ml;
+            infos[i].offsetInImage = (uintptr_t)addr - (uintptr_t)ml;
+            ml->getUuid(infos[i].uuid);
+        }
+    }
+}
+
+void _dyld_register_for_image_loads(void (*func)(const mach_header* mh, const char* path, bool unloadable))
+{
+    gAllImages.addLoadNotifier(func);
+}
 
-    return true;
+bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info)
+{
+    log_apis("_dyld_find_unwind_sections(%p, %p)\n", addr, info);
+    addr = (void*)stripPointer(addr);
+
+    const MachOLoaded* ml = nullptr;
+    if ( gAllImages.infoForImageMappedAt(addr, &ml, nullptr, nullptr) ) {
+        info->mh                            = ml;
+        info->dwarf_section                 = nullptr;
+        info->dwarf_section_length          = 0;
+        info->compact_unwind_section        = nullptr;
+        info->compact_unwind_section_length = 0;
+
+        uint64_t size;
+        if ( const void* content = ml->findSectionContent("__TEXT", "__eh_frame", size) ) {
+            info->dwarf_section                 = content;
+            info->dwarf_section_length          = (uintptr_t)size;
+        }
+        if ( const void* content = ml->findSectionContent("__TEXT", "__unwind_info", size) ) {
+            info->compact_unwind_section        = content;
+            info->compact_unwind_section_length = (uintptr_t)size;
+        }
+        return true;
+    }
+
+    return false;
 }
 
 
 bool dyld_process_is_restricted()
 {
     log_apis("dyld_process_is_restricted()\n");
-
-    launch_cache::Closure closure(gAllImages.mainClosure());
-    return closure.isRestricted();
+    return gAllImages.isRestricted();
 }
 
 
@@ -1443,7 +1246,7 @@ int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extr
     });
 
     // iterate all images
-    sharedCache->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName) {
+    sharedCache->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName, bool& stop) {
         dyld_shared_cache_dylib_text_info dylibTextInfo;
         dylibTextInfo.version              = 2;
         dylibTextInfo.loadAddressUnslid    = loadAddressUnslid;
index 544f0902212b3d66a7512984397c06837c4ca0f5..37e627e2effe1a77528a21a98a661fe1b1c0a249 100644 (file)
@@ -28,6 +28,8 @@
 
 #include <string.h>
 #include <stdint.h>
+#include <pthread.h>
+#include <uuid/uuid.h>
 
 #include "dlfcn.h"
 #include "dyld_priv.h"
 
 #define TEMP_HIDDEN __attribute__((visibility("hidden")))
 
+//
+// The implementation of all dyld load/unload API's must hold a global lock
+// so that the next load/unload does start until the current is complete.
+// This lock is recursive so that initializers can call dlopen().
+// This is done using the macros DYLD_LOCK_THIS_BLOCK.
+// Example:
+//
+//  void dyld_load_api() {
+//        DYLD_LOAD_LOCK_THIS_BLOCK;
+//        // free to do stuff here
+//        // that accesses dyld internal data structures
+//    }
+//
+//
+
+#define DYLD_LOAD_LOCK_THIS_BLOCK            RecursiveAutoLock _dyld_load_lock;
+
 namespace dyld3 {
 
+class __attribute__((visibility("hidden"))) RecursiveAutoLock
+{
+public:
+    RecursiveAutoLock() {
+        pthread_mutex_lock(&_sMutex);
+    }
+    ~RecursiveAutoLock() {
+        pthread_mutex_unlock(&_sMutex);
+    }
+private:
+    static pthread_mutex_t _sMutex;
+};
+
+
+
 
 uint32_t _dyld_image_count() TEMP_HIDDEN;
 
@@ -52,17 +86,11 @@ 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;
@@ -73,6 +101,14 @@ uint32_t dyld_get_min_os_version(const mach_header* mh) TEMP_HIDDEN;
 
 uint32_t dyld_get_program_min_os_version() TEMP_HIDDEN;
 
+dyld_platform_t dyld_get_active_platform(void) TEMP_HIDDEN;
+dyld_platform_t dyld_get_base_platform(dyld_platform_t platform) TEMP_HIDDEN;
+bool dyld_is_simulator_platform(dyld_platform_t platform) TEMP_HIDDEN;
+bool dyld_sdk_at_least(const struct mach_header* mh, dyld_build_version_t version) TEMP_HIDDEN;
+bool dyld_minos_at_least(const struct mach_header* mh, dyld_build_version_t version) TEMP_HIDDEN;
+bool dyld_program_sdk_at_least(dyld_build_version_t version) TEMP_HIDDEN;
+bool dyld_program_minos_at_least(dyld_build_version_t version) TEMP_HIDDEN;
+void dyld_get_image_versions(const struct mach_header* mh, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version)) TEMP_HIDDEN;
 
 bool _dyld_get_image_uuid(const mach_header* mh, uuid_t uuid) TEMP_HIDDEN;
 
@@ -103,11 +139,11 @@ char* dlerror() TEMP_HIDDEN;
 
 int dlclose(void* handle) TEMP_HIDDEN;
 
-void* dlopen(const char* path, int mode) TEMP_HIDDEN;
+void* dlopen_internal(const char* path, int mode, void* callerAddress) TEMP_HIDDEN;
 
-bool dlopen_preflight(const char* path) TEMP_HIDDEN;
+bool dlopen_preflight_internal(const char* path) TEMP_HIDDEN;
 
-void* dlsym(void* handle, const char* symbolName) TEMP_HIDDEN;
+void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress) TEMP_HIDDEN;
 
 const struct dyld_all_image_infos* _dyld_get_all_image_infos() TEMP_HIDDEN;
 
@@ -117,6 +153,10 @@ bool _dyld_get_shared_cache_uuid(uuid_t uuid) TEMP_HIDDEN;
 
 const void* _dyld_get_shared_cache_range(size_t* length) TEMP_HIDDEN;
 
+void _dyld_images_for_addresses(unsigned count, const void* addresses[], struct dyld_image_uuid_offset infos[]) TEMP_HIDDEN;
+
+void _dyld_register_for_image_loads(void (*func)(const mach_header* mh, const char* path, bool unloadable)) TEMP_HIDDEN;
+
 bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) TEMP_HIDDEN;
 
 bool dyld_process_is_restricted() TEMP_HIDDEN;
index 41fdff368fa324b9c1ee17694125464bb6b553f1..773a42469af5d7e14af05a5547afd14ac8035a2e 100644 (file)
@@ -40,7 +40,6 @@
 #include "dyld_priv.h"
 
 #include "AllImages.h"
-#include "MachOParser.h"
 #include "Loading.h"
 #include "Logging.h"
 #include "Diagnostics.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);
+void                    parseDlHandle(void* h, const MachOLoaded** mh, bool* dontContinue);
 
 
 // only in macOS and deprecated 
@@ -79,15 +73,15 @@ NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* path, NS
         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;
+    OFIInfo result;
+    result.path        = strdup(path);
+    result.memSource   = nullptr;
+    result.memLength   = 0;
+    result.loadAddress = nullptr;
+    result.imageNum    = 0;
+    *ofi = gAllImages.addNSObjectFileImage(result);
 
-    log_apis("NSCreateObjectFileImageFromFile() => %p\n", result);
+    log_apis("NSCreateObjectFileImageFromFile() => %p\n", *ofi);
 
     return NSObjectFileImageSuccess;
 }
@@ -98,150 +92,164 @@ NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void* memIma
 
     // 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;
+
+       // check if it is current arch mach-o or fat with slice for current arch
+    bool usable = false;
+    const MachOFile* mf = (MachOFile*)memImage;
+    if ( mf->hasMachOMagic() && mf->isMachO(diag, memImageSize) ) {
+        if ( strcmp(mf->archName(), MachOFile::currentArchName()) == 0 )
+            usable = true;
+#if __x86_64__
+        // <rdar://problem/42727628> support thin x86_64 on haswell machines
+        else if ( (strcmp(MachOFile::currentArchName(), "x86_64h") == 0) && (strcmp(mf->archName(), "x86_64") == 0) )
+            usable = true;
+#endif
     }
-    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;
+    else if ( const FatFile* ff = FatFile::isFatFile(memImage) ) {
+        uint64_t sliceOffset;
+        uint64_t sliceLen;
+        bool     missingSlice;
+        if ( ff->isFatFileWithSlice(diag, memImageSize, MachOFile::currentArchName(), sliceOffset, sliceLen, missingSlice) ) {
+            mf = (MachOFile*)((long)memImage+sliceOffset);
+            if ( mf->isMachO(diag, sliceLen) ) {
+                usable = true;
             }
-        });
+        }
+    }
+    if ( usable ) {
+        if ( !mf->supportsPlatform(Platform::macOS) )
+            usable = false;
     }
-    if ( foundMH == nullptr ) {
+    if ( !usable ) {
         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);
+    if ( !mf->isBundle() ) {
+        log_apis("NSCreateObjectFileImageFromMemory() not a bundle\n");
         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;
+    OFIInfo result;
+    result.path        = nullptr;
+    result.memSource   = memImage;
+    result.memLength   = memImageSize;
+    result.loadAddress = nullptr;
+    result.imageNum    = 0;
+    *ofi = gAllImages.addNSObjectFileImage(result);
 
-    log_apis("NSCreateObjectFileImageFromMemory() => %p\n", result);
+    log_apis("NSCreateObjectFileImageFromMemory() => %p\n", *ofi);
 
     return NSObjectFileImageSuccess;
 }
 
 NSModule NSLinkModule(NSObjectFileImage ofi, const char* moduleName, uint32_t options)
 {
+    DYLD_LOAD_LOCK_THIS_BLOCK
     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);
+    __block const char* path = nullptr;
+    bool foundImage = gAllImages.forNSObjectFileImage(ofi, ^(OFIInfo &image) {
+        // if this is memory based image, write to temp file, then use file based loading
+        if ( image.memSource != nullptr ) {
+            // make temp file with content of memory buffer
+            bool successfullyWritten = false;
+            image.path = ::tempnam(nullptr, "NSCreateObjectFileImageFromMemory-");
+            if ( image.path != nullptr ) {
+                int fd = ::open(image.path, O_WRONLY | O_CREAT | O_EXCL, 0644);
+                if ( fd != -1 ) {
+                    ssize_t writtenSize = ::pwrite(fd, image.memSource, image.memLength, 0);
+                    if ( writtenSize == image.memLength )
+                        successfullyWritten = true;
+                    ::close(fd);
+                }
             }
-        }
-        if ( !successfullyWritten ) {
-            if ( ofi->path != nullptr ) {
-                free((void*)ofi->path);
-                ofi->path = nullptr;
+            if ( !successfullyWritten ) {
+                if ( image.path != nullptr ) {
+                    free((void*)image.path);
+                    image.path = nullptr;
+                }
+                log_apis("NSLinkModule() => NULL (could not save memory image to temp file)\n");
+                return;
             }
-            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
-    }
+        path = image.path;
+    });
 
-    // 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]);
-        }
+    if (!foundImage) {
+        // ofi is invalid if not in list
+        log_apis("NSLinkModule() => NULL (invalid NSObjectFileImage)\n");
+        return nullptr;
     }
 
-    // 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);
+    if (!path)
+        return nullptr;
+
+    // dlopen the binary outside of the read lock as we don't want to risk deadlock
+    Diagnostics diag;
+    void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
+    const MachOLoaded* loadAddress = gAllImages.dlopen(diag, path, false, false, false, true, callerAddress);
+    if ( diag.hasError() ) {
+        log_apis("   NSLinkModule: failed: %s\n", diag.errorMessage());
+        return nullptr;
     }
 
-    log_apis("NSLinkModule() => %p\n", ofi->loadAddress);
-    return (NSModule)ofi->loadAddress;
+    // Now update the load address of this object
+    gAllImages.forNSObjectFileImage(ofi, ^(OFIInfo &image) {
+        image.loadAddress = loadAddress;
+
+        // if memory based load, delete temp file
+        if ( image.memSource != nullptr ) {
+            log_apis("   NSLinkModule: delete temp file: %s\n", image.path);
+            ::unlink(image.path);
+        }
+    });
+
+    log_apis("NSLinkModule() => %p\n", loadAddress);
+    return (NSModule)loadAddress;
 }
 
 // NSUnLinkModule unmaps the image, but does not release the NSObjectFileImage
 bool NSUnLinkModule(NSModule module, uint32_t options)
 {
+    DYLD_LOAD_LOCK_THIS_BLOCK
     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;
-    }
+    __block const mach_header* mh = nullptr;
+    gAllImages.infoForImageMappedAt(module, ^(const LoadedImage& foundImage, uint8_t permissions) {
+        mh = foundImage.loadedAddress();
+    });
 
-    log_apis("NSUnLinkModule() => %d\n", result);
+    if ( mh != nullptr )
+        gAllImages.decRefCount(mh); // removes image if reference count went to zero
 
-    return result;
+    log_apis("NSUnLinkModule() => %d\n", mh != nullptr);
+
+    return mh != nullptr;
 }
 
 // NSDestroyObjectFileImage releases the NSObjectFileImage, but the mapped image may remain in use
-bool NSDestroyObjectFileImage(NSObjectFileImage ofi)
-{
-    log_apis("NSDestroyObjectFileImage(%p)\n", ofi);
+bool NSDestroyObjectFileImage(NSObjectFileImage imageHandle)
+{
+    log_apis("NSDestroyObjectFileImage(%p)\n", imageHandle);
+
+    __block const void* memSource = nullptr;
+    __block size_t      memLength = 0;
+    __block const char* path      = nullptr;
+    bool foundImage = gAllImages.forNSObjectFileImage(imageHandle, ^(OFIInfo &image) {
+        // keep copy of info
+        memSource = image.memSource;
+        memLength = image.memLength;
+        path      = image.path;
+    });
 
-    // ofi is invalid if not in list
-    if ( !gAllImages.hasNSObjectFileImage(ofi) )
+    if (!foundImage)
         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);
+    gAllImages.removeNSObjectFileImage(imageHandle);
 
     // 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
@@ -277,79 +285,76 @@ const char* NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileI
     halt("NSSymbolReferenceNameInObjectFileImage() is obsolete");
 }
 
-bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage ofi, const char* symbolName)
+bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage imageHandle, const char* symbolName)
 {
-    log_apis("NSIsSymbolDefinedInObjectFileImage(%p, %s)\n", ofi, symbolName);
+    log_apis("NSIsSymbolDefinedInObjectFileImage(%p, %s)\n", imageHandle, symbolName);
+
+    __block bool hasSymbol = false;
+    bool foundImage = gAllImages.forNSObjectFileImage(imageHandle, ^(OFIInfo &image) {
+        void* addr;
+        bool resultPointsToInstructions = false;
+        hasSymbol = image.loadAddress->hasExportedSymbol(symbolName, nullptr, &addr, &resultPointsToInstructions);
+    });
 
     // ofi is invalid if not in list
-    if ( !gAllImages.hasNSObjectFileImage(ofi) )
+    if (!foundImage)
         return false;
 
-    void* addr;
-    MachOParser parser(ofi->loadAddress);
-    return parser.hasExportedSymbol(symbolName, ^(uint32_t , const char*, void*, const mach_header**, void**) {
-        return false;
-    }, &addr);
+    return hasSymbol;
 }
 
-void* NSGetSectionDataInObjectFileImage(NSObjectFileImage ofi, const char* segmentName, const char* sectionName, size_t* size)
+void* NSGetSectionDataInObjectFileImage(NSObjectFileImage imageHandle, const char* segmentName, const char* sectionName, size_t* size)
 {
+    __block const void* result = nullptr;
+    bool foundImage = gAllImages.forNSObjectFileImage(imageHandle, ^(OFIInfo &image) {
+        uint64_t sz;
+        result = image.loadAddress->findSectionContent(segmentName, sectionName, sz);
+        *size = (size_t)sz;
+    });
+
     // ofi is invalid if not in list
-    if ( !gAllImages.hasNSObjectFileImage(ofi) )
+    if (!foundImage)
         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;
+       return (void*)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;
+    __block const char* result = nullptr;
+    gAllImages.infoForImageMappedAt(m, ^(const LoadedImage& foundImage, uint8_t permissions) {
+        result = gAllImages.imagePath(foundImage.image());
+    });
+
+    return result;
 }
 
 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;
-}
+     __block const char* result = nullptr;
+    gAllImages.infoForImageMappedAt(m, ^(const LoadedImage& foundImage, uint8_t permissions) {
+        result = gAllImages.imagePath(foundImage.image());
+    });
+    return result;
+ }
 
 
 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;
-            }
+    __block bool result = false;
+    gAllImages.forEachImage(^(const LoadedImage& loadedImage, bool& stop) {
+        bool resultPointsToInstructions = false;
+        if ( loadedImage.loadedAddress()->hasExportedSymbol(symbolName, nullptr, symbolAddress, &resultPointsToInstructions) ) {
+            *foundInImageAtLoadAddress = loadedImage.loadedAddress();
+            stop = true;
+            result = true;
         }
-    }
-    return false;
+    });
+    return result;
 }
 
 bool NSIsSymbolNameDefined(const char* symbolName)
@@ -374,14 +379,9 @@ bool NSIsSymbolNameDefinedInImage(const struct mach_header* mh, const char* symb
 {
     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);
+    void* addr;
+    bool resultPointsToInstructions = false;
+    return ((MachOLoaded*)mh)->hasExportedSymbol(symbolName, nullptr, &addr, &resultPointsToInstructions);
 }
 
 NSSymbol NSLookupAndBindSymbol(const char* symbolName)
@@ -412,43 +412,30 @@ 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;
-        }
+    const MachOLoaded* mh = (const MachOLoaded*)module;
+    void* addr;
+    bool resultPointsToInstructions = false;
+    if ( mh->hasExportedSymbol(symbolName, nullptr, &addr, &resultPointsToInstructions) ) {
+        return (NSSymbol)addr;
     }
     return nullptr;
 }
 
-NSSymbol NSLookupSymbolInImage(const struct mach_header* mh, const char* symbolName, uint32_t options)
+NSSymbol NSLookupSymbolInImage(const 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;
-    }
-
+    void* addr;
+    bool resultPointsToInstructions = false;
+       if ( ((MachOLoaded*)mh)->hasExportedSymbol(symbolName, nullptr, &addr, &resultPointsToInstructions) ) {
+        log_apis("   NSLookupSymbolInImage() => %p\n", addr);
+        return (NSSymbol)addr;
+       }
     if ( options & NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR ) {
         log_apis("   NSLookupSymbolInImage() => NULL\n");
         return nullptr;
     }
+    // FIXME: abort();
     return nullptr;
 }
 
@@ -469,12 +456,12 @@ 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;
+    __block NSModule result = nullptr;
+    gAllImages.infoForImageMappedAt(symbol, ^(const LoadedImage& foundImage, uint8_t permissions) {
+        result = (NSModule)foundImage.loadedAddress();
+    });
+
+    return result;
 }
 
 void NSLinkEditError(NSLinkEditErrors *c, int *errorNumber, const char** fileName, const char** errorString)
@@ -490,14 +477,16 @@ bool NSAddLibrary(const char* pathName)
 {
     log_apis("NSAddLibrary(%s)\n", pathName);
 
-    return ( dlopen(pathName, 0) != nullptr);
+    void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
+    return ( dlopen_internal(pathName, 0, callerAddress) != nullptr);
 }
 
 bool NSAddLibraryWithSearching(const char* pathName)
 {
     log_apis("NSAddLibraryWithSearching(%s)\n", pathName);
 
-    return ( dlopen(pathName, 0) != nullptr);
+    void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
+    return ( dlopen_internal(pathName, 0, callerAddress) != nullptr);
 }
 
 const mach_header* NSAddImage(const char* imageName, uint32_t options)
@@ -509,9 +498,10 @@ const mach_header* NSAddImage(const char* imageName, uint32_t options)
     if ( (options & NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED) != 0 )
         dloptions |= RTLD_NOLOAD;
 
-    void* h = dlopen(imageName, dloptions);
+    void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
+    void* h = dlopen_internal(imageName, dloptions, callerAddress);
     if ( h != nullptr ) {
-        const mach_header* mh;
+        const MachOLoaded* mh;
         bool dontContinue;
         parseDlHandle(h, &mh, &dontContinue);
         return mh;
index a410456994d70997448a6c071688f9f50adb1c5a..5a696a71a02de46877a988cc5672c510991c8499 100644 (file)
 
 
 #include <stdint.h>
+#include <fcntl.h>
+#include <sys/types.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"
+#include "Closure.h"
+#include "ClosureBuilder.h"
+#include "ClosureFileSystemPhysical.h"
 
 extern const char** appleParams;
 
@@ -57,564 +58,247 @@ 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());
-}
 
 
+/////////////////////  AllImages ////////////////////////////
 
-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
+AllImages gAllImages;
 
 
-/////////////////////  ChunkedVector ////////////////////////////
 
-template <typename T, int C>
-ChunkedVector<T,C>* ChunkedVector<T,C>::make(uint32_t count)
+void AllImages::init(const closure::LaunchClosure* closure, const DyldSharedCache* dyldCacheLoadAddress, const char* dyldCachePath,
+                     const Array<LoadedImage>& initialImages)
 {
-    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;
-    }
-}
+    _mainClosure        = closure;
+    _initialImages      = &initialImages;
+    _dyldCacheAddress   = dyldCacheLoadAddress;
+    _dyldCachePath      = dyldCachePath;
 
-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;
+    if ( _dyldCacheAddress ) {
+        const dyld_cache_mapping_info* const fileMappings = (dyld_cache_mapping_info*)((uint64_t)_dyldCacheAddress + _dyldCacheAddress->header.mappingOffset);
+        _dyldCacheSlide = (uint64_t)dyldCacheLoadAddress - fileMappings[0].address;
+        _imagesArrays.push_back(dyldCacheLoadAddress->cachedDylibsImageArray());
+        if ( auto others = dyldCacheLoadAddress->otherOSImageArray() )
+            _imagesArrays.push_back(others);
     }
-}
-
-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;
-}
+    _imagesArrays.push_back(_mainClosure->images());
 
-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);
+    // record first ImageNum to do use for dlopen() calls
+    _mainClosure->images()->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
+        closure::ImageNum num = image->imageNum();
+        if ( num >= _nextImageNum )
+            _nextImageNum = num+1;
+    });
+    // Make temporary old image array, so libSystem initializers can be debugged
+    STACK_ALLOC_ARRAY(dyld_image_info, oldDyldInfo, initialImages.count());
+    for (const LoadedImage& li : initialImages) {
+        oldDyldInfo.push_back({li.loadedAddress(), li.image()->path(), 0});
     }
-    _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);
-}
+    _oldAllImageInfos->infoArray        = &oldDyldInfo[0];
+    _oldAllImageInfos->infoArrayCount   = (uint32_t)oldDyldInfo.count();
+    _oldAllImageInfos->notification(dyld_image_adding, _oldAllImageInfos->infoArrayCount, _oldAllImageInfos->infoArray);
+    _oldAllImageInfos->infoArray        = nullptr;
+    _oldAllImageInfos->infoArrayCount   = 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);
+    _processDOFs = Loader::dtraceUserProbesEnabled();
 }
 
-template <typename T, int C>
-void ReaderWriterChunkedVector<T,C>::acquireWriteLock()
+void AllImages::setProgramVars(ProgramVars* vars)
 {
-    assert(pthread_rwlock_wrlock(&_lock) == 0);
+    _programVars = vars;
+    const dyld3::MachOFile* mf = (dyld3::MachOFile*)_programVars->mh;
+    mf->forEachSupportedPlatform(^(dyld3::Platform platform, uint32_t minOS, uint32_t sdk) {
+        _platform = (dyld_platform_t)platform;
+        //FIXME assert there is only one?
+    });
 }
 
-template <typename T, int C>
-void ReaderWriterChunkedVector<T,C>::releaseWriteLock()
+void AllImages::setRestrictions(bool allowAtPaths, bool allowEnvPaths)
 {
-    assert(pthread_rwlock_unlock(&_lock) == 0);
+    _allowAtPaths  = allowAtPaths;
+    _allowEnvPaths = allowEnvPaths;
 }
 
-template <typename T, int C>
-uint32_t ReaderWriterChunkedVector<T,C>::count() const
+void AllImages::applyInitialImages()
 {
-    __block uint32_t result = 0;
-    withReadLock(^() {
-        for (const ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
-            result += chunk->count();
-        }
-    });
-    return result;
+    addImages(*_initialImages);
+    runImageNotifiers(*_initialImages);
+    _initialImages = nullptr;  // this was stack allocated
 }
 
-template <typename T, int C>
-uint32_t ReaderWriterChunkedVector<T,C>::countNoLock() const
+void AllImages::withReadLock(void (^work)()) const
 {
-    uint32_t result = 0;
-    for (const ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
-        result += chunk->count();
-    }
-    return result;
+#ifdef OS_UNFAIR_RECURSIVE_LOCK_INIT
+     os_unfair_recursive_lock_lock(&_loadImagesLock);
+        work();
+     os_unfair_recursive_lock_unlock(&_loadImagesLock);
+#else
+    pthread_mutex_lock(&_loadImagesLock);
+        work();
+    pthread_mutex_unlock(&_loadImagesLock);
+#endif
 }
 
-template <typename T, int C>
-T* ReaderWriterChunkedVector<T,C>::addNoLock(uint32_t count, const T values[])
+void AllImages::withWriteLock(void (^work)())
 {
-    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;
+#ifdef OS_UNFAIR_RECURSIVE_LOCK_INIT
+     os_unfair_recursive_lock_lock(&_loadImagesLock);
+        work();
+     os_unfair_recursive_lock_unlock(&_loadImagesLock);
+#else
+    pthread_mutex_lock(&_loadImagesLock);
+        work();
+    pthread_mutex_unlock(&_loadImagesLock);
+#endif
 }
 
-template <typename T, int C>
-T* ReaderWriterChunkedVector<T,C>::add(uint32_t count, const T values[])
+void AllImages::withNotifiersLock(void (^work)()) const
 {
-    __block T* result = nullptr;
-    withWriteLock(^() {
-        result = addNoLock(count, values);
-    });
-    return result;
+#ifdef OS_UNFAIR_RECURSIVE_LOCK_INIT
+     os_unfair_recursive_lock_lock(&_notifiersLock);
+        work();
+     os_unfair_recursive_lock_unlock(&_notifiersLock);
+#else
+    pthread_mutex_lock(&_notifiersLock);
+        work();
+    pthread_mutex_unlock(&_notifiersLock);
+#endif
 }
 
-template <typename T, int C>
-void ReaderWriterChunkedVector<T,C>::remove(const T& valueToRemove)
+void AllImages::mirrorToOldAllImageInfos()
 {
-    __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;
-                }
+     withReadLock(^(){
+        // set infoArray to NULL to denote it is in-use
+        _oldAllImageInfos->infoArray = nullptr;
+
+        // if array not large enough, re-alloc it
+        uint32_t imageCount = (uint32_t)_loadedImages.count();
+        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;
         }
-    });
-}
 
-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;
+        // fill out array to mirror current image list
+        int index = 0;
+        for (const LoadedImage& li : _loadedImages) {
+            _oldAllImageArray[index].imageLoadAddress = li.loadedAddress();
+            _oldAllImageArray[index].imageFilePath    = imagePath(li.image());
+            _oldAllImageArray[index].imageFileModDate = 0;
+            ++index;
         }
-    });
-}
 
-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;
-    }
-}
+        // 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;
 
-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;
-
-    if ( _dyldCacheAddress ) {
-        const DyldSharedCache* cache = (DyldSharedCache*)_dyldCacheAddress;
-        const dyld_cache_mapping_info* const fileMappings = (dyld_cache_mapping_info*)((uint64_t)_dyldCacheAddress + cache->header.mappingOffset);
-        _dyldCacheSlide     = (uint64_t)dyldCacheLoadAddress - fileMappings[0].address;
-    }
-
-    // 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()
+void AllImages::addImages(const Array<LoadedImage>& newImages)
 {
-   // 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);
+    // copy into _loadedImages
+    withWriteLock(^(){
+        _loadedImages.append(newImages);
+        // if any image not in the shared cache added, recompute bounds
+        for (const LoadedImage& li : newImages) {
+            if ( !((MachOAnalyzer*)li.loadedAddress())->inDyldCache() ) {
+                recomputeBounds();
+                break;
+            }
         }
-        _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)
+void AllImages::runImageNotifiers(const Array<LoadedImage>& 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();
-            });
-        }
+        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);
+        for (uint32_t i=0; i < count; ++i) {
+            oldDyldInfo[i].imageLoadAddress = newImages[i].loadedAddress();
+            oldDyldInfo[i].imageFilePath    = imagePath(newImages[i].image());
             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));
+    for (const LoadedImage& li : newImages) {
+        log_loads("dyld: %s\n", imagePath(li.image()));
     }
 
 #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 ) {
+        for (const LoadedImage& li : newImages) {
+            const closure::Image* image = li.image();
+            struct stat  stat_buf;
+            fsid_t       fsid = {{ 0, 0 }};
+            fsobj_id_t   fsobjid = { 0, 0 };
+            if ( !image->inDyldCache() && (stat(imagePath(image), &stat_buf) == 0) ) {
                 fsobjid = *(fsobj_id_t*)&stat_buf.st_ino;
-                fsid = {{ stat_buf.st_dev, 0 }};
+                fsid    = {{ stat_buf.st_dev, 0 }};
             }
-            kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, img.uuid(), fsobjid, fsid, newImages[i].loadAddress);
+            uuid_t uuid;
+            image->getUuid(uuid);
+            kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, &uuid, fsobjid, fsid, li.loadedAddress());
         }
     }
 #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) {
-            log_notifications("dyld: add notifier %p called with mh=%p\n", func, newImages[i].loadAddress);
-            if (newImages[i].justUsedFromDyldCache) {
-                func(newImages[i].loadAddress, _dyldCacheSlide);
-            } else {
-                MachOParser parser(newImages[i].loadAddress);
-                func(newImages[i].loadAddress, parser.getSlide());
+    withNotifiersLock(^{
+        for (NotifyFunc func : _loadNotifiers) {
+            for (const LoadedImage& li : newImages) {
+                dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)li.loadedAddress(), (uint64_t)func, 0);
+                log_notifications("dyld: add notifier %p called with mh=%p\n", func, li.loadedAddress());
+                if ( li.image()->inDyldCache() )
+                    func(li.loadedAddress(), (uintptr_t)_dyldCacheSlide);
+                else
+                    func(li.loadedAddress(), li.loadedAddress()->getSlide());
             }
         }
-    }
+        for (LoadNotifyFunc func : _loadNotifiers2) {
+            for (const LoadedImage& li : newImages) {
+                dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)li.loadedAddress(), (uint64_t)func, 0);
+                log_notifications("dyld: add notifier %p called with mh=%p\n", func, li.loadedAddress());
+                if ( li.image()->inDyldCache() )
+                    func(li.loadedAddress(), li.image()->path(), false);
+                else
+                    func(li.loadedAddress(), li.image()->path(), !li.image()->neverUnload());
+            }
+        }
+    });
 
     // 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;
+        for (const LoadedImage& li : newImages) {
+            const closure::Image* image = li.image();
+            if ( image->hasObjC() ) {
+                pathsBuffer[imagesWithObjC] = imagePath(image);
+                mhBuffer[imagesWithObjC]    = li.loadedAddress();
                ++imagesWithObjC;
             }
         }
         if ( imagesWithObjC != 0 ) {
+            dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_MAP, 0, 0, 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) {
@@ -628,37 +312,28 @@ void AllImages::addImages(const launch_cache::DynArray<loader::ImageInfo>& newIm
     notifyMonitorLoads(newImages);
 }
 
-void AllImages::removeImages(const launch_cache::DynArray<loader::ImageInfo>& unloadImages)
+void AllImages::removeImages(const Array<LoadedImage>& 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());
+    withNotifiersLock(^{
+        for (NotifyFunc func : _unloadNotifiers) {
+            for (const LoadedImage& li : unloadImages) {
+                dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_REMOVE_IMAGE, (uint64_t)li.loadedAddress(), (uint64_t)func, 0);
+                log_notifications("dyld: remove notifier %p called with mh=%p\n", func, li.loadedAddress());
+                if ( li.image()->inDyldCache() )
+                    func(li.loadedAddress(), (uintptr_t)_dyldCacheSlide);
+                else
+                    func(li.loadedAddress(), li.loadedAddress()->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));
+        for (const LoadedImage& li : unloadImages) {
+            if ( li.image()->hasObjC() ) {
+                (*_objcNotifyUnmapped)(imagePath(li.image()), li.loadedAddress());
+                log_notifications("dyld: objc-unmapped-notifier called with image %p %s\n", li.loadedAddress(), imagePath(li.image()));
             }
         }
     }
@@ -666,185 +341,422 @@ void AllImages::removeImages(const launch_cache::DynArray<loader::ImageInfo>& un
 #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 ) {
+        for (const LoadedImage& li : unloadImages) {
+            const closure::Image* image = li.image();
+            struct stat  stat_buf;
+            fsid_t       fsid = {{ 0, 0 }};
+            fsobj_id_t   fsobjid = { 0, 0 };
+            if ( stat(imagePath(image), &stat_buf) == 0 ) {
                 fsobjid = *(fsobj_id_t*)&stat_buf.st_ino;
-                fsid = {{ stat_buf.st_dev, 0 }};
+                fsid    = {{ stat_buf.st_dev, 0 }};
             }
-            kdebug_trace_dyld_image(DBG_DYLD_UUID_UNMAP_A, img.uuid(), fsobjid, fsid, unloadImages[i].loadAddress);
+            uuid_t uuid;
+            image->getUuid(uuid);
+            kdebug_trace_dyld_image(DBG_DYLD_UUID_UNMAP_A, &uuid, fsobjid, fsid, li.loadedAddress());
         }
     }
 #endif
 
-    // remove each from sLoadedImages
-    for (uint32_t i=0; i < count; ++i) {
-        LoadedImage info(unloadImages[i].loadAddress, unloadImages[i].imageData);
-        sLoadedImages.remove(info);
-    }
+    // remove each from _loadedImages
+    withWriteLock(^(){
+        for (const LoadedImage& uli : unloadImages) {
+            for (LoadedImage& li : _loadedImages) {
+                if ( uli.loadedAddress() == li.loadedAddress() ) {
+                    _loadedImages.erase(li);
+                    break;
+                }
+            }
+        }
+        recomputeBounds();
+    });
 
     // sync to old all image infos struct
-    sLoadedImages.withReadLock(^{
-        mirrorToOldAllImageInfos();
-    });
+    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));
+    STACK_ALLOC_ARRAY(dyld_image_info, oldDyldInfo, unloadImages.count());
+    for (const LoadedImage& li : unloadImages) {
+        oldDyldInfo.push_back({li.loadedAddress(), li.image()->path(), 0});
     }
+    _oldAllImageInfos->notification(dyld_image_removing, (uint32_t)oldDyldInfo.count(), &oldDyldInfo[0]);
 
     // notify any processes tracking loads in this process
     notifyMonitorUnloads(unloadImages);
+
+    // finally, unmap images
+       for (const LoadedImage& li : unloadImages) {
+        if ( li.leaveMapped() ) {
+            log_loads("dyld: unloaded but left mmapped %s\n", imagePath(li.image()));
+        }
+        else {
+            // unmapImage() modifies parameter, so use copy
+            LoadedImage copy = li;
+            Loader::unmapImage(copy);
+            log_loads("dyld: unloaded %s\n", imagePath(li.image()));
+        }
+    }
+}
+
+// must be called with writeLock held
+void AllImages::recomputeBounds()
+{
+    _lowestNonCached  = UINTPTR_MAX;
+    _highestNonCached = 0;
+    for (const LoadedImage& li : _loadedImages) {
+        const MachOLoaded* ml = li.loadedAddress();
+        uintptr_t start = (uintptr_t)ml;
+        if ( !((MachOAnalyzer*)ml)->inDyldCache() ) {
+            if ( start < _lowestNonCached )
+                _lowestNonCached = start;
+            uintptr_t end = start + (uintptr_t)(li.image()->vmSizeToMap());
+            if ( end > _highestNonCached )
+                _highestNonCached = end;
+        }
+    }
+}
+
+uint32_t AllImages::count() const
+{
+    return (uint32_t)_loadedImages.count();
+}
+
+bool AllImages::dyldCacheHasPath(const char* path) const
+{
+    uint32_t dyldCacheImageIndex;
+    if ( _dyldCacheAddress != nullptr )
+        return _dyldCacheAddress->hasImagePath(path, dyldCacheImageIndex);
+    return false;
+}
+
+const char* AllImages::imagePathByIndex(uint32_t index) const
+{
+    if ( index < _loadedImages.count() )
+        return imagePath(_loadedImages[index].image());
+    return nullptr;
 }
 
-void AllImages::setNeverUnload(const loader::ImageInfo& existingImage)
+const mach_header* AllImages::imageLoadAddressByIndex(uint32_t index) const
 {
-    sLoadedImages.forEachWithWriteLock(^(uint32_t index, dyld3::LoadedImage &value, bool &stop) {
-        if (value.image() == existingImage.imageData) {
-            value.setNeverUnload();
-            stop = true;
+    if ( index < _loadedImages.count() )
+        return _loadedImages[index].loadedAddress();
+    return nullptr;
+}
+
+bool AllImages::findImage(const mach_header* loadAddress, LoadedImage& foundImage) const
+{
+    __block bool result = false;
+    withReadLock(^(){
+        for (const LoadedImage& li : _loadedImages) {
+            if ( li.loadedAddress() == loadAddress ) {
+                foundImage = li;
+                result = true;
+                break;
+            }
         }
     });
+    return result;
 }
 
-uint32_t AllImages::count() const
+void AllImages::forEachImage(void (^handler)(const LoadedImage& loadedImage, bool& stop)) const
 {
-    return sLoadedImages.count();
+    withReadLock(^{
+        bool stop = false;
+        for (const LoadedImage& li : _loadedImages) {
+           handler(li, stop);
+           if ( stop )
+               break;
+        }
+    });
 }
 
 
-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;
+const char* AllImages::pathForImageMappedAt(const void* addr) const
+{
+    if ( _initialImages != nullptr ) {
+        // being called during libSystem initialization, so _loadedImages not allocated yet
+        for (const LoadedImage& li : *_initialImages) {
+            uint8_t permissions;
+            if ( li.image()->containsAddress(addr, li.loadedAddress(), &permissions) ) {
+                return li.image()->path();
+            }
+        }
+        return nullptr;
+    }
+
+    // if address is in cache, do fast search of TEXT segments in cache
+    __block const char* result = nullptr;
+    if ( (_dyldCacheAddress != nullptr) && (addr > _dyldCacheAddress) ) {
+        if ( addr < (void*)((uint8_t*)_dyldCacheAddress+_dyldCacheAddress->mappedSize()) ) {
+            uint64_t cacheSlide        = (uint64_t)_dyldCacheAddress - _dyldCacheAddress->unslidLoadAddress();
+            uint64_t unslidTargetAddr  = (uint64_t)addr - cacheSlide;
+            _dyldCacheAddress->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const unsigned char* dylibUUID, const char* installName, bool& stop) {
+                if ( (loadAddressUnslid <= unslidTargetAddr) && (unslidTargetAddr < loadAddressUnslid+textSegmentSize) ) {
+                    result = installName;
+                    stop   = true;
+                }
+            });
+            if ( result != nullptr )
+                return result;
+        }
+    }
+
+    // slow path - search image list
+    infoForImageMappedAt(addr, ^(const LoadedImage& foundImage, uint8_t permissions) {
+        result = foundImage.image()->path();
+    });
+
+    return result;
+}
+
+void AllImages::infoForImageMappedAt(const void* addr, void (^handler)(const LoadedImage& foundImage, uint8_t permissions)) const
+{
+    __block uint8_t permissions;
+    if ( _initialImages != nullptr ) {
+        // being called during libSystem initialization, so _loadedImages not allocated yet
+        for (const LoadedImage& li : *_initialImages) {
+            if ( li.image()->containsAddress(addr, li.loadedAddress(), &permissions) ) {
+                handler(li, permissions);
+                break;
+            }
+        }
+        return;
+    }
+
+    withReadLock(^{
+        for (const LoadedImage& li : _loadedImages) {
+            if ( li.image()->containsAddress(addr, li.loadedAddress(), &permissions) ) {
+                handler(li, permissions);
+                break;
+            }
+        }
+    });
+}
+
+
+bool AllImages::infoForImageMappedAt(const void* addr, const MachOLoaded** ml, uint64_t* textSize, const char** path) const
+{
+    if ( _initialImages != nullptr ) {
+        // being called during libSystem initialization, so _loadedImages not allocated yet
+        for (const LoadedImage& li : *_initialImages) {
+            uint8_t permissions;
+            if ( li.image()->containsAddress(addr, li.loadedAddress(), &permissions) ) {
+                if ( ml != nullptr )
+                    *ml = li.loadedAddress();
+                if ( path != nullptr )
+                    *path = li.image()->path();
+                if ( textSize != nullptr ) {
+                    *textSize = li.image()->textSize();
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // if address is in cache, do fast search of TEXT segments in cache
+    __block bool result = false;
+    if ( (_dyldCacheAddress != nullptr) && (addr > _dyldCacheAddress) ) {
+        if ( addr < (void*)((uint8_t*)_dyldCacheAddress+_dyldCacheAddress->mappedSize()) ) {
+            uint64_t cacheSlide        = (uint64_t)_dyldCacheAddress - _dyldCacheAddress->unslidLoadAddress();
+            uint64_t unslidTargetAddr  = (uint64_t)addr - cacheSlide;
+            _dyldCacheAddress->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const unsigned char* dylibUUID, const char* installName, bool& stop) {
+                if ( (loadAddressUnslid <= unslidTargetAddr) && (unslidTargetAddr < loadAddressUnslid+textSegmentSize) ) {
+                    if ( ml != nullptr )
+                        *ml = (MachOLoaded*)(loadAddressUnslid + cacheSlide);
+                    if ( path != nullptr )
+                        *path = installName;
+                    if ( textSize != nullptr )
+                        *textSize = textSegmentSize;
+                    stop = true;
+                    result = true;
+                }
+            });
+            if ( result )
+                return result;
+        }
+    }
+
+    // slow path - search image list
+    infoForImageMappedAt(addr, ^(const LoadedImage& foundImage, uint8_t permissions) {
+        if ( ml != nullptr )
+            *ml = foundImage.loadedAddress();
+        if ( path != nullptr )
+            *path = foundImage.image()->path();
+        if ( textSize != nullptr )
+            *textSize = foundImage.image()->textSize();
+        result = true;
+    });
+
+    return result;
+}
+
+// same as infoForImageMappedAt(), but only look at images not in the dyld cache
+void AllImages::infoForNonCachedImageMappedAt(const void* addr, void (^handler)(const LoadedImage& foundImage, uint8_t permissions)) const
+{
+    __block uint8_t permissions;
+    if ( _initialImages != nullptr ) {
+        // being called during libSystem initialization, so _loadedImages not allocated yet
+        for (const LoadedImage& li : *_initialImages) {
+            if ( !((MachOAnalyzer*)li.loadedAddress())->inDyldCache() ) {
+                if (  li.image()->containsAddress(addr, li.loadedAddress(), &permissions) ) {
+                    handler(li, permissions);
+                    break;
+                }
+            }
+        }
+        return;
+    }
+
+    withReadLock(^{
+        for (const LoadedImage& li : _loadedImages) {
+            if ( !((MachOAnalyzer*)li.loadedAddress())->inDyldCache() ) {
+                if ( li.image()->containsAddress(addr, li.loadedAddress(), &permissions) ) {
+                    handler(li, permissions);
+                    break;
+                }
+            }
         }
     });
-    return launch_cache::Image(foundImage);
 }
 
-launch_cache::Image AllImages::findByLoadAddress(const mach_header* loadAddress) const
+bool AllImages::immutableMemory(const void* addr, size_t length) 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;
+    // quick check to see if in shared cache
+    if ( _dyldCacheAddress != nullptr ) {
+        bool readOnly;
+        if ( _dyldCacheAddress->inCache(addr, length, readOnly) ) {
+            return readOnly;
         }
-    });
-    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;
+    withReadLock(^() {
+        // quick check to see if it is not any non-cached image loaded
+        if ( ((uintptr_t)addr < _lowestNonCached) || ((uintptr_t)addr+length > _highestNonCached) ) {
+            result = false;
+            return;
+        }
+        // slow walk through all images, only look at images not in dyld cache
+        for (const LoadedImage& li : _loadedImages) {
+            if ( !((MachOAnalyzer*)li.loadedAddress())->inDyldCache() ) {
+                uint8_t permissions;
+                if ( li.image()->containsAddress(addr, li.loadedAddress(), &permissions) ) {
+                    result = ((permissions & VM_PROT_WRITE) == 0) && li.image()->neverUnload();
+                    break;
+                }
+            }
         }
     });
+
     return result;
 }
 
-void AllImages::forEachImage(void (^handler)(uint32_t imageIndex, const mach_header* loadAddress, const launch_cache::Image image, bool& stop)) const
+void AllImages::infoForImageWithLoadAddress(const MachOLoaded* mh, void (^handler)(const LoadedImage& foundImage)) const
 {
-       sLoadedImages.forEachWithReadLock(^(uint32_t imageIndex, const LoadedImage& loadedImage, bool& stop) {
-       handler(imageIndex, loadedImage.loadedAddress(), launch_cache::Image(loadedImage.image()), stop);
+    withReadLock(^{
+        for (const LoadedImage& li : _loadedImages) {
+            if ( li.loadedAddress() == mh ) {
+                handler(li);
+                break;
+            }
+        }
     });
 }
 
-launch_cache::Image AllImages::findByOwnedAddress(const void* addr, const mach_header** loadAddress, uint8_t* permissions) const
+bool AllImages::findImageNum(closure::ImageNum imageNum, LoadedImage& foundImage) 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;
+   if ( _initialImages != nullptr ) {
+        // being called during libSystem initialization, so _loadedImages not allocated yet
+        for (const LoadedImage& li : *_initialImages) {
+            if ( li.image()->representsImageNum(imageNum) ) {
+                foundImage = li;
+                return true;
             }
         }
-        return launch_cache::Image(nullptr);
+        return false;
     }
 
-    // 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;
-            }
+    bool result = false;
+    for (const LoadedImage& li : _loadedImages) {
+        if ( li.image()->representsImageNum(imageNum) ) {
+            foundImage = li;
+            result = true;
+            break;
         }
     }
 
-    __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 result;
+}
+
+const MachOLoaded* AllImages::findDependent(const MachOLoaded* mh, uint32_t depIndex)
+{
+    __block const MachOLoaded* result = nullptr;
+    withReadLock(^{
+        for (const LoadedImage& li : _loadedImages) {
+            if ( li.loadedAddress() == mh ) {
+                closure::ImageNum depImageNum = li.image()->dependentImageNum(depIndex);
+                LoadedImage depLi;
+                if ( findImageNum(depImageNum, depLi) )
+                    result = depLi.loadedAddress();
+                break;
+            }
         }
     });
-    return launch_cache::Image(foundImage);
+    return result;
 }
 
-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;
+
+void AllImages::breadthFirstRecurseDependents(Array<closure::ImageNum>& visited, const LoadedImage& nodeLi, bool& stopped, void (^handler)(const LoadedImage& aLoadedImage, bool& stop)) const
+{
+    // call handler on all direct dependents (unless already visited)
+    STACK_ALLOC_ARRAY(LoadedImage, dependentsToRecurse, 256);
+    nodeLi.image()->forEachDependentImage(^(uint32_t depIndex, closure::Image::LinkKind kind, closure::ImageNum depImageNum, bool& depStop) {
+        if ( kind == closure::Image::LinkKind::upward )
+            return;
+        if ( visited.contains(depImageNum) )
+            return;
+        LoadedImage depLi;
+        if ( !findImageNum(depImageNum, depLi) )
+            return;
+        handler(depLi, depStop);
+        visited.push_back(depImageNum);
+        if ( depStop ) {
+            stopped = true;
+            return;
         }
+        dependentsToRecurse.push_back(depLi);
+    });
+    if ( stopped )
+        return;
+    // recurse on all dependents just visited
+    for (LoadedImage& depLi : dependentsToRecurse) {
+       breadthFirstRecurseDependents(visited, depLi, stopped, handler);
+    }
+}
+
+void AllImages::visitDependentsTopDown(const LoadedImage& start, void (^handler)(const LoadedImage& aLoadedImage, bool& stop)) const
+{
+    withReadLock(^{
+        STACK_ALLOC_ARRAY(closure::ImageNum, visited, count());
+        bool stop = false;
+        handler(start, stop);
+        if ( stop )
+            return;
+        visited.push_back(start.image()->imageNum());
+        breadthFirstRecurseDependents(visited, start, stop, handler);
     });
-    return foundAddress;
 }
 
-const mach_header* AllImages::mainExecutable() const
+const MachOLoaded* AllImages::mainExecutable() const
 {
     assert(_programVars != nullptr);
-    return (const mach_header*)_programVars->mh;
+    return (const MachOLoaded*)_programVars->mh;
 }
 
-launch_cache::Image AllImages::mainExecutableImage() const
+const closure::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;
+    return _mainClosure->images()->imageForNum(_mainClosure->topImage());
 }
 
 void AllImages::setMainPath(const char* path )
@@ -852,197 +764,94 @@ void AllImages::setMainPath(const char* path )
     _mainExeOverridePath = path;
 }
 
-const char* AllImages::imagePath(const BinaryImage* binImage) const
+const char* AllImages::imagePath(const closure::Image* image) 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() )
+        if ( image == mainExecutableImage() )
             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;
-    });
+    return image->path();
 }
 
-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;
+dyld_platform_t AllImages::platform() const {
+    return _platform;
 }
 
 void AllImages::incRefCount(const mach_header* loadAddress)
 {
-    __block bool found = false;
-    sDlopenRefCounts.forEachWithWriteLock(^(uint32_t index, DlopenCount& entry, bool& stop) {
+    for (DlopenCount& entry : _dlopenRefCounts) {
         if ( entry.loadAddress == loadAddress ) {
-            found = true;
+            // found existing DlopenCount entry, bump counter
             entry.refCount += 1;
-            stop = true;
+            return;
         }
-    });
-    if ( !found ) {
-        DlopenCount newEnty = { loadAddress, 1 };
-        sDlopenRefCounts.add(newEnty);
     }
+
+    // no existing DlopenCount, add new one
+    _dlopenRefCounts.push_back({ loadAddress, 1 });
 }
 
 void AllImages::decRefCount(const mach_header* loadAddress)
 {
-    __block bool refCountNowZero = false;
-    sDlopenRefCounts.forEachWithWriteLock(^(uint32_t index, DlopenCount& entry, bool& stop) {
+    bool doCollect = false;
+    for (DlopenCount& entry : _dlopenRefCounts) {
         if ( entry.loadAddress == loadAddress ) {
+            // found existing DlopenCount entry, bump counter
             entry.refCount -= 1;
-            stop = true;
-            if ( entry.refCount == 0 )
-                refCountNowZero = true;
+            if ( entry.refCount == 0 ) {
+                _dlopenRefCounts.erase(entry);
+                doCollect = true;
+                break;
+            }
+            return;
         }
-    });
-    if ( refCountNowZero ) {
-        DlopenCount delEnty = { loadAddress, 0 };
-        sDlopenRefCounts.remove(delEnty);
-        garbageCollectImages();
     }
+    if ( doCollect )
+        garbageCollectImages();
 }
 
 
 #if __MAC_OS_X_VERSION_MIN_REQUIRED
-__NSObjectFileImage* AllImages::addNSObjectFileImage()
+NSObjectFileImage AllImages::addNSObjectFileImage(const OFIInfo& image)
 {
-    // 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;
-        }
+    __block uint64_t imageNum = 0;
+    withWriteLock(^{
+        imageNum = ++_nextObjectFileImageNum;
+        _objectFileImages.push_back(image);
+        _objectFileImages.back().imageNum = imageNum;
     });
-    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 (NSObjectFileImage)imageNum;
+}
+
+bool AllImages::forNSObjectFileImage(NSObjectFileImage imageHandle,
+                                     void (^handler)(OFIInfo& image)) {
+    uint64_t imageNum = (uint64_t)imageHandle;
+    bool __block foundImage = false;
+    withReadLock(^{
+        for (OFIInfo& ofi : _objectFileImages) {
+            if ( ofi.imageNum == imageNum ) {
+                handler(ofi);
+                foundImage = true;
+                return;
+            }
         }
     });
-    return result;
+
+    return foundImage;
 }
 
-void AllImages::removeNSObjectFileImage(__NSObjectFileImage* ofi)
+void AllImages::removeNSObjectFileImage(NSObjectFileImage imageHandle)
 {
-    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;
+    uint64_t imageNum = (uint64_t)imageHandle;
+    withWriteLock(^{
+        for (OFIInfo& ofi : _objectFileImages) {
+            if ( ofi.imageNum == imageNum ) {
+                _objectFileImages.erase(ofi);
+                return;
+            }
         }
     });
 }
@@ -1052,105 +861,82 @@ void AllImages::removeNSObjectFileImage(__NSObjectFileImage* ofi)
 class VIS_HIDDEN Reaper
 {
 public:
-                        Reaper(uint32_t count, const LoadedImage** unloadables, bool* inUseArray);
+    struct ImageAndUse
+    {
+        const LoadedImage*  li;
+        bool                inUse;
+    };
+                        Reaper(Array<ImageAndUse>& unloadables, AllImages*);
     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;
+    Array<ImageAndUse>& _unloadables;
+    AllImages*          _allImages;
     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)
+Reaper::Reaper(Array<ImageAndUse>& unloadables, AllImages* all)
+ : _unloadables(unloadables), _allImages(all), _deadCount(0)
 {
-    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;
+    for (AllImages::DlopenCount& entry : _allImages->_dlopenRefCounts) {
+        if ( entry.refCount != 0 ) {
+            for (ImageAndUse& iu : _unloadables) {
+                if ( iu.li->loadedAddress() == entry.loadAddress ) {
+                    iu.inUse = true;
+                    break;
+                }
             }
-         }
-    });
+        }
+       }
 }
 
 uint32_t Reaper::inUseCount()
 {
     uint32_t count = 0;
-    for (uint32_t i=0; i < _arrayCount; ++i) {
-        if ( _inUseArray[i] )
+       for (ImageAndUse& iu : _unloadables) {
+        if ( iu.inUse )
             ++count;
     }
     return count;
 }
 
-void Reaper::markDependentsOf(const LoadedImage* entry)
+void Reaper::markDependentsOf(const LoadedImage* li)
 {
-    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;
+    li->image()->forEachDependentImage(^(uint32_t depIndex, closure::Image::LinkKind kind, closure::ImageNum depImageNum, bool& stop) {
+        for (ImageAndUse& iu : _unloadables) {
+            if ( !iu.inUse && iu.li->image()->representsImageNum(depImageNum) ) {
+                iu.inUse = true;
+                break;
+            }
         }
     });
 }
 
 void Reaper::markDependentOfInUseImages()
 {
-    for (uint32_t i=0; i < _arrayCount; ++i) {
-        if ( _inUseArray[i] )
-            markDependentsOf(_unloadablesArray[i]);
+       for (ImageAndUse& iu : _unloadables) {
+        if ( iu.inUse )
+            markDependentsOf(iu.li);
     }
 }
 
 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());
-    }
+    //for (ImageAndUse& iu : _unloadables) {
+    //    log("  in-used=%d  %s\n", iu.inUse, iu.li->image()->path());
+    //}
 }
 
 void Reaper::garbageCollect()
@@ -1173,7 +959,7 @@ void Reaper::garbageCollect()
         lastCount = newCount;
     } while (countChanged);
 
-    _deadCount = _arrayCount - inUseCount();
+    _deadCount = (uint32_t)_unloadables.count() - inUseCount();
 }
 
 void Reaper::finalizeDeadImages()
@@ -1183,13 +969,12 @@ void Reaper::finalizeDeadImages()
     __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] )
+       for (ImageAndUse& iu : _unloadables) {
+        if ( iu.inUse )
             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) {
+        iu.li->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].addr   = (char*)(iu.li->loadedAddress()) + vmOffset;
                 rangesArray[rangesCount].length = (size_t)vmSize;
                 ++rangesCount;
            }
@@ -1210,7 +995,7 @@ void Reaper::finalizeDeadImages()
 // 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
+// Also note that this is done within the _loadedImages writer lock, so any dlopen/dlclose
 // on other threads are blocked while this garbage collections runs
 //
 void AllImages::garbageCollectImages()
@@ -1221,68 +1006,48 @@ void AllImages::garbageCollectImages()
         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;
+        STACK_ALLOC_ARRAY(Reaper::ImageAndUse, unloadables, _loadedImages.count());
+        withReadLock(^{
+            for (const LoadedImage& li : _loadedImages) {
+                if ( !li.image()->neverUnload() /*&& !li.neverUnload()*/ ) {
+                    unloadables.push_back({&li, false});
+                    //fprintf(stderr, "unloadable[%lu] %p %s\n", unloadables.count(), li.loadedAddress(), li.image()->path());
                 }
-            });
-            // make reaper object to do garbage collection and notifications
-            Reaper reaper(unloadableCount, unloadablesArray, unloadableInUseArray);
-            reaper.garbageCollect();
+            }
+        });
+        // make reaper object to do garbage collection and notifications
+        Reaper reaper(unloadables, this);
+        reaper.garbageCollect();
 
-            // FIXME: we should sort dead images so higher level ones are terminated first
+        // FIXME: we should sort dead images so higher level ones are terminated first
 
-            // call cxa_finalize_ranges of dead images
-            reaper.finalizeDeadImages();
+        // call cxa_finalize_ranges of dead images
+        reaper.finalizeDeadImages();
 
-            // FIXME: call static terminators of dead images
+        // FIXME: call static terminators of dead images
 
-            // FIXME: DOF unregister
-        });
+        // 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());
-        //});
+        //fprintf(stderr, "_loadedImages before GC removals:\n");
+        //for (const LoadedImage& li : _loadedImages) {
+        //    fprintf(stderr, "   loadAddr=%p, path=%s\n", li.loadedAddress(), li.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;
+        // because unloadables[] points into LoadedImage we are shrinking
+        STACK_ALLOC_ARRAY(LoadedImage, unloadImages, _loadedImages.count());
+        for (const Reaper::ImageAndUse& iu : unloadables) {
+            if ( !iu.inUse )
+                unloadImages.push_back(*iu.li);
         }
-        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
+        // remove entries from _loadedImages
+        if ( !unloadImages.empty() ) {
             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());
-            //});
+            //fprintf(stderr, "_loadedImages after GC removals:\n");
+            //for (const LoadedImage& li : _loadedImages) {
+            //   fprintf(stderr, "   loadAddr=%p, path=%s\n", li.loadedAddress(), li.image()->path());
+            //}
         }
 
         // if some other thread called GC during our work, redo GC on its behalf
@@ -1293,123 +1058,51 @@ void AllImages::garbageCollectImages()
 
 
 
-VIS_HIDDEN
-const launch_cache::binary_format::Image* AllImages::messageClosured(const char* path, const char* apiName, const char* closuredErrorMessages[3], int& closuredErrorMessagesCount)
+void AllImages::addLoadNotifier(NotifyFunc func)
 {
-    __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();
+    // callback about already loaded images
+    withReadLock(^{
+        for (const LoadedImage& li : _loadedImages) {
+            dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)li.loadedAddress(), (uint64_t)func, 0);
+            log_notifications("dyld: add notifier %p called with mh=%p\n", func, li.loadedAddress());
+            if ( li.image()->inDyldCache() )
+                func(li.loadedAddress(), (uintptr_t)_dyldCacheSlide);
+            else
+                func(li.loadedAddress(), li.loadedAddress()->getSlide());
         }
-        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;
-        }
+    // add to list of functions to call about future loads
+    withNotifiersLock(^{
+        _loadNotifiers.push_back(func);
     });
-    return result;
 }
 
-bool AllImages::imageUnloadable(const launch_cache::Image& image, const mach_header* loadAddress) const
+void AllImages::addUnloadNotifier(NotifyFunc func)
 {
-    // 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;
-        }
+    // add to list of functions to call about future unloads
+    withNotifiersLock(^{
+        _unloadNotifiers.push_back(func);
     });
-    if ( foundAsNeverUnload )
-        return false;
-
-    return true;
 }
 
-void AllImages::addLoadNotifier(NotifyFunc func)
+void AllImages::addLoadNotifier(LoadNotifyFunc 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();
+    withReadLock(^{
+        for (const LoadedImage& li : _loadedImages) {
+            dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)li.loadedAddress(), (uint64_t)func, 0);
+            log_notifications("dyld: add notifier %p called with mh=%p\n", func, li.loadedAddress());
+            func(li.loadedAddress(), li.image()->path(), !li.image()->neverUnload());
+        }
     });
-    // 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);
+    withNotifiersLock(^{
+        _loadNotifiers2.push_back(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)
 {
@@ -1418,150 +1111,357 @@ void AllImages::setObjCNotifiers(_dyld_objc_notify_mapped map, _dyld_objc_notify
     _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]);
+    uint32_t maxCount = count();
+    STACK_ALLOC_ARRAY(const mach_header*, mhs,   maxCount);
+    STACK_ALLOC_ARRAY(const char*,        paths, maxCount);
+    // don't need _mutex here because this is called when process is still single threaded
+    for (const LoadedImage& li : _loadedImages) {
+        if ( li.image()->hasObjC() ) {
+            paths.push_back(imagePath(li.image()));
+            mhs.push_back(li.loadedAddress());
+        }
+    }
+    if ( !mhs.empty() ) {
+        (*map)((uint32_t)mhs.count(), &paths[0], &mhs[0]);
+        if ( log_notifications("dyld: objc-mapped-notifier called with %ld images:\n", mhs.count()) ) {
+            for (uintptr_t i=0; i < mhs.count(); ++i) {
+                log_notifications("dyld:  objc-mapped: %p %s\n",  mhs[i], paths[i]);
             }
         }
     }
 }
 
-void AllImages::vmAccountingSetSuspended(bool suspend)
+void AllImages::applyInterposingToDyldCache(const closure::Closure* closure)
 {
-#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);
+    dyld3::ScopedTimer timer(DBG_DYLD_TIMING_APPLY_INTERPOSING, 0, 0, 0);
+    const uintptr_t                 cacheStart              = (uintptr_t)_dyldCacheAddress;
+    __block closure::ImageNum       lastCachedDylibImageNum = 0;
+    __block const closure::Image*   lastCachedDylibImage    = nullptr;
+    __block bool                    suspendedAccounting     = false;
+    closure->forEachPatchEntry(^(const closure::Closure::PatchEntry& entry) {
+        if ( entry.overriddenDylibInCache != lastCachedDylibImageNum ) {
+            lastCachedDylibImage    = closure::ImageArray::findImage(imagesArrays(), entry.overriddenDylibInCache);
+            assert(lastCachedDylibImage != nullptr);
+            lastCachedDylibImageNum = entry.overriddenDylibInCache;
+        }
+        if ( !suspendedAccounting ) {
+            Loader::vmAccountingSetSuspended(true, log_fixups);
+            suspendedAccounting = true;
+        }
+        uintptr_t newValue = 0;
+        LoadedImage foundImage;
+        switch ( entry.replacement.image.kind ) {
+            case closure::Image::ResolvedSymbolTarget::kindImage:
+                assert(findImageNum(entry.replacement.image.imageNum, foundImage));
+                newValue = (uintptr_t)(foundImage.loadedAddress()) + (uintptr_t)entry.replacement.image.offset;
+                break;
+            case closure::Image::ResolvedSymbolTarget::kindSharedCache:
+                newValue = (uintptr_t)_dyldCacheAddress + (uintptr_t)entry.replacement.sharedCache.offset;
+                break;
+            case closure::Image::ResolvedSymbolTarget::kindAbsolute:
+                // this means the symbol was missing in the cache override dylib, so set any uses to NULL
+                newValue = (uintptr_t)entry.replacement.absolute.value;
+                break;
+            default:
+                assert(0 && "bad replacement kind");
+        }
+        lastCachedDylibImage->forEachPatchableUseOfExport(entry.exportCacheOffset, ^(closure::Image::PatchableExport::PatchLocation patchLocation) {
+            uintptr_t* loc = (uintptr_t*)(cacheStart+patchLocation.cacheOffset);
+ #if __has_feature(ptrauth_calls)
+            if ( patchLocation.authenticated ) {
+                MachOLoaded::ChainedFixupPointerOnDisk fixupInfo;
+                fixupInfo.authRebase.auth      = true;
+                fixupInfo.authRebase.addrDiv   = patchLocation.usesAddressDiversity;
+                fixupInfo.authRebase.diversity = patchLocation.discriminator;
+                fixupInfo.authRebase.key       = patchLocation.key;
+                *loc = fixupInfo.signPointer(loc, newValue + patchLocation.getAddend());
+                log_fixups("dyld: cache fixup: *%p = %p (JOP: diversity 0x%04X, addr-div=%d, key=%s)\n",
+                           loc, (void*)*loc, patchLocation.discriminator, patchLocation.usesAddressDiversity, patchLocation.keyName());
+                return;
+            }
 #endif
+            log_fixups("dyld: cache fixup: *%p = 0x%0lX (dyld cache patch)\n", loc, newValue + (uintptr_t)patchLocation.getAddend());
+            *loc = newValue + (uintptr_t)patchLocation.getAddend();
+        });
+    });
+    if ( suspendedAccounting )
+        Loader::vmAccountingSetSuspended(false, log_fixups);
 }
 
-void AllImages::applyInterposingToDyldCache(const launch_cache::binary_format::Closure* closure, const dyld3::launch_cache::DynArray<loader::ImageInfo>& initialImages)
+void AllImages::runStartupInitialzers()
 {
-    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;
+    __block bool mainExecutableInitializerNeedsToRun = true;
+    __block uint32_t imageIndex = 0;
+    while ( mainExecutableInitializerNeedsToRun ) {
+        __block const closure::Image* image = nullptr;
+        withReadLock(^{
+            image = _loadedImages[imageIndex].image();
+            if ( _loadedImages[imageIndex].loadedAddress()->isMainExecutable() )
+                mainExecutableInitializerNeedsToRun = false;
+        });
+       runInitialzersBottomUp(image);
+        ++imageIndex;
+    }
+}
+
+
+// Find image in _loadedImages which has ImageNum == num.
+// Try indexHint first, if hint is wrong, updated it, so next use is faster.
+LoadedImage AllImages::findImageNum(closure::ImageNum num, uint32_t& indexHint)
+{
+    __block LoadedImage copy;
+    withReadLock(^{
+        if ( (indexHint >= _loadedImages.count()) || !_loadedImages[indexHint].image()->representsImageNum(num) ) {
+            indexHint = 0;
+            for (indexHint=0; indexHint < _loadedImages.count(); ++indexHint) {
+                if ( _loadedImages[indexHint].image()->representsImageNum(num) )
+                    break;
+            }
+            assert(indexHint < _loadedImages.count());
+        }
+        copy = _loadedImages[indexHint];
+    });
+    return copy;
+}
+
+
+// Change the state of the LoadedImage in _loadedImages which has ImageNum == num.
+// Only change state if current state is expectedCurrentState (atomic swap).
+bool AllImages::swapImageState(closure::ImageNum num, uint32_t& indexHint, LoadedImage::State expectedCurrentState, LoadedImage::State newState)
+{
+    __block bool result = false;
+    withWriteLock(^{
+        if ( (indexHint >= _loadedImages.count()) || !_loadedImages[indexHint].image()->representsImageNum(num) ) {
+            indexHint = 0;
+            for (indexHint=0; indexHint < _loadedImages.count(); ++indexHint) {
+                if ( _loadedImages[indexHint].image()->representsImageNum(num) )
+                    break;
             }
+            assert(indexHint < _loadedImages.count());
         }
-        if ( !foundInImages ) {
-            launch_cache::Image img(imageData);
-            log_fixups("did not find loaded image to patch into cache: %s\n", img.path());
+        if ( _loadedImages[indexHint].state() == expectedCurrentState ) {
+           _loadedImages[indexHint].setState(newState);
+            result = true;
         }
     });
-    if ( suspendedAccounting )
-        vmAccountingSetSuspended(false);
+    return result;
+}
+
+// dyld3 pre-builds the order initializers need to be run (bottom up) in a list in the closure.
+// This method uses that list to run all initializers.
+// Because an initializer may call dlopen() and/or create threads, the _loadedImages array
+// may move under us. So, never keep a pointer into it. Always reference images by ImageNum
+// and use hint to make that faster in the case where the _loadedImages does not move.
+void AllImages::runInitialzersBottomUp(const closure::Image* topImage)
+{
+    // walk closure specified initializer list, already ordered bottom up
+    topImage->forEachImageToInitBefore(^(closure::ImageNum imageToInit, bool& stop) {
+        // get copy of LoadedImage about imageToInit, but don't keep reference into _loadedImages, because it may move if initialzers call dlopen()
+        uint32_t    indexHint = 0;
+        LoadedImage loadedImageCopy = findImageNum(imageToInit, indexHint);
+        // skip if the image is already inited, or in process of being inited (dependency cycle)
+        if ( (loadedImageCopy.state() == LoadedImage::State::fixedUp) && swapImageState(imageToInit, indexHint, LoadedImage::State::fixedUp, LoadedImage::State::beingInited) ) {
+            // tell objc to run any +load methods in image
+            if ( (_objcNotifyInit != nullptr) && loadedImageCopy.image()->mayHavePlusLoads() ) {
+                dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)loadedImageCopy.loadedAddress(), 0, 0);
+                const char* path = imagePath(loadedImageCopy.image());
+                log_notifications("dyld: objc-init-notifier called with mh=%p, path=%s\n", loadedImageCopy.loadedAddress(), path);
+                (*_objcNotifyInit)(path, loadedImageCopy.loadedAddress());
+            }
+
+            // run all initializers in image
+            runAllInitializersInImage(loadedImageCopy.image(), loadedImageCopy.loadedAddress());
+
+            // advance state to inited
+            swapImageState(imageToInit, indexHint, LoadedImage::State::beingInited, LoadedImage::State::inited);
+        }
+    });
+}
+
+
+void AllImages::runLibSystemInitializer(const LoadedImage& libSystem)
+{
+    // run all initializers in libSystem.dylib
+    runAllInitializersInImage(libSystem.image(), libSystem.loadedAddress());
+
+    // Note: during libSystem's initialization, libdyld_initializer() is called which copies _initialImages to _loadedImages
+
+    // mark libSystem.dylib as being inited, so later recursive-init would re-run it
+    for (LoadedImage& li : _loadedImages) {
+        if ( li.loadedAddress() == libSystem.loadedAddress() ) {
+            li.setState(LoadedImage::State::inited);
+            break;
+        }
+    }
 }
 
-void AllImages::runLibSystemInitializer(const mach_header* libSystemAddress, const launch_cache::binary_format::Image* libSystemBinImage)
+void AllImages::runAllInitializersInImage(const closure::Image* image, const MachOLoaded* ml)
 {
-    // run all initializers in image
-    launch_cache::Image libSystemImage(libSystemBinImage);
-    libSystemImage.forEachInitializer(libSystemAddress, ^(const void* func) {
+    image->forEachInitializer(ml, ^(const void* func) {
         Initializer initFunc = (Initializer)func;
-        dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{
+#if __has_feature(ptrauth_calls)
+        initFunc = (Initializer)__builtin_ptrauth_sign_unauthenticated((void*)initFunc, 0, 0);
+#endif
+        {
+            ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)ml, (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;
         }
+        log_initializers("dyld: called initialzer %p in %s\n", initFunc, image->path());
     });
 }
 
-void AllImages::runInitialzersBottomUp(const mach_header* imageLoadAddress)
+const MachOLoaded* AllImages::dlopen(Diagnostics& diag, const char* path, bool rtldNoLoad, bool rtldLocal, bool rtldNoDelete, bool fromOFI, const void* callerAddress)
 {
-    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());
+    // quick check if path is in shared cache and already loaded
+    if ( _dyldCacheAddress != nullptr ) {
+        uint32_t dyldCacheImageIndex;
+        if ( _dyldCacheAddress->hasImagePath(path, dyldCacheImageIndex) ) {
+            uint64_t mTime;
+            uint64_t inode;
+            const MachOLoaded* mh = (MachOLoaded*)_dyldCacheAddress->getIndexedImageEntry(dyldCacheImageIndex, mTime, inode);
+            // Note: we do not need readLock because this is within global dlopen lock
+            for (const LoadedImage& li : _loadedImages) {
+                if ( li.loadedAddress() == mh ) {
+                    return mh;
                 }
-                // 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);
-    });
+        }
+    }
+
+    __block closure::ImageNum callerImageNum = 0;
+    STACK_ALLOC_ARRAY(LoadedImage, loadedList, 1024);
+    for (const LoadedImage& li : _loadedImages) {
+        loadedList.push_back(li);
+        uint8_t permissions;
+        if ( (callerImageNum == 0) && li.image()->containsAddress(callerAddress, li.loadedAddress(), &permissions) ) {
+            callerImageNum = li.image()->imageNum();
+        }
+        //fprintf(stderr, "mh=%p, image=%p, imageNum=0x%04X, path=%s\n", li.loadedAddress(), li.image(), li.image()->imageNum(), li.image()->path());
+    }
+    uintptr_t alreadyLoadedCount = loadedList.count();
+
+    // make closure
+    closure::ImageNum topImageNum = 0;
+    const closure::DlopenClosure* newClosure;
+
+    // First try with closures from the shared cache permitted.
+    // Then try again with forcing a new closure
+    for (bool canUseSharedCacheClosure : { true, false }) {
+        closure::FileSystemPhysical fileSystem;
+        closure::ClosureBuilder::AtPath atPathHanding = (_allowAtPaths ? closure::ClosureBuilder::AtPath::all : closure::ClosureBuilder::AtPath::onlyInRPaths);
+        closure::ClosureBuilder cb(_nextImageNum, fileSystem, _dyldCacheAddress, true, closure::gPathOverrides, atPathHanding);
+        newClosure = cb.makeDlopenClosure(path, _mainClosure, loadedList, callerImageNum, rtldNoLoad, canUseSharedCacheClosure, &topImageNum);
+        if ( newClosure == closure::ClosureBuilder::sRetryDlopenClosure ) {
+            log_apis("   dlopen: closure builder needs to retry: %s\n", path);
+            assert(canUseSharedCacheClosure);
+            continue;
+        }
+        if ( (newClosure == nullptr) && (topImageNum == 0) ) {
+            if ( cb.diagnostics().hasError())
+                diag.error("%s", cb.diagnostics().errorMessage());
+            else if ( !rtldNoLoad )
+                diag.error("dlopen(): file not found: %s", path);
+            return nullptr;
+        }
+        // save off next available ImageNum for use by next call to dlopen()
+        _nextImageNum = cb.nextFreeImageNum();
+        break;
+    }
+
+    if ( newClosure != nullptr ) {
+        // if new closure contains an ImageArray, add it to list
+        if ( const closure::ImageArray* newArray = newClosure->images() ) {
+            appendToImagesArray(newArray);
+        }
+        log_apis("   dlopen: made closure: %p\n", newClosure);
+    }
+
+    // if already loaded, just bump refCount and return
+    if ( (newClosure == nullptr) && (topImageNum != 0) ) {
+        for (LoadedImage& li : _loadedImages) {
+            if ( li.image()->imageNum() == topImageNum ) {
+                // is already loaded
+                const MachOLoaded* topLoadAddress = li.loadedAddress();
+                if ( !li.image()->inDyldCache() )
+                    incRefCount(topLoadAddress);
+                log_apis("   dlopen: already loaded as '%s'\n", li.image()->path());
+                // if previously opened with RTLD_LOCAL, but now opened with RTLD_GLOBAL, unhide it
+                if ( !rtldLocal && li.hideFromFlatSearch() )
+                    li.setHideFromFlatSearch(false);
+                // if called with RTLD_NODELETE, mark it as never-unload
+                if ( rtldNoDelete )
+                    li.markLeaveMapped();
+                return topLoadAddress;
+            }
+        }
+    }
+
+    // run loader to load all new images
+       Loader loader(loadedList, _dyldCacheAddress, imagesArrays(), &dyld3::log_loads, &dyld3::log_segments, &dyld3::log_fixups, &dyld3::log_dofs);
+       const closure::Image* topImage = closure::ImageArray::findImage(imagesArrays(), topImageNum);
+    if ( newClosure == nullptr ) {
+        if ( topImageNum < dyld3::closure::kLastDyldCacheImageNum )
+            log_apis("   dlopen: using image in dyld shared cache %p\n", topImage);
+        else
+            log_apis("   dlopen: using pre-built dlopen closure %p\n", topImage);
+    }
+    uintptr_t topIndex = loadedList.count();
+    LoadedImage topLoadedImage = LoadedImage::make(topImage);
+    if ( rtldLocal && !topImage->inDyldCache() )
+        topLoadedImage.setHideFromFlatSearch(true);
+    if ( rtldNoDelete && !topImage->inDyldCache() )
+        topLoadedImage.markLeaveMapped();
+    loader.addImage(topLoadedImage);
+
+
+    // recursively load all dependents and fill in allImages array
+    loader.completeAllDependents(diag, topIndex);
+    if ( diag.hasError() )
+         return nullptr;
+    loader.mapAndFixupAllImages(diag, _processDOFs, fromOFI, topIndex);
+    if ( diag.hasError() )
+         return nullptr;
+
+    const MachOLoaded* topLoadAddress = loadedList[topIndex].loadedAddress();
+
+    // bump dlopen refcount of image directly loaded
+    if ( !topImage->inDyldCache() )
+        incRefCount(topLoadAddress);
+
+    // tell gAllImages about new images
+    const uint32_t newImageCount = (uint32_t)(loadedList.count() - alreadyLoadedCount);
+    addImages(loadedList.subArray(alreadyLoadedCount, newImageCount));
+
+    // if closure adds images that override dyld cache, patch cache
+    if ( newClosure != nullptr )
+        applyInterposingToDyldCache(newClosure);
+
+    runImageNotifiers(loadedList.subArray(alreadyLoadedCount, newImageCount));
+
+    // run initializers
+    runInitialzersBottomUp(topImage);
+
+    return topLoadAddress;
+}
+
+void AllImages::appendToImagesArray(const closure::ImageArray* newArray)
+{
+    _imagesArrays.push_back(newArray);
 }
 
+const Array<const closure::ImageArray*>& AllImages::imagesArrays()
+{
+    return _imagesArrays.array();
+}
+
+bool AllImages::isRestricted() const
+{
+    return !_allowEnvPaths;
+}
+
+
+
 
 } // namespace dyld3
 
index 47d06552dbec9fb130f09ba07ff6a764ac31a495..988077393943af0485c9ec97291e293fb4ca4a6e 100644 (file)
 #ifndef __ALL_IMAGES_H__
 #define __ALL_IMAGES_H__
 
-#include <pthread.h>
 #include <mach-o/loader.h>
+#include <pthread.h>
+#include <os/lock_private.h>
 
 #include "dyld_priv.h"
 
-#include "LaunchCache.h"
+#include "Closure.h"
 #include "Loading.h"
-
+#include "MachOLoaded.h"
+#include "DyldSharedCache.h"
 
 
 #if __MAC_OS_X_VERSION_MIN_REQUIRED
 // only in macOS and deprecated 
-struct VIS_HIDDEN __NSObjectFileImage
+struct VIS_HIDDEN OFIInfo
 {
-    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;
+    const char*                     path; //        = nullptr;
+    const void*                     memSource; //   = nullptr;
+    size_t                          memLength; //   = 0;
+    const dyld3::MachOLoaded*       loadAddress; // = nullptr;
+    uint64_t                        imageNum; //    = 0;
 };
 #endif
 
@@ -52,53 +54,48 @@ 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);
+    typedef void                (*NotifyFunc)(const mach_header* mh, intptr_t slide);
+    typedef void                (*LoadNotifyFunc)(const mach_header* mh, const char* path, bool unloadable);
+
+    void                        init(const closure::LaunchClosure* closure, const DyldSharedCache* dyldCacheLoadAddress, const char* dyldCachePath,
+                                     const Array<LoadedImage>& initialImages);
+    void                        setRestrictions(bool allowAtPaths, bool allowEnvPaths);
     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();
+    void                        addImages(const Array<LoadedImage>& newImages);
+    void                        removeImages(const Array<LoadedImage>& unloadImages);
+    void                        runImageNotifiers(const Array<LoadedImage>& newImages);
+    void                        applyInterposingToDyldCache(const closure::Closure* closure);
+    void                        runStartupInitialzers();
+    void                        runInitialzersBottomUp(const closure::Image* topImage);
+    void                        runLibSystemInitializer(const LoadedImage& libSystem);
 
     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;
+
+    void                        forEachImage(void (^handler)(const LoadedImage& loadedImage, bool& stop)) const;
+    const MachOLoaded*          findDependent(const MachOLoaded* mh, uint32_t depIndex);
+    void                        visitDependentsTopDown(const LoadedImage& start, void (^handler)(const LoadedImage& aLoadedImage, bool& stop)) const;
+    void                        infoForImageMappedAt(const void* addr, void (^handler)(const LoadedImage& foundImage, uint8_t permissions)) const;
+    bool                        infoForImageMappedAt(const void* addr, const MachOLoaded** ml, uint64_t* textSize, const char** path) const;
+    void                        infoForNonCachedImageMappedAt(const void* addr, void (^handler)(const LoadedImage& foundImage, uint8_t permissions)) const;
+    void                        infoForImageWithLoadAddress(const MachOLoaded*, void (^handler)(const LoadedImage& foundImage)) const;
+    const char*                 pathForImageMappedAt(const void* addr) const;
+    const char*                 imagePathByIndex(uint32_t index) const;
+    const mach_header*          imageLoadAddressByIndex(uint32_t index) const;
+    bool                        immutableMemory(const void* addr, size_t length) const;
+
+    bool                        isRestricted() const;
+    const MachOLoaded*          mainExecutable() const;
+    const closure::Image*       mainExecutableImage() const;
     const void*                 cacheLoadAddress() const { return _dyldCacheAddress; }
     const char*                 dyldCachePath() const { return _dyldCachePath; }
-    const char*                 imagePath(const BinaryImage*) const;
+    bool                        dyldCacheHasPath(const char* path) const;
+    const char*                 imagePath(const closure::Image*) const;
+    dyld_platform_t             platform() 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);
+    const Array<const closure::ImageArray*>& imagesArrays();
 
-    bool                        imageUnloadable(const launch_cache::Image& image, const mach_header* loadAddress) const;
     void                        incRefCount(const mach_header* loadAddress);
     void                        decRefCount(const mach_header* loadAddress);
 
@@ -106,22 +103,24 @@ public:
     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                        addLoadNotifier(LoadNotifyFunc);
 
-    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);
+    void                        notifyMonitorLoads(const Array<LoadedImage>& newImages);
+    void                        notifyMonitorUnloads(const Array<LoadedImage>& unloadingImages);
 
 #if __MAC_OS_X_VERSION_MIN_REQUIRED
-    __NSObjectFileImage*        addNSObjectFileImage();
-    bool                        hasNSObjectFileImage(__NSObjectFileImage*);
-    void                        removeNSObjectFileImage(__NSObjectFileImage*);
+    NSObjectFileImage           addNSObjectFileImage(const OFIInfo&);
+    void                        removeNSObjectFileImage(NSObjectFileImage);
+    bool                        forNSObjectFileImage(NSObjectFileImage imageHandle,
+                                                     void (^handler)(OFIInfo& image));
 #endif
 
+    const MachOLoaded*          dlopen(Diagnostics& diag, const char* path, bool rtldNoLoad, bool rtldLocal, bool rtldNoDelete, bool fromOFI, const void* callerAddress);
+
     struct ProgramVars
     {
         const void*        mh;
@@ -133,18 +132,42 @@ public:
     void                    setProgramVars(ProgramVars* vars);
 
 private:
+    friend class Reaper;
+    
+    struct DlopenCount {
+        const mach_header*  loadAddress;
+        uintptr_t           refCount;
+    };
+
 
     typedef void (*Initializer)(int argc, const char* argv[], char* envp[], const char* apple[], const ProgramVars* vars);
-    typedef const launch_cache::DynArray<loader::ImageInfo> StartImageArray;
+    typedef const Array<LoadedImage> StartImageArray;
     
-    void                        runInitialzersInImage(const mach_header* imageLoadAddress, const launch_cache::binary_format::Image* binImage);
+    void                        runInitialzersInImage(const mach_header* imageLoadAddress, const closure::Image* image);
     void                        mirrorToOldAllImageInfos();
     void                        garbageCollectImages();
-    void                        vmAccountingSetSuspended(bool suspend);
-    void                        copyCurrentGroupsNoLock(ImageGroupList& groups) const;
-
-    const BinaryClosure*                    _mainClosure         = nullptr;
-    const void*                             _dyldCacheAddress    = nullptr;
+    void                        breadthFirstRecurseDependents(Array<closure::ImageNum>& visited, const LoadedImage& nodeLi, bool& stop, void (^handler)(const LoadedImage& aLoadedImage, bool& stop)) const;
+    void                        appendToImagesArray(const closure::ImageArray* newArray);
+    void                        withReadLock(void (^work)()) const;
+    void                        withWriteLock(void (^work)());
+    void                        withNotifiersLock(void (^work)()) const;
+    bool                        findImage(const mach_header* loadAddress, LoadedImage& foundImage) const;
+    bool                        findImageNum(closure::ImageNum imageNum, LoadedImage& foundImage) const;
+    LoadedImage                 findImageNum(closure::ImageNum num, uint32_t& indexHint);
+    bool                        swapImageState(closure::ImageNum num, uint32_t& indexHint, LoadedImage::State expectedCurrentState, LoadedImage::State newState);
+    void                        runAllInitializersInImage(const closure::Image* image, const MachOLoaded* ml);
+    void                        recomputeBounds();
+
+    void                        constructMachPorts(int slot);
+    void                        teardownMachPorts(int slot);
+    void                        forEachPortSlot(void (^callback)(int slot));
+    void                        sendMachMessage(int slot, mach_msg_id_t msg_id, mach_msg_header_t* msg_buffer, mach_msg_size_t msg_size);
+    void                        notifyMonitoringDyld(bool unloading, const Array<LoadedImage>& images);
+
+    typedef closure::ImageArray  ImageArray;
+
+    const closure::LaunchClosure*           _mainClosure         = nullptr;
+    const DyldSharedCache*                  _dyldCacheAddress    = nullptr;
     const char*                             _dyldCachePath       = nullptr;
     uint64_t                                _dyldCacheSlide      = 0;
     StartImageArray*                        _initialImages       = nullptr;
@@ -155,10 +178,32 @@ private:
     ProgramVars*                            _programVars         = nullptr;
     dyld_all_image_infos*                   _oldAllImageInfos    = nullptr;
     dyld_image_info*                        _oldAllImageArray    = nullptr;
+    dyld_platform_t                         _platform            = 0;
     uint32_t                                _oldArrayAllocCount  = 0;
-    pthread_mutex_t                         _initializerLock     = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
-    pthread_cond_t                          _initializerCondition= PTHREAD_COND_INITIALIZER;
+    closure::ImageNum                       _nextImageNum        = 0;
     int32_t                                 _gcCount             = 0;
+    bool                                    _processDOFs         = false;
+    bool                                    _allowAtPaths        = false;
+    bool                                    _allowEnvPaths       = false;
+    uintptr_t                               _lowestNonCached     = 0;
+    uintptr_t                               _highestNonCached    = UINTPTR_MAX;
+#ifdef OS_UNFAIR_RECURSIVE_LOCK_INIT
+    mutable os_unfair_recursive_lock        _loadImagesLock      = OS_UNFAIR_RECURSIVE_LOCK_INIT;
+    mutable os_unfair_recursive_lock        _notifiersLock       = OS_UNFAIR_RECURSIVE_LOCK_INIT;
+#else
+    mutable pthread_mutex_t                 _loadImagesLock      = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
+    mutable pthread_mutex_t                 _notifiersLock       = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
+#endif
+    GrowableArray<const ImageArray*, 4, 4>  _imagesArrays;
+    GrowableArray<NotifyFunc, 4, 4>         _loadNotifiers;
+    GrowableArray<NotifyFunc, 4, 4>         _unloadNotifiers;
+    GrowableArray<LoadNotifyFunc, 4, 4>     _loadNotifiers2;
+    GrowableArray<DlopenCount, 4, 4>        _dlopenRefCounts;
+    GrowableArray<LoadedImage, 16>          _loadedImages;
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+    uint64_t                                 _nextObjectFileImageNum = 0;
+    GrowableArray<OFIInfo, 4, 1>             _objectFileImages;
+#endif
 };
 
 extern AllImages gAllImages;
diff --git a/dyld3/Array.h b/dyld3/Array.h
new file mode 100644 (file)
index 0000000..5ff120f
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * 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 Array_h
+#define Array_h
+
+#include <algorithm>
+#include <stdint.h>
+#include <stddef.h>
+#include <mach/mach.h>
+
+#define VIS_HIDDEN __attribute__((visibility("hidden")))
+
+namespace dyld3 {
+
+
+//
+// Similar to std::vector<> but storage is pre-allocated and cannot be re-allocated.
+// Storage is normally stack allocated.
+//
+// Use push_back() to add elements and range based for loops to iterate and [] to access by index.
+//
+template <typename T>
+class VIS_HIDDEN Array
+{
+public:
+                    Array()                                 : _elements(nullptr), _allocCount(0), _usedCount(0) {}
+                    Array(T* storage, uintptr_t allocCount, uintptr_t usedCount=0) : _elements(storage), _allocCount(allocCount), _usedCount(usedCount) {}
+    void            setInitialStorage(T* storage, uintptr_t allocCount) { assert(_usedCount == 0); _elements=storage; _allocCount=allocCount; }
+
+    T&              operator[](size_t idx)       { assert(idx < _usedCount); return _elements[idx]; }
+    const T&        operator[](size_t idx) const { assert(idx < _usedCount); return _elements[idx]; }
+    T&              back()                       { assert(_usedCount > 0); return _elements[_usedCount-1]; }
+    uintptr_t       count() const                { return _usedCount; }
+    uintptr_t       maxCount() const             { return _allocCount; }
+    uintptr_t       freeCount() const            { return _allocCount - _usedCount; }
+    bool            empty() const                { return (_usedCount == 0); }
+    uintptr_t       index(const T& element)      { return &element - _elements; }
+    void            push_back(const T& t)        { assert(_usedCount < _allocCount); _elements[_usedCount++] = t; }
+    void            pop_back()                   { assert(_usedCount > 0); _usedCount--; }
+    T*              begin()                      { return &_elements[0]; }
+    T*              end()                        { return &_elements[_usedCount]; }
+    const T*        begin() const                { return &_elements[0]; }
+    const T*        end() const                  { return &_elements[_usedCount]; }
+    const Array<T>  subArray(uintptr_t start, uintptr_t size) const { assert(start+size <= _usedCount);
+                                                                      return Array<T>(&_elements[start], size, size); }
+    bool            contains(const T& targ) const { for (const T& a : *this) { if ( a == targ ) return true; } return false; }
+    void            remove(size_t idx)          { assert(idx < _usedCount); ::memmove(&_elements[idx], &_elements[idx+1], sizeof(T)*(_usedCount-idx-1)); }
+
+protected:
+    T*          _elements;
+    uintptr_t   _allocCount;
+    uintptr_t   _usedCount;
+};
+
+
+// If an Array<>.setInitialStorage() is used, the array may out live the stack space of the storage.
+// To allow cleanup to be done to array elements when the stack goes away, you can make a local
+// variable of ArrayFinalizer<>.
+template <typename T>
+class VIS_HIDDEN ArrayFinalizer
+{
+public:
+    typedef void (^CleanUp)(T& element);
+                    ArrayFinalizer(Array<T>& array, CleanUp handler) : _array(array), _handler(handler) { }
+                    ~ArrayFinalizer() { for(T& element : _array) _handler(element); }
+private:
+    Array<T>&   _array;
+    CleanUp     _handler;
+};
+
+
+
+//
+// Similar to Array<> but if the array overflows, it is re-allocated using vm_allocate().
+// When the variable goes out of scope, any vm_allocate()ed storage is released.
+// if MAXCOUNT is specified, then only one one vm_allocate() to that size is done.
+//
+template <typename T, uintptr_t MAXCOUNT=0xFFFFFFFF>
+class VIS_HIDDEN OverflowSafeArray : public Array<T>
+{
+public:
+                    OverflowSafeArray() : Array<T>(nullptr, 0) {}
+                    OverflowSafeArray(T* stackStorage, uintptr_t stackAllocCount) : Array<T>(stackStorage, stackAllocCount) {}
+                    ~OverflowSafeArray();
+
+    void            push_back(const T& t)        { verifySpace(1); this->_elements[this->_usedCount++] = t; }
+    void            clear() { this->_usedCount = 0; }
+    void            reserve(uintptr_t n) { if (this->_allocCount < n) growTo(n); }
+
+protected:
+    void            growTo(uintptr_t n);
+    void            verifySpace(uintptr_t n)     { if (this->_usedCount+n > this->_allocCount) growTo(this->_usedCount + n); }
+
+private:
+    vm_address_t    _overflowBuffer         = 0;
+    vm_size_t       _overflowBufferSize     = 0;
+};
+
+
+template <typename T, uintptr_t MAXCOUNT>
+inline void OverflowSafeArray<T,MAXCOUNT>::growTo(uintptr_t n)
+{
+    vm_address_t    oldBuffer      = _overflowBuffer;
+    vm_size_t       oldBufferSize  = _overflowBufferSize;
+    if ( MAXCOUNT != 0xFFFFFFFF ) {
+        assert(oldBufferSize == 0); // only re-alloc once
+        // MAXCOUNT is specified, so immediately jump to that size
+        _overflowBufferSize = round_page(MAXCOUNT * sizeof(T));
+    }
+    else {
+       // MAXCOUNT is not specified, keep doubling size
+       _overflowBufferSize = round_page(std::max(this->_allocCount * 2, n) * sizeof(T));
+    }
+    assert(::vm_allocate(mach_task_self(), &_overflowBuffer, _overflowBufferSize, VM_FLAGS_ANYWHERE) == KERN_SUCCESS);
+    ::memcpy((void*)_overflowBuffer, this->_elements, this->_usedCount*sizeof(T));
+    this->_elements = (T*)_overflowBuffer;
+    this->_allocCount = _overflowBufferSize / sizeof(T);
+
+    if ( oldBuffer != 0 )
+        ::vm_deallocate(mach_task_self(), oldBuffer, oldBufferSize);
+}
+
+template <typename T, uintptr_t MAXCOUNT>
+inline OverflowSafeArray<T,MAXCOUNT>::~OverflowSafeArray()
+{
+    if ( _overflowBuffer != 0 )
+        ::vm_deallocate(mach_task_self(), _overflowBuffer, _overflowBufferSize);
+}
+
+
+
+
+#if BUILDING_LIBDYLD
+//
+// Similar to std::vector<> but storage is initially allocated in the object. But if it needs to
+// grow beyond, it will use malloc.  The QUANT template arg is the "quantum" size for allocations.
+// When the allocation needs to be grown, it is re-allocated at the required size rounded up to
+// the next quantum.
+//
+// Use push_back() to add elements and range based for loops to iterate and [] to access by index.
+//
+// Note: this should be a subclass of Array<T> but doing so disables the compiler from optimizing away static constructors
+//
+template <typename T, int QUANT=4, int INIT=1>
+class VIS_HIDDEN GrowableArray
+{
+public:
+
+    T&              operator[](size_t idx)       { assert(idx < _usedCount); return _elements[idx]; }
+    const T&        operator[](size_t idx) const { assert(idx < _usedCount); return _elements[idx]; }
+    T&              back()                       { assert(_usedCount > 0); return _elements[_usedCount-1]; }
+    uintptr_t       count() const                { return _usedCount; }
+    uintptr_t       maxCount() const             { return _allocCount; }
+    bool            empty() const                { return (_usedCount == 0); }
+    uintptr_t       index(const T& element)      { return &element - _elements; }
+    void            push_back(const T& t)        { verifySpace(1); _elements[_usedCount++] = t; }
+    void            append(const Array<T>& a);
+    void            pop_back()                   { assert(_usedCount > 0); _usedCount--; }
+    T*              begin()                      { return &_elements[0]; }
+    T*              end()                        { return &_elements[_usedCount]; }
+    const T*        begin() const                { return &_elements[0]; }
+    const T*        end() const                  { return &_elements[_usedCount]; }
+    const Array<T>  subArray(uintptr_t start, uintptr_t size) const { assert(start+size <= _usedCount);
+                                                                      return Array<T>(&_elements[start], size, size); }
+    const Array<T>& array() const                 { return *((Array<T>*)this); }
+    bool            contains(const T& targ) const { for (const T& a : *this) { if ( a == targ ) return true; } return false; }
+    void            erase(T& targ);
+
+protected:
+    void            growTo(uintptr_t n);
+    void            verifySpace(uintptr_t n)     { if (this->_usedCount+n > this->_allocCount) growTo(this->_usedCount + n); }
+
+private:
+    T*              _elements               = _initialAlloc;
+    uintptr_t       _allocCount             = INIT;
+    uintptr_t       _usedCount              = 0;
+    T               _initialAlloc[INIT]     = { };
+};
+
+
+template <typename T, int QUANT, int INIT>
+inline void GrowableArray<T,QUANT,INIT>::growTo(uintptr_t n)
+{
+    uintptr_t newCount = (n + QUANT - 1) & (-QUANT);
+    T* newArray = (T*)::malloc(sizeof(T)*newCount);
+    T* oldArray = this->_elements;
+    if ( this->_usedCount != 0 )
+        ::memcpy(newArray, oldArray, sizeof(T)*this->_usedCount);
+    this->_elements   = newArray;
+    this->_allocCount = newCount;
+    if ( oldArray != this->_initialAlloc )
+        ::free(oldArray);
+}
+
+template <typename T, int QUANT, int INIT>
+inline void GrowableArray<T,QUANT,INIT>::append(const Array<T>& a)
+{
+    verifySpace(a.count());
+    ::memcpy(&_elements[_usedCount], a.begin(), a.count()*sizeof(T));
+    _usedCount += a.count();
+}
+
+template <typename T, int QUANT, int INIT>
+inline void GrowableArray<T,QUANT,INIT>::erase(T& targ)
+{
+    intptr_t index = &targ - _elements;
+    assert(index >= 0);
+    assert(index < (intptr_t)_usedCount);
+    intptr_t moveCount = _usedCount-index-1;
+    if ( moveCount > 0 )
+        ::memcpy(&_elements[index], &_elements[index+1], moveCount*sizeof(T));
+    _usedCount -= 1;
+}
+
+#endif // BUILDING_LIBDYLD
+
+
+
+//  STACK_ALLOC_ARRAY(foo, myarray, 10);
+//  myarray is of type Array<foo>
+#define STACK_ALLOC_ARRAY(_type, _name, _count)  \
+    uintptr_t __##_name##_array_alloc[1 + ((sizeof(_type)*(_count))/sizeof(uintptr_t))]; \
+    __block dyld3::Array<_type> _name((_type*)__##_name##_array_alloc, _count);
+
+
+//  STACK_ALLOC_OVERFLOW_SAFE_ARRAY(foo, myarray, 10);
+//  myarray is of type OverflowSafeArray<foo>
+#define STACK_ALLOC_OVERFLOW_SAFE_ARRAY(_type, _name, _count)  \
+    uintptr_t __##_name##_array_alloc[1 + ((sizeof(_type)*(_count))/sizeof(uintptr_t))]; \
+    __block dyld3::OverflowSafeArray<_type> _name((_type*)__##_name##_array_alloc, _count);
+
+
+//  work around compiler bug where:
+//       __block type name[count];
+//  is not accessible in a block
+#define BLOCK_ACCCESSIBLE_ARRAY(_type, _name, _count)  \
+    _type __##_name##_array_alloc[_count]; \
+    _type* _name = __##_name##_array_alloc;
+
+
+} // namespace dyld3
+
+#endif /* Array_h */
diff --git a/dyld3/Closure.cpp b/dyld3/Closure.cpp
new file mode 100644 (file)
index 0000000..7a28ae1
--- /dev/null
@@ -0,0 +1,963 @@
+/*
+ * 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 "Closure.h"
+#include "MachOFile.h"
+#include "MachOLoaded.h"
+
+
+namespace dyld {
+    extern void log(const char* format, ...)  __attribute__((format(printf, 1, 2)));
+}
+
+namespace dyld3 {
+namespace closure {
+
+
+////////////////////////////  TypedBytes ////////////////////////////////////////
+
+const void* TypedBytes::payload() const
+{
+    return (uint8_t*)this + sizeof(TypedBytes);
+}
+
+void* TypedBytes::payload()
+{
+    return (uint8_t*)this + sizeof(TypedBytes);
+}
+
+
+////////////////////////////  ContainerTypedBytes ////////////////////////////////////////
+
+const TypedBytes* ContainerTypedBytes::first() const
+{
+    return (TypedBytes*)payload();
+}
+
+const TypedBytes* ContainerTypedBytes::next(const TypedBytes* p) const
+{
+    assert((p->payloadLength & 0x3) == 0);
+    return (TypedBytes*)((uint8_t*)(p->payload()) + p->payloadLength);
+}
+
+void ContainerTypedBytes::forEachAttribute(void (^handler)(const TypedBytes* typedBytes, bool& stop)) const
+{
+    assert(((long)this & 0x3) == 0);
+    const TypedBytes* end = next(this);
+    bool stop = false;
+    for (const TypedBytes* p = first(); p < end && !stop; p = next(p)) {
+        handler(p, stop);
+    }
+}
+
+void ContainerTypedBytes::forEachAttributePayload(Type requestedType, void (^handler)(const void* payload, uint32_t size, bool& stop)) const
+{
+    forEachAttribute(^(const TypedBytes* typedBytes, bool& stop) {
+        if ( (Type)(typedBytes->type) != requestedType )
+            return;
+        handler(typedBytes->payload(), typedBytes->payloadLength, stop);
+    });
+}
+
+const void* ContainerTypedBytes::findAttributePayload(Type requestedType, uint32_t* payloadSize) const
+{
+    assert(((long)this & 0x3) == 0);
+    if ( payloadSize != nullptr )
+        *payloadSize = 0;
+    const TypedBytes* end = next(this);
+    bool stop = false;
+    for (const TypedBytes* p = first(); p < end && !stop; p = next(p)) {
+        if ( (Type)(p->type) == requestedType ) {
+            if ( payloadSize != nullptr )
+                *payloadSize = p->payloadLength;
+            return p->payload();
+        }
+    }
+    return nullptr;
+}
+
+
+////////////////////////////  Image ////////////////////////////////////////
+
+const Image::Flags& Image::getFlags() const
+{
+    return *(Flags*)((uint8_t*)this + 2*sizeof(TypedBytes));
+}
+
+bool Image::isInvalid() const
+{
+    return getFlags().isInvalid;
+}
+
+size_t Image::size() const
+{
+    return sizeof(TypedBytes) + this->payloadLength;
+}
+
+ImageNum Image::imageNum() const
+{
+    return getFlags().imageNum;
+}
+
+// returns true iff 'num' is this image's ImageNum, or this image overrides that imageNum (in dyld cache)
+bool Image::representsImageNum(ImageNum num) const
+{
+    const Flags& flags = getFlags();
+    if ( flags.imageNum == num )
+        return true;
+    if ( !flags.isDylib )
+        return false;
+    if ( flags.inDyldCache )
+        return false;
+    ImageNum cacheImageNum;
+    if ( isOverrideOfDyldCacheImage(cacheImageNum) )
+        return (cacheImageNum == num);
+    return false;
+}
+
+uint32_t Image::maxLoadCount() const
+{
+    return getFlags().maxLoadCount;
+}
+
+bool Image::isBundle() const
+{
+    return getFlags().isBundle;
+}
+
+bool Image::isDylib() const
+{
+    return getFlags().isDylib;
+}
+
+bool Image::isExecutable() const
+{
+    return getFlags().isExecutable;
+}
+
+bool Image::hasObjC() const
+{
+    return getFlags().hasObjC;
+}
+
+bool Image::is64() const
+{
+    return getFlags().is64;
+}
+
+bool Image::hasWeakDefs() const
+{
+    return getFlags().hasWeakDefs;
+}
+
+bool Image::mayHavePlusLoads() const
+{
+    return getFlags().mayHavePlusLoads;
+}
+
+bool Image::neverUnload() const
+{
+    return getFlags().neverUnload;
+}
+
+bool Image::overridableDylib() const
+{
+    return getFlags().overridableDylib;
+}
+
+bool Image::inDyldCache() const
+{
+    return getFlags().inDyldCache;
+}
+
+const char* Image::path() const
+{
+    // might be multiple pathWithHash enties, first is canonical name
+    const PathAndHash* result = (PathAndHash*)findAttributePayload(Type::pathWithHash);
+    assert(result && "Image missing pathWithHash");
+    return result->path;
+}
+
+const char* Image::leafName() const
+{
+    uint32_t size;
+    // might be multiple pathWithHash enties, first is canonical name
+    const PathAndHash* result = (PathAndHash*)findAttributePayload(Type::pathWithHash, &size);
+    assert(result && "Image missing pathWithHash");
+    for (const char* p=(char*)result + size; p > result->path; --p) {
+        if ( *p == '/' )
+            return p+1;
+    }
+    return result->path;
+}
+
+bool Image::hasFileModTimeAndInode(uint64_t& inode, uint64_t& mTime) const
+{
+    uint32_t size;
+    const FileInfo* info = (FileInfo*)(findAttributePayload(Type::fileInodeAndTime, &size));
+    if ( info != nullptr ) {
+        assert(size == sizeof(FileInfo));
+        inode = info->inode;
+        mTime = info->modTime;
+        return true;
+    }
+    return false;
+}
+
+bool Image::hasCdHash(uint8_t cdHash[20]) const
+{
+    uint32_t size;
+    const uint8_t* bytes = (uint8_t*)(findAttributePayload(Type::cdHash, &size));
+    if ( bytes != nullptr ) {
+        assert(size == 20);
+        memcpy(cdHash, bytes, 20);
+        return true;
+    }
+    return false;
+}
+
+bool Image::getUuid(uuid_t uuid) const
+{
+    uint32_t size;
+    const uint8_t* bytes = (uint8_t*)(findAttributePayload(Type::uuid, &size));
+    if ( bytes == nullptr )
+        return false;
+    assert(size == 16);
+    memcpy(uuid, bytes, 16);
+    return true;
+}
+
+bool Image::hasCodeSignature(uint32_t& sigFileOffset, uint32_t& sigSize) const
+{
+    uint32_t sz;
+    const Image::CodeSignatureLocation* sigInfo = (Image::CodeSignatureLocation*)(findAttributePayload(Type::codeSignLoc, &sz));
+    if ( sigInfo != nullptr ) {
+        assert(sz == sizeof(Image::CodeSignatureLocation));
+        sigFileOffset = sigInfo->fileOffset;
+        sigSize       = sigInfo->fileSize;
+        return true;
+    }
+    return false;
+}
+
+bool Image::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const
+{
+    uint32_t sz;
+    const Image::FairPlayRange* fpInfo = (Image::FairPlayRange*)(findAttributePayload(Type::fairPlayLoc, &sz));
+    if ( fpInfo != nullptr ) {
+        assert(sz == sizeof(Image::FairPlayRange));
+        textOffset = fpInfo->textStartPage * pageSize();
+        size       = fpInfo->textPageCount * pageSize();
+        return true;
+    }
+    return false;
+}
+
+const Array<Image::LinkedImage> Image::dependentsArray() const
+{
+    uint32_t size;
+    LinkedImage* dependents = (LinkedImage*)findAttributePayload(Type::dependents, &size);
+    assert((size % sizeof(LinkedImage)) == 0);
+    uintptr_t count = size / sizeof(LinkedImage);
+    return Array<Image::LinkedImage>(dependents, count, count);
+}
+
+void Image::forEachDependentImage(void (^handler)(uint32_t dependentIndex, LinkKind kind, ImageNum imageNum, bool& stop)) const
+{
+    uint32_t size;
+    const LinkedImage* dependents = (LinkedImage*)findAttributePayload(Type::dependents, &size);
+    assert((size % sizeof(LinkedImage)) == 0);
+    const uint32_t count = size / sizeof(LinkedImage);
+    bool stop = false;
+    for (uint32_t i=0; (i < count) && !stop; ++i) {
+        LinkKind kind     = dependents[i].kind();
+        ImageNum imageNum = dependents[i].imageNum();
+        // ignore missing weak links
+        if ( (imageNum == kMissingWeakLinkedImage) && (kind == LinkKind::weak) )
+            continue;
+        handler(i, kind, imageNum, stop);
+    }
+}
+
+ImageNum Image::dependentImageNum(uint32_t depIndex) const
+{
+    uint32_t size;
+    const LinkedImage* dependents = (LinkedImage*)findAttributePayload(Type::dependents, &size);
+    assert((size % sizeof(LinkedImage)) == 0);
+    const uint32_t count = size / sizeof(LinkedImage);
+    assert(depIndex < count);
+    return dependents[depIndex].imageNum();
+}
+
+
+uint32_t Image::hashFunction(const char* str)
+{
+    uint32_t h = 0;
+    for (const char* s=str; *s != '\0'; ++s)
+        h = h*5 + *s;
+    return h;
+}
+
+void Image::forEachAlias(void (^handler)(const char* aliasPath, bool& stop)) const
+{
+    __block bool foundFirst = false;
+    forEachAttribute(^(const TypedBytes* typedBytes, bool& stopLoop) {
+        if ( (Type)(typedBytes->type) != Type::pathWithHash )
+            return;
+        if ( foundFirst ) {
+            const PathAndHash* aliasInfo = (PathAndHash*)typedBytes->payload();
+            handler(aliasInfo->path, stopLoop);
+        }
+        else {
+            foundFirst = true;
+        }
+    });
+}
+
+bool Image::hasPathWithHash(const char* path, uint32_t hash) const
+{
+    __block bool found = false;
+    forEachAttribute(^(const TypedBytes* typedBytes, bool& stop) {
+        if ( (Type)(typedBytes->type) != Type::pathWithHash )
+            return;
+        const PathAndHash* pathInfo = (PathAndHash*)typedBytes->payload();
+        if ( (pathInfo->hash == hash) && (strcmp(path, pathInfo->path) == 0) ) {
+            stop = true;
+            found = true;
+        }
+    });
+    return found;
+}
+
+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
+{
+    uint32_t size;
+    const DiskSegment* segments = (DiskSegment*)findAttributePayload(Type::diskSegment, &size);
+    assert(segments != nullptr);
+    assert((size % sizeof(DiskSegment)) == 0);
+    const uint32_t  count        = size / sizeof(DiskSegment);
+    const uint32_t  pageSz       = pageSize();
+    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 < count; ++i) {
+        const DiskSegment* seg = &segments[i];
+        if ( seg->filePageCount != 0 ) {
+            break;
+        }
+        vmOffset -= (uint64_t)seg->vmPageCount * pageSz;
+    }
+    // walk each segment and call handler
+    bool stop = false;
+    for (uint32_t i=0; i < count && !stop; ++i) {
+        const DiskSegment* seg = &segments[i];
+        uint64_t vmSize   = (uint64_t)seg->vmPageCount * pageSz;
+        uint32_t fileSize = seg->filePageCount * pageSz;
+        if ( !seg->paddingNotSeg ) {
+            handler(segIndex, ( fileSize == 0) ? 0 : fileOffset, fileSize, vmOffset, vmSize, seg->permissions, stop);
+            ++segIndex;
+        }
+        vmOffset   += vmSize;
+        fileOffset += fileSize;
+    }
+}
+
+uint32_t Image::pageSize() const
+{
+    if ( getFlags().has16KBpages )
+        return 0x4000;
+    else
+        return 0x1000;
+}
+
+uint32_t Image::cacheOffset() const
+{
+    uint32_t size;
+    const DyldCacheSegment* segments = (DyldCacheSegment*)findAttributePayload(Type::cacheSegment, &size);
+    assert(segments != nullptr);
+    assert((size % sizeof(DyldCacheSegment)) == 0);
+    return segments[0].cacheOffset;
+}
+
+void Image::forEachCacheSegment(void (^handler)(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop)) const
+{
+    uint32_t size;
+    const DyldCacheSegment* segments = (DyldCacheSegment*)findAttributePayload(Type::cacheSegment, &size);
+    assert(segments != nullptr);
+    assert((size % sizeof(DyldCacheSegment)) == 0);
+    const uint32_t  count = size / sizeof(DyldCacheSegment);
+    bool stop = false;
+    for (uint32_t i=0; i < count; ++i) {
+        uint64_t vmOffset    = segments[i].cacheOffset - segments[0].cacheOffset;
+        uint64_t vmSize      = segments[i].size;
+        uint8_t  permissions = segments[i].permissions;
+        handler(i, vmOffset, vmSize, permissions, stop);
+        if ( stop )
+            break;
+    }
+}
+
+uint64_t Image::textSize() const
+{
+    __block uint64_t result = 0;
+    if ( inDyldCache() ) {
+        forEachCacheSegment(^(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
+            result = vmSize;
+            stop = true;
+        });
+    }
+    else {
+        forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
+            if ( permissions != 0) {
+                result = vmSize;
+                stop = true;
+            }
+        });
+    }
+    return result;
+}
+
+bool Image::containsAddress(const void* addr, const void* imageLoadAddress, uint8_t* permsResult) const
+{
+    __block bool  result     = false;
+    uint64_t      targetAddr = (uint64_t)addr;
+    uint64_t      imageStart = (uint64_t)imageLoadAddress;
+    if ( inDyldCache() ) {
+        forEachCacheSegment(^(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
+            if ( (targetAddr >= imageStart+vmOffset) && (targetAddr < imageStart+vmOffset+vmSize) ) {
+                result = true;
+                if ( permsResult )
+                    *permsResult = permissions;
+                stop = true;
+            }
+        });
+    }
+    else {
+        forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
+            if ( (targetAddr >= imageStart+vmOffset) && (targetAddr < imageStart+vmOffset+vmSize) ) {
+                result = true;
+                if ( permsResult )
+                    *permsResult = permissions;
+                stop = true;
+            }
+        });
+    }
+    return result;
+}
+
+uint64_t Image::vmSizeToMap() const
+{
+    uint32_t size;
+    const Image::MappingInfo* info = (Image::MappingInfo*)(findAttributePayload(Type::mappingInfo, &size));
+    assert(info != nullptr);
+    assert(size == sizeof(Image::MappingInfo));
+    return info->totalVmPages * pageSize();
+}
+
+uint64_t Image::sliceOffsetInFile() const
+{
+    uint32_t size;
+    const Image::MappingInfo* info = (Image::MappingInfo*)(findAttributePayload(Type::mappingInfo, &size));
+    assert(info != nullptr);
+    assert(size == sizeof(Image::MappingInfo));
+    return info->sliceOffsetIn4K * 0x1000;
+}
+
+void Image::forEachInitializer(const void* imageLoadAddress, void (^handler)(const void* initializer)) const
+{
+    uint32_t size;
+    const uint32_t* inits = (uint32_t*)findAttributePayload(Type::initOffsets, &size);
+    if ( inits != nullptr ) {
+        assert((size % sizeof(uint32_t)) == 0);
+        const uint32_t count = size / sizeof(uint32_t);
+        for (uint32_t i=0; i < count; ++i) {
+            uint32_t offset = inits[i];
+            const void* init = (void*)((uint8_t*)imageLoadAddress + offset);
+            handler(init);
+        }
+    }
+}
+
+bool Image::hasInitializers() const
+{
+    uint32_t size;
+    return ( findAttributePayload(Type::initOffsets, &size) != nullptr );
+}
+
+void Image::forEachDOF(const void* imageLoadAddress, void (^handler)(const void* dofSection)) const
+{
+    uint32_t size;
+    const uint32_t* dofs = (uint32_t*)findAttributePayload(Type::dofOffsets, &size);
+    if ( dofs != nullptr ) {
+        assert((size % sizeof(uint32_t)) == 0);
+        const uint32_t count = size / sizeof(uint32_t);
+        for (uint32_t i=0; i < count; ++i) {
+            uint32_t offset = dofs[i];
+            const void* sect = (void*)((uint8_t*)imageLoadAddress + offset);
+            handler(sect);
+        }
+    }
+}
+
+void Image::forEachPatchableExport(void (^handler)(uint32_t cacheOffsetOfImpl, const char* exportName)) const
+{
+    forEachAttributePayload(Type::cachePatchInfo, ^(const void* payload, uint32_t size, bool& stop) {
+        const Image::PatchableExport* pe = (Image::PatchableExport*)payload;
+        assert(size > (sizeof(Image::PatchableExport) + pe->patchLocationsCount*sizeof(PatchableExport::PatchLocation)));
+        handler(pe->cacheOffsetOfImpl, (char*)(&pe->patchLocations[pe->patchLocationsCount]));
+    });
+}
+
+void Image::forEachPatchableUseOfExport(uint32_t cacheOffsetOfImpl, void (^handler)(PatchableExport::PatchLocation patchLocation)) const
+{
+    forEachAttributePayload(Type::cachePatchInfo, ^(const void* payload, uint32_t size, bool& stop) {
+        const Image::PatchableExport* pe = (Image::PatchableExport*)payload;
+        assert(size > (sizeof(Image::PatchableExport) + pe->patchLocationsCount*sizeof(PatchableExport::PatchLocation)));
+        if ( pe->cacheOffsetOfImpl != cacheOffsetOfImpl )
+            return;
+        const PatchableExport::PatchLocation* start = pe->patchLocations;
+        const PatchableExport::PatchLocation* end   = &start[pe->patchLocationsCount];
+        for (const PatchableExport::PatchLocation* p=start; p < end; ++p)
+            handler(*p);
+    });
+}
+
+uint32_t Image::patchableExportCount() const
+{
+    __block uint32_t count = 0;
+    forEachAttributePayload(Type::cachePatchInfo, ^(const void* payload, uint32_t size, bool& stop) {
+        ++count;
+    });
+    return count;
+}
+
+void Image::forEachFixup(void (^rebase)(uint64_t imageOffsetToRebase, bool& stop),
+                         void (^bind)(uint64_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop),
+                         void (^chainedFixupsStart)(uint64_t imageOffsetStart, const Array<ResolvedSymbolTarget>& targets, bool& stop)) const
+{
+    const uint32_t pointerSize = is64() ? 8 : 4;
+       uint64_t curRebaseOffset = 0;
+       bool stop = false;
+    for (const Image::RebasePattern& rebasePat : rebaseFixups()) {
+        //fprintf(stderr, " repeat=0x%04X, contig=%d, skip=%d\n", rebasePat.repeatCount, rebasePat.contigCount, rebasePat.skipCount);
+        if ( rebasePat.contigCount == 0 ) {
+            // note: contigCount==0 means this just advances location
+            if ( (rebasePat.repeatCount == 0) && (rebasePat.skipCount == 0) ) {
+                // all zeros is special pattern that means reset to rebase offset to zero
+                curRebaseOffset = 0;
+            }
+            else {
+                curRebaseOffset += rebasePat.repeatCount * rebasePat.skipCount;
+            }
+        }
+        else {
+            for (int r=0; r < rebasePat.repeatCount && !stop; ++r) {
+                for (int i=0; i < rebasePat.contigCount && !stop; ++i) {
+                    //fprintf(stderr, "  0x%08llX\n", curRebaseOffset);
+                    rebase(curRebaseOffset, stop);
+                    curRebaseOffset += pointerSize;
+                }
+                curRebaseOffset += pointerSize * rebasePat.skipCount;
+            }
+        }
+        if ( stop )
+            break;
+    }
+    if ( stop )
+        return;
+
+    for (const Image::BindPattern& bindPat : bindFixups()) {
+        uint64_t curBindOffset = bindPat.startVmOffset;
+        for (uint16_t i=0; i < bindPat.repeatCount; ++i) {
+            bind(curBindOffset, bindPat.target, stop);
+            curBindOffset += (pointerSize * (1 + bindPat.skipCount));
+            if ( stop )
+                break;
+        }
+        if ( stop )
+            break;
+    }
+
+    const Array<Image::ResolvedSymbolTarget> targetsArray = chainedTargets();
+    for (uint64_t start : chainedStarts()) {
+        chainedFixupsStart(start, targetsArray, stop);
+        if ( stop )
+            break;
+    }
+}
+
+void Image::forEachChainedFixup(void* imageLoadAddress, uint64_t imageOffsetChainStart, void (^callback)(uint64_t* fixUpLoc, ChainedFixupPointerOnDisk fixupInfo, bool& stop))
+{
+    bool stop = false;
+    uint64_t* fixupLoc = (uint64_t*)((uint8_t*)imageLoadAddress + imageOffsetChainStart);
+    do {
+        // save off current entry as it will be overwritten in callback
+        ChainedFixupPointerOnDisk info = *((ChainedFixupPointerOnDisk*)fixupLoc);
+        callback(fixupLoc, info, stop);
+        if ( info.plainRebase.next != 0 )
+            fixupLoc += info.plainRebase.next;
+        else
+            stop = true;
+    } while (!stop);
+}
+
+void Image::forEachTextReloc(void (^rebase)(uint32_t imageOffsetToRebase, bool& stop),
+                             void (^bind)(uint32_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop)) const
+{
+    bool stop = false;
+    const Array<Image::TextFixupPattern> f = textFixups();
+    for (const Image::TextFixupPattern& pat : f) {
+        uint32_t curOffset = pat.startVmOffset;
+        for (uint16_t i=0; i < pat.repeatCount; ++i) {
+            if ( pat.target.raw == 0 )
+                rebase(curOffset, stop);
+            else
+                bind(curOffset, pat.target, stop);
+            curOffset += pat.skipCount;
+        }
+    }
+}
+
+const Array<Image::RebasePattern> Image::rebaseFixups() const
+{
+    uint32_t rebaseFixupsSize;
+    Image::RebasePattern* rebaseFixupsContent = (RebasePattern*)findAttributePayload(Type::rebaseFixups, &rebaseFixupsSize);
+    uint32_t rebaseCount = rebaseFixupsSize/sizeof(RebasePattern);
+    return Array<RebasePattern>(rebaseFixupsContent, rebaseCount, rebaseCount);
+}
+
+const Array<Image::BindPattern> Image::bindFixups() const
+{
+    uint32_t bindFixupsSize;
+    BindPattern* bindFixupsContent = (BindPattern*)findAttributePayload(Type::bindFixups, &bindFixupsSize);
+    uint32_t bindCount = bindFixupsSize/sizeof(BindPattern);
+    return Array<BindPattern>(bindFixupsContent, bindCount, bindCount);
+}
+
+const Array<uint64_t> Image::chainedStarts() const
+{
+    uint32_t startsSize;
+    uint64_t* starts = (uint64_t*)findAttributePayload(Type::chainedFixupsStarts, &startsSize);
+    uint32_t count = startsSize/sizeof(uint64_t);
+    return Array<uint64_t>(starts, count, count);
+}
+
+const Array<Image::ResolvedSymbolTarget> Image::chainedTargets() const
+{
+    uint32_t size;
+    ResolvedSymbolTarget* targetsContent = (ResolvedSymbolTarget*)findAttributePayload(Type::chainedFixupsTargets, &size);
+    uint32_t count = size/sizeof(ResolvedSymbolTarget);
+    return Array<ResolvedSymbolTarget>(targetsContent, count, count);
+}
+
+const Array<Image::TextFixupPattern> Image::textFixups() const
+{
+    uint32_t fixupsSize;
+    TextFixupPattern* fixupsContent = (TextFixupPattern*)findAttributePayload(Type::textFixups, &fixupsSize);
+    uint32_t count = fixupsSize/sizeof(TextFixupPattern);
+    return Array<TextFixupPattern>(fixupsContent, count, count);
+}
+
+bool Image::isOverrideOfDyldCacheImage(ImageNum& imageNum) const
+{
+       uint32_t size;
+       const uint32_t* content = (uint32_t*)findAttributePayload(Type::imageOverride, &size);
+       if ( content != nullptr ) {
+        assert(size == sizeof(uint32_t));
+        imageNum = *content;
+        return true;
+    }
+    return false;
+}
+
+void Image::forEachImageToInitBefore(void (^handler)(ImageNum imageToInit, bool& stop)) const
+{
+    uint32_t size;
+    const ImageNum* initBefores = (ImageNum*)findAttributePayload(Type::initBefores, &size);
+    if ( initBefores != nullptr ) {
+        assert((size % sizeof(ImageNum)) == 0);
+        const uint32_t count = size / sizeof(ImageNum);
+        bool stop = false;
+        for (uint32_t i=0; (i < count) && !stop; ++i) {
+            handler(initBefores[i], stop);
+        }
+    }
+}
+
+const char* Image::PatchableExport::PatchLocation::keyName() const
+{
+    return MachOLoaded::ChainedFixupPointerOnDisk::keyName(this->key);
+}
+
+Image::PatchableExport::PatchLocation::PatchLocation(size_t cacheOff, uint64_t ad)
+    : cacheOffset(cacheOff), addend(ad), authenticated(0), usesAddressDiversity(0), key(0), discriminator(0)
+{
+    int64_t signedAddend = (int64_t)ad;
+    assert(((signedAddend << 52) >> 52) == signedAddend);
+}
+
+Image::PatchableExport::PatchLocation::PatchLocation(size_t cacheOff, uint64_t ad, dyld3::MachOLoaded::ChainedFixupPointerOnDisk authInfo)
+    : cacheOffset(cacheOff), addend(ad), authenticated(authInfo.authBind.auth), usesAddressDiversity(authInfo.authBind.addrDiv), key(authInfo.authBind.key), discriminator(authInfo.authBind.diversity)
+{
+    int64_t signedAddend = (int64_t)ad;
+    assert(((signedAddend << 52) >> 52) == signedAddend);
+}
+
+////////////////////////////  ImageArray ////////////////////////////////////////
+
+size_t ImageArray::size() const
+{
+    return sizeof(TypedBytes) + this->payloadLength;
+}
+
+size_t ImageArray::startImageNum() const
+{
+    return firstImageNum;
+}
+
+uint32_t ImageArray::imageCount() const
+{
+    return count;
+}
+
+void ImageArray::forEachImage(void (^callback)(const Image* image, bool& stop)) const
+{
+    bool stop = false;
+    for (uint32_t i=0; i < count && !stop; ++i) {
+        const Image* image = (Image*)((uint8_t*)payload() + offsets[i]);
+        callback(image, stop);
+        if (stop)
+            break;
+    }
+}
+
+bool ImageArray::hasPath(const char* path, ImageNum& num) const
+{
+    const uint32_t hash = Image::hashFunction(path);
+    __block bool found = false;
+    forEachImage(^(const Image* image, bool& stop) {
+        if ( image->hasPathWithHash(path, hash) ) {
+            num   = image->imageNum();
+            found = true;
+            stop  = true;
+        }
+    });
+    return found;
+}
+
+const Image* ImageArray::imageForNum(ImageNum num) const
+{
+    if ( num < firstImageNum )
+        return nullptr;
+
+    uint32_t index = num - firstImageNum;
+    if ( index >= count )
+        return nullptr;
+
+    return (Image*)((uint8_t*)payload() + offsets[index]);
+}
+
+const Image* ImageArray::findImage(const Array<const ImageArray*> imagesArrays, ImageNum imageNum)
+{
+   for (const ImageArray* ia : imagesArrays) {
+        if ( const Image* result = ia->imageForNum(imageNum) )
+            return result;
+    }
+    return nullptr;
+}
+
+////////////////////////////  Closure ////////////////////////////////////////
+
+size_t Closure::size() const
+{
+    return sizeof(TypedBytes) + this->payloadLength;
+}
+
+const ImageArray* Closure::images() const
+{
+    __block const TypedBytes* result = nullptr;
+    forEachAttribute(^(const TypedBytes* typedBytes, bool& stop) {
+        if ( (Type)(typedBytes->type) == Type::imageArray ) {
+            result = typedBytes;
+            stop = true;
+        }
+    });
+
+    return (ImageArray*)result;
+}
+
+ImageNum Closure::topImage() const
+{
+    uint32_t size;
+    const ImageNum* top = (ImageNum*)findAttributePayload(Type::topImage, &size);
+    assert(top != nullptr);
+    assert(size == sizeof(ImageNum));
+    return *top;
+}
+
+void Closure::forEachPatchEntry(void (^handler)(const PatchEntry& entry)) const
+{
+       forEachAttributePayload(Type::cacheOverrides, ^(const void* payload, uint32_t size, bool& stop) {
+        assert((size % sizeof(Closure::PatchEntry)) == 0);
+        const PatchEntry* patches    = (PatchEntry*)payload;
+        const PatchEntry* patchesEnd = (PatchEntry*)((char*)payload + size);
+        for (const PatchEntry* p=patches; p < patchesEnd; ++p)
+            handler(*p);
+       });
+}
+
+void Closure::deallocate() const
+{
+    ::vm_deallocate(mach_task_self(), (long)this, size());
+}
+
+////////////////////////////  LaunchClosure ////////////////////////////////////////
+
+void LaunchClosure::forEachMustBeMissingFile(void (^handler)(const char* path, bool& stop)) const
+{
+    uint32_t size;
+    const char* paths = (const char*)findAttributePayload(Type::missingFiles, &size);
+    bool stop = false;
+    for (const char* s=paths; s < &paths[size]; ++s) {
+        if ( *s != '\0' )
+            handler(s, stop);
+        if ( stop )
+            break;
+        s += strlen(s);
+    }
+}
+
+bool LaunchClosure::builtAgainstDyldCache(uuid_t cacheUUID) const
+{
+    uint32_t size;
+    const uint8_t* uuidBytes = (uint8_t*)findAttributePayload(Type::dyldCacheUUID, &size);
+    if ( uuidBytes == nullptr )
+        return false;
+    assert(size == sizeof(uuid_t));
+    memcpy(cacheUUID, uuidBytes, sizeof(uuid_t));
+    return true;
+}
+
+const char* LaunchClosure::bootUUID() const
+{
+    uint32_t size;
+    return (char*)findAttributePayload(Type::bootUUID, &size);
+}
+
+void LaunchClosure::forEachEnvVar(void (^handler)(const char* keyEqualValue, bool& stop)) const
+{
+    forEachAttributePayload(Type::envVar, ^(const void* payload, uint32_t size, bool& stop) {
+        handler((char*)payload, stop);
+    });
+}
+
+ImageNum LaunchClosure::libSystemImageNum() const
+{
+    uint32_t size;
+    const ImageNum* num = (ImageNum*)findAttributePayload(Type::libSystemNum, &size);
+    assert(num != nullptr);
+    assert(size == sizeof(ImageNum));
+    return *num;
+}
+
+void LaunchClosure::libDyldEntry(Image::ResolvedSymbolTarget& loc) const
+{
+    uint32_t size;
+    const Image::ResolvedSymbolTarget* data = (Image::ResolvedSymbolTarget*)findAttributePayload(Type::libDyldEntry, &size);
+    assert(data != nullptr);
+    assert(size == sizeof(Image::ResolvedSymbolTarget));
+    loc = *data;
+}
+
+bool LaunchClosure::mainEntry(Image::ResolvedSymbolTarget& mainLoc) const
+{
+    uint32_t size;
+    const Image::ResolvedSymbolTarget* data = (Image::ResolvedSymbolTarget*)findAttributePayload(Type::mainEntry, &size);
+    if ( data == nullptr )
+        return false;
+    assert(size == sizeof(Image::ResolvedSymbolTarget));
+    mainLoc = *data;
+    return true;
+}
+
+bool LaunchClosure::startEntry(Image::ResolvedSymbolTarget& startLoc) const
+{
+    uint32_t size;
+    const Image::ResolvedSymbolTarget* data = (Image::ResolvedSymbolTarget*)findAttributePayload(Type::startEntry, &size);
+    if ( data == nullptr )
+        return false;
+    assert(size == sizeof(Image::ResolvedSymbolTarget));
+    startLoc = *data;
+    return true;
+}
+
+const LaunchClosure::Flags& LaunchClosure::getFlags() const
+{
+    uint32_t size;
+    const Flags* flags = (Flags*)findAttributePayload(Type::closureFlags, &size);
+    assert(flags != nullptr && "Closure missing Flags");
+    return *flags;
+}
+
+uint32_t LaunchClosure::initialLoadCount() const
+{
+    return getFlags().initImageCount;
+}
+
+bool LaunchClosure::usedAtPaths() const
+{
+    return getFlags().usedAtPaths;
+}
+
+bool LaunchClosure::usedFallbackPaths() const
+{
+       return getFlags().usedFallbackPaths;
+}
+
+void LaunchClosure::forEachInterposingTuple(void (^handler)(const InterposingTuple& tuple, bool& stop)) const
+{
+       forEachAttributePayload(Type::interposeTuples, ^(const void* payload, uint32_t size, bool& stop) {
+        assert((size % sizeof(InterposingTuple)) == 0);
+        uintptr_t count = size / sizeof(InterposingTuple);
+        const InterposingTuple* tuples = (InterposingTuple*)payload;
+        for (uint32_t i=0; i < count && !stop; ++i) {
+            handler(tuples[i], stop);
+        }
+       });
+}
+
+
+
+} // namespace closure
+} // namespace dyld3
+
+
+
diff --git a/dyld3/Closure.h b/dyld3/Closure.h
new file mode 100644 (file)
index 0000000..40ab625
--- /dev/null
@@ -0,0 +1,570 @@
+/*
+ * 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 Closures_h
+#define Closures_h
+
+
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <mach/mach.h>
+#include <mach-o/loader.h>
+
+#include "Diagnostics.h"
+#include "Array.h"
+#include "MachOLoaded.h"
+#include "SupportedArchs.h"
+
+namespace dyld3 {
+namespace closure {
+
+
+
+// bump this number each time binary format changes
+enum  { kFormatVersion = 10 };
+
+
+typedef uint32_t ImageNum;
+
+const ImageNum kFirstDyldCacheImageNum      = 0x00000001;
+const ImageNum kLastDyldCacheImageNum       = 0x00000FFF;
+const ImageNum kFirstOtherOSImageNum        = 0x00001001;
+const ImageNum kLastOtherOSImageNum         = 0x00001FFF;
+const ImageNum kFirstLaunchClosureImageNum  = 0x00002000;
+const ImageNum kMissingWeakLinkedImage      = 0x0FFFFFFF;
+
+
+//
+//  Generic typed range of bytes (similar to load commands)
+//  Must be 4-byte aligned
+//
+struct VIS_HIDDEN TypedBytes
+{
+    uint32_t     type          : 8,
+                 payloadLength : 24;
+
+    enum class Type {
+        // containers which have an overall length and TypedBytes inside their content
+        launchClosure    =  1, // contains TypedBytes of closure attributes including imageArray
+        imageArray       =  2, // sizeof(ImageArray) + sizeof(uint32_t)*count + size of all images
+        image            =  3, // contains TypedBytes of image attributes
+        dlopenClosure    =  4, // contains TypedBytes of closure attributes including imageArray
+
+        // attributes for Images
+        imageFlags       =  7, // sizeof(Image::Flags)
+        pathWithHash     =  8, // len = uint32_t + length path + 1, use multiple entries for aliases
+        fileInodeAndTime =  9, // sizeof(FileInfo)
+        cdHash           = 10, // 20
+        uuid             = 11, // 16
+        mappingInfo      = 12, // sizeof(MappingInfo)
+        diskSegment      = 13, // sizeof(DiskSegment) * count
+        cacheSegment     = 14, // sizeof(DyldCacheSegment) * count
+        dependents       = 15, // sizeof(LinkedImage) * count
+        initOffsets      = 16, // sizeof(uint32_t) * count
+        dofOffsets       = 17, // sizeof(uint32_t) * count
+        codeSignLoc      = 18, // sizeof(CodeSignatureLocation)
+        fairPlayLoc      = 19, // sizeof(FairPlayRange)
+        rebaseFixups     = 20, // sizeof(RebasePattern) * count
+        bindFixups       = 21, // sizeof(BindPattern) * count
+        cachePatchInfo   = 22, // sizeof(PatchableExport) + count*sizeof(PatchLocation) + strlen(name) // only in dyld cache Images
+        textFixups       = 23, // sizeof(TextFixupPattern) * count
+        imageOverride    = 24, // sizeof(ImageNum)
+        initBefores      = 25, // sizeof(ImageNum) * count
+        chainedFixupsStarts  = 26, // sizeof(uint64_t) * count
+        chainedFixupsTargets = 27, // sizeof(ResolvedSymbolTarget) * count
+
+        // attributes for Closures (launch or dlopen)
+        closureFlags     = 32,  // sizeof(Closure::Flags)
+        dyldCacheUUID    = 33,  // 16
+        missingFiles     = 34,
+        envVar           = 35,  // "DYLD_BLAH=stuff"
+        topImage         = 36,  // sizeof(ImageNum)
+        libDyldEntry     = 37,  // sizeof(ResolvedSymbolTarget)
+        libSystemNum     = 38,  // sizeof(ImageNum)
+        bootUUID         = 39,  // c-string 40
+        mainEntry        = 40,  // sizeof(ResolvedSymbolTarget)
+        startEntry       = 41,  // sizeof(ResolvedSymbolTarget)     // used by programs built with crt1.o
+        cacheOverrides   = 42,  // sizeof(PatchEntry) * count       // used if process uses interposing or roots (cached dylib overrides)
+        interposeTuples  = 43,  // sizeof(InterposingTuple) * count
+   };
+
+    const void*     payload() const;
+    void*           payload();
+};
+
+
+//
+//  A TypedBytes which is a bag of other TypedBytes
+//
+struct VIS_HIDDEN ContainerTypedBytes : TypedBytes
+{
+    void                forEachAttribute(void (^callback)(const TypedBytes* typedBytes, bool& stop)) const;
+    void                forEachAttributePayload(Type requestedType, void (^handler)(const void* payload, uint32_t size, bool& stop)) const;
+    const void*         findAttributePayload(Type requestedType, uint32_t* payloadSize=nullptr) const;
+private:
+    const TypedBytes*   first() const;
+    const TypedBytes*   next(const TypedBytes*) const;
+};
+
+
+//
+//  Information about a mach-o file
+//
+struct VIS_HIDDEN Image : ContainerTypedBytes
+{
+    enum class LinkKind { regular=0, weak=1, upward=2, reExport=3 };
+
+    size_t              size() const;
+    ImageNum            imageNum() const;
+    bool                representsImageNum(ImageNum num) const;  // imageNum() or isOverrideOfDyldCacheImage()
+    uint32_t            maxLoadCount() const;
+    const char*         path() const;
+    const char*         leafName() const;
+    bool                getUuid(uuid_t) const;
+    bool                isInvalid() const;
+    bool                inDyldCache() const;
+    bool                hasObjC() const;
+    bool                hasInitializers() const;
+    bool                isBundle() const;
+    bool                isDylib() const;
+    bool                isExecutable() const;
+    bool                hasWeakDefs() const;
+    bool                mayHavePlusLoads() const;
+    bool                is64() const;
+    bool                neverUnload() const;
+    bool                cwdMustBeThisDir() const;
+    bool                isPlatformBinary() const;
+    bool                overridableDylib() const;
+    bool                hasFileModTimeAndInode(uint64_t& inode, uint64_t& mTime) const;
+    bool                hasCdHash(uint8_t cdHash[20]) const;
+    void                forEachAlias(void (^handler)(const char* aliasPath, bool& stop)) const;
+    void                forEachDependentImage(void (^handler)(uint32_t dependentIndex, LinkKind kind, ImageNum imageNum, bool& stop)) const;
+    ImageNum            dependentImageNum(uint32_t depIndex) const;
+    bool                containsAddress(const void* addr, const void* imageLoadAddress, uint8_t* permissions=nullptr) const;
+    void                forEachInitializer(const void* imageLoadAddress, void (^handler)(const void* initializer)) const;
+    void                forEachImageToInitBefore(void (^handler)(ImageNum imageToInit, bool& stop)) const;
+    void                forEachDOF(const void* imageLoadAddress, void (^handler)(const void* initializer)) const;
+    bool                hasPathWithHash(const char* path, uint32_t hash) const;
+    bool                isOverrideOfDyldCacheImage(ImageNum& cacheImageNum) const;
+    uint64_t            textSize() const;
+
+       union ResolvedSymbolTarget
+    {
+        enum Kinds { kindRebase, kindSharedCache, kindImage, kindAbsolute };
+
+        struct Rebase {
+            uint64_t    kind            :  2,       // kindRebase
+                        unused          : 62;       // all zeros
+        };
+        struct SharedCache {
+            uint64_t    kind            :  2,       // kindSharedCache
+                        offset          : 62;
+        };
+        struct Image {
+            uint64_t    kind            :  2,       // kindImage
+                        imageNum        : 22,       // ImageNum
+                        offset          : 40;
+        };
+        struct Absolute {
+            uint64_t    kind            :  2,       // kindAbsolute
+                        value           : 62;       // sign extended
+        };
+        Rebase          rebase;
+        SharedCache     sharedCache;
+        Image           image;
+        Absolute        absolute;
+        uint64_t        raw;
+
+        bool operator==(const ResolvedSymbolTarget& rhs) const {
+            return (raw == rhs.raw);
+        }
+        bool operator!=(const ResolvedSymbolTarget& rhs) const {
+            return (raw != rhs.raw);
+        }
+     };
+
+    typedef MachOLoaded::ChainedFixupPointerOnDisk ChainedFixupPointerOnDisk;
+
+    // the following are only valid if inDyldCache() returns true
+    uint32_t            cacheOffset() const;
+    uint32_t            patchStartIndex() const;
+    uint32_t            patchCount() const;
+    void                forEachCacheSegment(void (^handler)(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop)) const;
+
+
+    // the following are only valid if inDyldCache() returns false
+    uint64_t            vmSizeToMap() const;
+    uint64_t            sliceOffsetInFile() const;
+    bool                hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const;
+    bool                isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) 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                forEachFixup(void (^rebase)(uint64_t imageOffsetToRebase, bool& stop),
+                                     void (^bind)(uint64_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop),
+                                     void (^chainedFixupStart)(uint64_t imageOffsetStart, const Array<ResolvedSymbolTarget>& targets, bool& stop)) const;
+    void                forEachTextReloc(void (^rebase)(uint32_t imageOffsetToRebase, bool& stop),
+                                         void (^bind)(uint32_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop)) const;
+    static void         forEachChainedFixup(void* imageLoadAddress, uint64_t imageOffsetChainStart,
+                                        void (^chainedFixupStart)(uint64_t* fixupLoc, ChainedFixupPointerOnDisk fixupInfo, bool& stop));
+
+       static_assert(sizeof(ResolvedSymbolTarget) == 8, "Overflow in size of SymbolTargetLocation");
+
+    static uint32_t     hashFunction(const char*);
+
+
+       // only in Image for cached dylibs
+    struct PatchableExport
+    {
+        struct PatchLocation
+        {
+            uint64_t    cacheOffset             : 32,
+                        addend                  : 12,    // +/- 2048
+                        authenticated           : 1,
+                        usesAddressDiversity    : 1,
+                        key                     : 2,
+                        discriminator           : 16;
+
+                        PatchLocation(size_t cacheOffset, uint64_t addend);
+                        PatchLocation(size_t cacheOffset, uint64_t addend, dyld3::MachOLoaded::ChainedFixupPointerOnDisk authInfo);
+
+            uint64_t getAddend() const {
+                uint64_t unsingedAddend = addend;
+                int64_t signedAddend = (int64_t)unsingedAddend;
+                signedAddend = (signedAddend << 52) >> 52;
+                return (uint64_t)signedAddend;
+            }
+                        
+            const char* keyName() const;
+            bool operator==(const PatchLocation& other) const {
+                return this->cacheOffset == other.cacheOffset;
+            }
+        };
+
+        uint32_t       cacheOffsetOfImpl;
+        uint32_t       patchLocationsCount;
+        PatchLocation  patchLocations[];
+        // export name
+    };
+    uint32_t            patchableExportCount() const;
+    void                forEachPatchableExport(void (^handler)(uint32_t cacheOffsetOfImpl, const char* exportName)) const;
+    void                forEachPatchableUseOfExport(uint32_t cacheOffsetOfImpl, void (^handler)(PatchableExport::PatchLocation patchLocation)) const;
+
+private:
+    friend struct Closure;
+    friend class ImageWriter;
+    friend class ClosureBuilder;
+    friend class LaunchClosureWriter;
+
+    uint32_t             pageSize() const;
+
+    struct Flags
+    {
+        uint64_t        imageNum         : 16,
+                        maxLoadCount     : 12,
+                        isInvalid        : 1,       // an error occurred creating the info for this image
+                        has16KBpages     : 1,
+                        is64             : 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,
+                        isDylib          : 1,
+                        isExecutable     : 1,
+                        overridableDylib : 1,       // only applicable to cached dylibs
+                        inDyldCache      : 1,
+                        padding          : 21;
+    };
+
+    const Flags&        getFlags() const;
+
+    struct PathAndHash
+    {
+        uint32_t    hash;
+        char        path[];
+    };
+
+    // 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;
+    };
+
+    struct CodeSignatureLocation
+    {
+        uint32_t     fileOffset;
+        uint32_t     fileSize;
+    };
+
+    struct FileInfo
+    {
+        uint64_t     inode;
+        uint64_t     modTime;
+    };
+
+    struct FairPlayRange
+    {
+        uint32_t     textPageCount : 28,
+                     textStartPage : 4;
+    };
+
+    struct MappingInfo
+    {
+        uint32_t     totalVmPages;
+        uint32_t     sliceOffsetIn4K;
+    };
+
+    struct LinkedImage {
+
+        LinkedImage() : imgNum(0), linkKind(0) {
+        }
+        LinkedImage(LinkKind k, ImageNum num) : imgNum(num), linkKind((uint32_t)k) {
+            assert((num & 0xC0000000) == 0);
+        }
+
+        LinkKind    kind()  const      { return (LinkKind)linkKind; }
+        ImageNum    imageNum() const   { return imgNum; }
+        void        clearKind()        { linkKind = 0; }
+
+        bool operator==(const LinkedImage& rhs) const {
+            return (linkKind == rhs.linkKind) && (imgNum == rhs.imgNum);
+        }
+        bool operator!=(const LinkedImage& rhs) const {
+            return (linkKind != rhs.linkKind) || (imgNum != rhs.imgNum);
+        }
+       private:
+        uint32_t     imgNum         :  30,
+                     linkKind       :   2;     // LinkKind
+    };
+
+    const Array<LinkedImage> dependentsArray() const;
+
+    struct RebasePattern
+    {
+        uint32_t    repeatCount   : 20,     
+                    contigCount   :  8, // how many contiguous pointers neeed rebasing
+                    skipCount     :  4; // how many pointers to skip between contig groups
+        // If contigCount == 0, then there are no rebases for this entry,
+        // instead it advances the rebase location by repeatCount*skipCount.
+        // If all fields are zero, then the rebase position is reset to the start.
+        // This is to support old binaries with some non-monotonically-increasing rebases.
+    };
+    const Array<RebasePattern> rebaseFixups() const;
+
+    struct BindPattern
+    {
+        Image::ResolvedSymbolTarget     target;
+        uint64_t                        startVmOffset : 40, // max 1TB offset
+                                        skipCount     :  8, 
+                                        repeatCount   : 16;
+    };
+    const Array<BindPattern> bindFixups() const;
+
+    struct TextFixupPattern
+    {
+        Image::ResolvedSymbolTarget     target;
+        uint32_t                        startVmOffset;
+        uint16_t                        repeatCount;
+        uint16_t                        skipCount;
+    };
+    const Array<TextFixupPattern> textFixups() const;
+
+    // for use with chained fixups
+    const Array<uint64_t>                     chainedStarts() const;
+    const Array<Image::ResolvedSymbolTarget>  chainedTargets() const;
+
+};
+
+/*
+     Dyld cache patching notes:
+
+     The dyld cache needs to be patched to support interposing and dylib "roots".
+
+     For cached dylibs overrides:
+         Closure build time:
+             1) LoadedImages will contain the new dylib, so all symbol look ups
+                will naturally find new impl.  Only dyld cache needs special help.
+             2) LoadedImages entry will have flag for images that override cache.
+             3) When setting Closure attributes, if flag is set, builder will
+                iterate PatchableExport entries in Image* from cache and create
+                a PatchEntry for each.
+         Runtime:
+             1) [lib]dyld will iterate PatchEntry in closure and patch cache
+
+     For interposing:
+         Closure build time:
+             1) After Images built, if __interpose section(s) exist, builder will
+                build InterposingTuple entries for closure
+             2) For being-built closure and launch closure, apply any InterposingTuple
+                to modify Image fixups before Image finalized.
+             3) Builder will find PatchableExport entry that matchs stock Impl
+                and add PatchEntry to closure for it.
+         Runtime:
+             1) When closure is loaded (launch or dlopen) PatchEntries are
+                applied (exactly once) to whole cache.
+             2) For each DlopenClosure loaded, any InterposeTuples in *launch* closure
+                are applied to all new images in new DlopenClosure.
+
+     For weak-def coalesing:
+          Closure build time:
+             1) weak_bind entries are turned into -3 ordinal lookup which search through images
+                in load order for first def (like flat). This fixups up all images not in cache.
+             2) When processing -3 ordinals, it continues past first found and if any images
+                past it are in dyld cache and export that same symbol, a PatchEntry is added to
+                closure to fix up all cached uses of that symbol.
+             3) If a weak_bind has strong bit set (no fixup, just def), all images from the dyld
+                cache are checked to see if the export that symbol, if so, a PatchEntry is added
+                to the closure.
+          Runtime:
+             1) When closure is loaded (launch or dlopen) PatchEntries are
+                applied (exactly once) to whole cache.
+
+*/
+
+
+//
+// An array (accessible by index) list of Images
+//
+struct VIS_HIDDEN ImageArray : public TypedBytes
+{
+    size_t              size() const;
+    size_t              startImageNum() const;
+    uint32_t            imageCount() const;
+    void                forEachImage(void (^callback)(const Image* image, bool& stop)) const;
+    bool                hasPath(const char* path, ImageNum& num) const;
+    const Image*        imageForNum(ImageNum) const;
+
+    static const Image* findImage(const Array<const ImageArray*> imagesArrays, ImageNum imageNum);
+
+private:
+    friend class ImageArrayWriter;
+    
+    uint32_t        firstImageNum;
+    uint32_t        count;
+    uint32_t        offsets[];
+    // Image data
+};
+
+
+struct InterposingTuple
+{
+    Image::ResolvedSymbolTarget    stockImplementation;
+    Image::ResolvedSymbolTarget    newImplementation;
+};
+
+
+//
+// Describes how dyld should load a set of mach-o files
+//
+struct VIS_HIDDEN Closure : public ContainerTypedBytes
+{
+    size_t              size() const;
+    const ImageArray*   images() const;
+    ImageNum            topImage() const;
+    void                deallocate() const;
+
+    friend class ClosureWriter;
+
+    struct PatchEntry
+    {
+        ImageNum                    overriddenDylibInCache;
+        uint32_t                    exportCacheOffset;
+        Image::ResolvedSymbolTarget replacement;
+    };
+    void                forEachPatchEntry(void (^handler)(const PatchEntry& entry)) const;
+};
+
+
+//
+// Describes how dyld should launch a main executable
+//
+struct VIS_HIDDEN LaunchClosure : public Closure
+{
+    bool                builtAgainstDyldCache(uuid_t cacheUUID) const;
+    const char*         bootUUID() const;
+    void                forEachMustBeMissingFile(void (^handler)(const char* path, bool& stop)) const;
+    void                forEachEnvVar(void (^handler)(const char* keyEqualValue, bool& stop)) const;
+    ImageNum            libSystemImageNum() const;
+    void                libDyldEntry(Image::ResolvedSymbolTarget& loc) const;
+    bool                mainEntry(Image::ResolvedSymbolTarget& mainLoc) const;
+    bool                startEntry(Image::ResolvedSymbolTarget& startLoc) const;
+    uint32_t            initialLoadCount() const;
+    void                forEachInterposingTuple(void (^handler)(const InterposingTuple& tuple, bool& stop)) const;
+    bool                usedAtPaths() const;
+    bool                usedFallbackPaths() const;
+
+
+private:
+    friend class LaunchClosureWriter;
+
+    struct Flags
+    {
+        uint32_t        usedAtPaths              :  1,
+                        usedFallbackPaths        :  1,
+                        initImageCount           : 16,
+                        padding                  : 14;
+    };
+    const Flags&        getFlags() const;
+};
+
+
+
+//
+// Describes how dyld should dlopen() a mach-o file
+//
+struct VIS_HIDDEN DlopenClosure : public Closure
+{
+
+};
+
+
+
+} //  namespace closure
+} //  namespace dyld3
+
+
+#endif // Closures_h
+
+
diff --git a/dyld3/ClosureBuffer.cpp b/dyld3/ClosureBuffer.cpp
deleted file mode 100644 (file)
index 0231827..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * 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
deleted file mode 100644 (file)
index c1ab2c3..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * 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/ClosureBuilder.cpp b/dyld3/ClosureBuilder.cpp
new file mode 100644 (file)
index 0000000..e16760e
--- /dev/null
@@ -0,0 +1,2278 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+ #include <sys/types.h>
+ #include <sys/sysctl.h>
+
+#include "mach-o/dyld_priv.h"
+
+#include "ClosureWriter.h"
+#include "ClosureBuilder.h"
+#include "MachOAnalyzer.h"
+#include "libdyldEntryVector.h"
+#include "Tracing.h"
+
+namespace dyld3 {
+namespace closure {
+
+const DlopenClosure* ClosureBuilder::sRetryDlopenClosure = (const DlopenClosure*)(-1);
+
+ClosureBuilder::ClosureBuilder(uint32_t startImageNum, const FileSystem& fileSystem, const DyldSharedCache* dyldCache, bool dyldCacheIsLive,
+                               const PathOverrides& pathOverrides, AtPath atPathHandling, LaunchErrorInfo* errorInfo,
+                               const char* archName, Platform platform,
+                               const CacheDylibsBindingHandlers* handlers)
+    : _fileSystem(fileSystem), _dyldCache(dyldCache), _pathOverrides(pathOverrides), _archName(archName), _platform(platform), _startImageNum(startImageNum),
+      _handlers(handlers), _atPathHandling(atPathHandling), _launchErrorInfo(errorInfo), _dyldCacheIsLive(dyldCacheIsLive)
+{
+    if ( dyldCache != nullptr ) {
+        _dyldImageArray = dyldCache->cachedDylibsImageArray();
+        if ( (dyldCache->header.otherImageArrayAddr != 0) && (dyldCache->header.progClosuresSize == 0) )
+            _makingClosuresInCache = true;
+    }
+}
+
+
+ClosureBuilder::~ClosureBuilder() {
+    if ( _tempPaths != nullptr )
+        PathPool::deallocate(_tempPaths);
+    if ( _mustBeMissingPaths != nullptr )
+        PathPool::deallocate(_mustBeMissingPaths);
+}
+
+bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& forImageChain, BuilderLoadedImage*& foundImage, bool staticLinkage, bool allowOther)
+{
+    __block bool result = false;
+
+    _pathOverrides.forEachPathVariant(loadPath, ^(const char* possiblePath, bool isFallbackPath, bool& stop) {
+        bool                  unmapWhenDone    = false;
+        bool                  contentRebased   = false;
+        bool                  hasInits         = false;
+        bool                  fileFound        = false;
+        bool                  markNeverUnload  = staticLinkage ? forImageChain.image.markNeverUnload : false;
+        ImageNum              overrideImageNum = 0;
+        ImageNum              foundImageNum    = 0;
+        const MachOAnalyzer*  mh               = nullptr;
+        const char*           filePath         = nullptr;
+        LoadedFileInfo        loadedFileInfo;
+
+        // This check is within forEachPathVariant() to let DYLD_LIBRARY_PATH override LC_RPATH
+        bool isRPath = (strncmp(possiblePath, "@rpath/", 7) == 0);
+
+        // passing a leaf name to dlopen() allows rpath searching for it
+        bool implictRPath = !staticLinkage && (loadPath[0] != '/') && (loadPath == possiblePath) && (_atPathHandling != AtPath::none);
+
+        // expand @ paths
+        const char* prePathVarExpansion = possiblePath;
+        possiblePath = resolvePathVar(possiblePath, forImageChain, implictRPath);
+        if ( prePathVarExpansion != possiblePath )
+            _atPathUsed = true;
+
+        // look at already loaded images
+        const char* leafName = strrchr(possiblePath, '/');
+        for (BuilderLoadedImage& li: _loadedImages) {
+            if ( strcmp(li.path(), possiblePath) == 0 ) {
+                foundImage = &li;
+                result = true;
+                stop = true;
+                return;
+            }
+            else if ( isRPath ) {
+                // Special case @rpath/ because name in li.fileInfo.path is full path.
+                // Getting installName is expensive, so first see if an already loaded image
+                // has same leaf name and if so see if its installName matches request @rpath
+                if (const char* aLeaf = strrchr(li.path(), '/')) {
+                    if ( strcmp(aLeaf, leafName) == 0 ) {
+                        if ( li.loadAddress()->isDylib() && (strcmp(loadPath, li.loadAddress()->installName()) == 0) ) {
+                            foundImage = &li;
+                            result = true;
+                            stop = true;
+                            return;
+                        }
+                    }
+                }
+            }
+        }
+
+        // look to see if image already loaded via a different symlink
+        if ( _fileSystem.fileExists(possiblePath, &loadedFileInfo.inode, &loadedFileInfo.mtime) ) {
+            fileFound = true;
+            for (BuilderLoadedImage& li: _loadedImages) {
+                if ( (li.loadedFileInfo.inode == loadedFileInfo.inode) && (li.loadedFileInfo.mtime == loadedFileInfo.mtime) )  {
+                    foundImage = &li;
+                    result = true;
+                    stop = true;
+                    return;
+                }
+            }
+        }
+
+        // look in dyld cache
+        filePath = possiblePath;
+        char realPath[MAXPATHLEN];
+        if ( _dyldImageArray != nullptr && (_dyldCache->header.formatVersion == dyld3::closure::kFormatVersion) ) {
+            uint32_t dyldCacheImageIndex;
+            bool foundInCache =  _dyldCache->hasImagePath(possiblePath, dyldCacheImageIndex);
+            if ( !foundInCache && fileFound ) {
+                // see if this is an OS dylib/bundle with a pre-built dlopen closure
+                if ( allowOther ) {
+                    if (const dyld3::closure::Image* otherImage = _dyldCache->findDlopenOtherImage(possiblePath) ) {
+                        uint64_t expectedInode;
+                        uint64_t expectedModTime;
+                        if ( !otherImage->isInvalid()  ) {
+                            bool hasInodeInfo = otherImage->hasFileModTimeAndInode(expectedInode, expectedModTime);
+                            // use pre-built Image if it does not have mtime/inode or it does and it has matches current file info
+                            if ( !hasInodeInfo || ((expectedInode == loadedFileInfo.inode) && (expectedModTime == loadedFileInfo.mtime)) ) {
+                                loadedFileInfo = MachOAnalyzer::load(_diag, _fileSystem, possiblePath, _archName, _platform);
+                                if ( _diag.noError() ) {
+                                    mh = (const MachOAnalyzer*)loadedFileInfo.fileContent;
+                                    foundImageNum = otherImage->imageNum();
+                                    unmapWhenDone = true;
+                                    contentRebased = false;
+                                    hasInits = otherImage->hasInitializers() || otherImage->mayHavePlusLoads();
+                                }
+                            }
+                        }
+                    }
+                }
+                // if not found in cache, may be a symlink to something in cache
+                if ( mh == nullptr ) {
+                    if ( _fileSystem.getRealPath(possiblePath, realPath) ) {
+                        foundInCache = _dyldCache->hasImagePath(realPath, dyldCacheImageIndex);
+                        if ( foundInCache ) {
+                            filePath = realPath;
+    #if BUILDING_LIBDYLD
+                            // handle case where OS dylib was updated after this process launched
+                            if ( foundInCache ) {
+                                for (BuilderLoadedImage& li: _loadedImages) {
+                                    if ( strcmp(li.path(), realPath) == 0 ) {
+                                        foundImage = &li;
+                                        result = true;
+                                        stop = true;
+                                        return;
+                                    }
+                                }
+                            }
+    #endif
+                        }
+                    }
+                }
+            }
+
+            // if using a cached dylib, look to see if there is an override
+            if ( foundInCache ) {
+                ImageNum dyldCacheImageNum = dyldCacheImageIndex + 1;
+                bool useCache = true;
+                markNeverUnload = true; // dylibs in cache, or dylibs that override cache should not be unloaded at runtime
+                const Image* image = _dyldImageArray->imageForNum(dyldCacheImageNum);
+                if ( image->overridableDylib() ) {
+                    if ( fileFound && (_platform == MachOFile::currentPlatform()) ) {
+                        uint64_t expectedInode;
+                        uint64_t expectedModTime;
+                        if ( image->hasFileModTimeAndInode(expectedInode, expectedModTime) ) {
+                            // macOS where dylibs remain on disk.  only use cache if mtime and inode have not changed
+                            useCache = ( (loadedFileInfo.inode == expectedInode) && (loadedFileInfo.mtime == expectedModTime) );
+                        }
+                        else if ( _makingClosuresInCache ) {
+                            // during iOS cache build, don't look at files on disk, use ones in cache
+                            useCache = true;
+                        }
+                        else {
+                            // iOS internal build. Any disk on cache overrides cache
+                            useCache = false;
+                        }
+                    }
+                    if ( !useCache )
+                        overrideImageNum = dyldCacheImageNum;
+                }
+                if ( useCache ) {
+                    foundImageNum = dyldCacheImageNum;
+                    mh = (MachOAnalyzer*)_dyldCache->getIndexedImageEntry(foundImageNum-1, loadedFileInfo.mtime, loadedFileInfo.inode);
+                    unmapWhenDone = false;
+                    // if we are building ImageArray in dyld cache, content is not rebased
+                    contentRebased = !_makingDyldCacheImages && _dyldCacheIsLive;
+                    hasInits = image->hasInitializers() || image->mayHavePlusLoads();
+                }
+            }
+        }
+
+        // If we are building the cache, and don't find an image, then it might be weak so just return
+        if (_makingDyldCacheImages) {
+            addMustBeMissingPath(possiblePath);
+            return;
+        }
+
+         // if not found yet, mmap file
+        if ( mh == nullptr ) {
+            loadedFileInfo = MachOAnalyzer::load(_diag, _fileSystem, filePath, _archName, _platform);
+            mh = (const MachOAnalyzer*)loadedFileInfo.fileContent;
+            if ( mh == nullptr ) {
+                // Don't add must be missing paths for dlopen as we don't cache dlopen closures
+                if (_isLaunchClosure) {
+                    addMustBeMissingPath(possiblePath);
+                }
+                return;
+            }
+            if ( staticLinkage ) {
+                // LC_LOAD_DYLIB can only link with dylibs
+                if ( !mh->isDylib() ) {
+                    _diag.error("not a dylib");
+                    return;
+                }
+            }
+            else if ( mh->isMainExecutable() ) {
+                // when dlopen()ing a main executable, it must be dynamic Position Independent Executable
+                if ( !mh->isPIE() || !mh->isDynamicExecutable() ) {
+                    _diag.error("not PIE");
+                    return;
+                }
+            }
+            foundImageNum = _startImageNum + _nextIndex++;
+            unmapWhenDone = true;
+        } else {
+            loadedFileInfo.fileContent = mh;
+        }
+
+        // if path is not original path
+        if ( filePath != loadPath ) {
+            // possiblePath may be a temporary (stack) string, since we found file at that path, make it permanent
+            filePath = strdup_temp(filePath);
+            // check if this overrides what would have been found in cache
+            if ( overrideImageNum == 0 ) {
+                if ( _dyldImageArray != nullptr )  {
+                    uint32_t dyldCacheImageIndex;
+                    if ( _dyldCache->hasImagePath(loadPath, dyldCacheImageIndex) ) {
+                        ImageNum possibleOverrideNum = dyldCacheImageIndex+1;
+                        if ( possibleOverrideNum != foundImageNum )
+                            overrideImageNum = possibleOverrideNum;
+                    }
+                }
+            }
+        }
+
+        if ( !markNeverUnload ) {
+            // If the parent didn't force us to be never unload, other conditions still may
+            if ( mh->hasThreadLocalVariables() ) {
+                markNeverUnload = true;
+            } else if ( mh->hasObjC() && mh->isDylib() ) {
+                markNeverUnload = true;
+            } else {
+                // record if image has DOF sections
+                __block bool hasDOFs = false;
+                mh->forEachDOFSection(_diag, ^(uint32_t offset) {
+                    hasDOFs = true;
+                });
+                if ( hasDOFs )
+                    markNeverUnload = true;
+            }
+        }
+
+        // Set the path again just in case it was strdup'ed.
+        loadedFileInfo.path = filePath;
+
+        // add new entry
+        BuilderLoadedImage entry;
+        entry.loadedFileInfo   = loadedFileInfo;
+        entry.imageNum         = foundImageNum;
+        entry.unmapWhenDone    = unmapWhenDone;
+        entry.contentRebased   = contentRebased;
+        entry.hasInits         = hasInits;
+        entry.markNeverUnload  = markNeverUnload;
+        entry.rtldLocal        = false;
+        entry.isBadImage       = false;
+        entry.overrideImageNum = overrideImageNum;
+        _loadedImages.push_back(entry);
+        foundImage = &_loadedImages.back();
+        if ( isFallbackPath )
+            _fallbackPathUsed = true;
+        stop = true;
+        result = true;
+    }, _platform);
+
+    return result;
+}
+
+bool ClosureBuilder::expandAtLoaderPath(const char* loadPath, bool fromLCRPATH, const BuilderLoadedImage& loadedImage, char fixedPath[])
+{
+    switch ( _atPathHandling ) {
+        case AtPath::none:
+            return false;
+        case AtPath::onlyInRPaths:
+            if ( !fromLCRPATH ) {
+                // <rdar://42360708> allow @loader_path in LC_LOAD_DYLIB during dlopen()
+                if ( _isLaunchClosure )
+                    return false;
+            }
+            break;
+        case AtPath::all:
+            break;
+    }
+    if ( strncmp(loadPath, "@loader_path/", 13) != 0 )
+        return false;
+
+    strlcpy(fixedPath, loadedImage.path(), PATH_MAX);
+    char* lastSlash = strrchr(fixedPath, '/');
+    if ( lastSlash != nullptr ) {
+        strcpy(lastSlash+1, &loadPath[13]);
+        return true;
+    }
+    return false;
+}
+
+bool ClosureBuilder::expandAtExecutablePath(const char* loadPath, bool fromLCRPATH, char fixedPath[])
+{
+    switch ( _atPathHandling ) {
+        case AtPath::none:
+            return false;
+        case AtPath::onlyInRPaths:
+            if ( !fromLCRPATH )
+                return false;
+            break;
+        case AtPath::all:
+            break;
+    }
+    if ( strncmp(loadPath, "@executable_path/", 17) != 0 )
+        return false;
+
+    if ( _atPathHandling != AtPath::all )
+        return false;
+
+    strlcpy(fixedPath, _loadedImages[_mainProgLoadIndex].path(), PATH_MAX);
+    char* lastSlash = strrchr(fixedPath, '/');
+    if ( lastSlash != nullptr ) {
+        strcpy(lastSlash+1, &loadPath[17]);
+        return true;
+    }
+    return false;
+}
+
+const char* ClosureBuilder::resolvePathVar(const char* loadPath, const LoadedImageChain& forImageChain, bool implictRPath)
+{
+    // don't expand @ path if disallowed
+    if ( (_atPathHandling == AtPath::none) && (loadPath[0] == '@') )
+        return loadPath;
+
+    // quick out if not @ path or not implicit rpath
+    if ( !implictRPath && (loadPath[0] != '@') )
+        return loadPath;
+
+    // expand @loader_path
+    BLOCK_ACCCESSIBLE_ARRAY(char, tempPath, PATH_MAX);  // read as:  char tempPath[PATH_MAX];
+    if ( expandAtLoaderPath(loadPath, false, forImageChain.image, tempPath) )
+        return strdup_temp(tempPath);
+
+    // expand @executable_path
+    if ( expandAtExecutablePath(loadPath, false, tempPath) )
+        return strdup_temp(tempPath);
+
+    // expand @rpath
+    const char* rpathTail = nullptr;
+    char implicitRpathBuffer[PATH_MAX];
+    if ( strncmp(loadPath, "@rpath/", 7) == 0 ) {
+        // note: rpathTail starts with '/'
+        rpathTail = &loadPath[6];
+    }
+    else if ( implictRPath ) {
+        // make rpathTail starts with '/'
+        strlcpy(implicitRpathBuffer, "/", PATH_MAX);
+        strlcat(implicitRpathBuffer, loadPath, PATH_MAX);
+        rpathTail = implicitRpathBuffer;
+    }
+    if ( rpathTail != nullptr ) {
+        // rpath is expansion is technically a stack of rpath dirs built starting with main executable and pushing
+        // LC_RPATHS from each dylib as they are recursively loaded.  Our imageChain represents that stack.
+        __block const char* result = nullptr;
+        for (const LoadedImageChain* link = &forImageChain; (link != nullptr) && (result == nullptr); link = link->previous) {
+            link->image.loadAddress()->forEachRPath(^(const char* rPath, bool& stop) {
+                // fprintf(stderr, "LC_RPATH %s from %s\n", rPath, link->image.fileInfo.path);
+                if ( expandAtLoaderPath(rPath, true, link->image, tempPath) || expandAtExecutablePath(rPath, true, tempPath) ) {
+                    strlcat(tempPath, rpathTail, PATH_MAX);
+                }
+                else {
+                    strlcpy(tempPath, rPath, PATH_MAX);
+                    strlcat(tempPath, rpathTail, PATH_MAX);
+                }
+                if ( _fileSystem.fileExists(tempPath) ) {
+                    stop = true;
+                    result = strdup_temp(tempPath);
+                }
+                else {
+                    // Don't add must be missing paths for dlopen as we don't cache dlopen closures
+                    if (_isLaunchClosure) {
+                        addMustBeMissingPath(tempPath);
+                    }
+                }
+            });
+        }
+        if ( result != nullptr )
+            return result;
+    }
+
+    return loadPath;
+}
+
+const char* ClosureBuilder::strdup_temp(const char* path)
+{
+    if ( _tempPaths == nullptr )
+        _tempPaths = PathPool::allocate();
+    return _tempPaths->add(path);
+}
+
+void ClosureBuilder::addMustBeMissingPath(const char* path)
+{
+    //fprintf(stderr, "must be missing: %s\n", path);
+    if ( _mustBeMissingPaths == nullptr )
+        _mustBeMissingPaths = PathPool::allocate();
+    _mustBeMissingPaths->add(path);
+}
+
+ClosureBuilder::BuilderLoadedImage& ClosureBuilder::findLoadedImage(ImageNum imageNum)
+{
+    for (BuilderLoadedImage& li : _loadedImages) {
+        if ( li.imageNum == imageNum ) {
+            return li;
+        }
+    }
+    for (BuilderLoadedImage& li : _loadedImages) {
+        if ( li.overrideImageNum == imageNum ) {
+            return li;
+        }
+    }
+    assert(0 && "LoadedImage not found");
+}
+
+ClosureBuilder::BuilderLoadedImage& ClosureBuilder::findLoadedImage(const MachOAnalyzer* mh)
+{
+    for (BuilderLoadedImage& li : _loadedImages) {
+        if ( li.loadAddress() == mh ) {
+             return li;
+        }
+    }
+    assert(0 && "LoadedImage not found");
+}
+
+const MachOAnalyzer* ClosureBuilder::machOForImageNum(ImageNum imageNum)
+{
+    return findLoadedImage(imageNum).loadAddress();
+}
+
+const MachOAnalyzer* ClosureBuilder::findDependent(const MachOLoaded* mh, uint32_t depIndex)
+{
+    for (const BuilderLoadedImage& li : _loadedImages) {
+        if ( li.loadAddress() == mh ) {
+            if (li.isBadImage) {
+                // Bad image duting building group 1 closures, so the dependents array
+                // is potentially incomplete.
+                return nullptr;
+            }
+            ImageNum childNum = li.dependents[depIndex].imageNum();
+            return machOForImageNum(childNum);
+        }
+    }
+    return nullptr;
+}
+
+ImageNum ClosureBuilder::imageNumForMachO(const MachOAnalyzer* mh)
+{
+    for (const BuilderLoadedImage& li : _loadedImages) {
+        if ( li.loadAddress() == mh ) {
+             return li.imageNum;
+        }
+    }
+    assert(0 && "unknown mach-o");
+    return 0;
+}
+
+void ClosureBuilder::recursiveLoadDependents(LoadedImageChain& forImageChain)
+{
+    // if dependents is set, then we have already loaded this
+    if ( forImageChain.image.dependents.begin() != nullptr )
+        return;
+
+    uintptr_t startDepIndex = _dependencies.count();
+    // add dependents
+    __block uint32_t depIndex = 0;
+    forImageChain.image.loadAddress()->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+        Image::LinkKind kind = Image::LinkKind::regular;
+        if ( isWeak )
+            kind = Image::LinkKind::weak;
+        else if ( isReExport )
+            kind = Image::LinkKind::reExport;
+        else if ( isUpward )
+            kind = Image::LinkKind::upward;
+        BuilderLoadedImage* foundImage;
+        if ( findImage(loadPath, forImageChain, foundImage, true, false) ) {
+            // verify this is compatable dylib version
+            if ( foundImage->loadAddress()->filetype != MH_DYLIB ) {
+                _diag.error("found '%s' which is not a dylib.  Needed by '%s'", foundImage->path(), forImageChain.image.path());
+            }
+            else {
+                const char* installName;
+                uint32_t    foundCompatVers;
+                uint32_t    foundCurrentVers;
+                foundImage->loadAddress()->getDylibInstallName(&installName, &foundCompatVers, &foundCurrentVers);
+                if ( (foundCompatVers < compatVersion) && foundImage->loadAddress()->enforceCompatVersion() ) {
+                    char foundStr[32];
+                    char requiredStr[32];
+                    MachOFile::packedVersionToString(foundCompatVers, foundStr);
+                    MachOFile::packedVersionToString(compatVersion, requiredStr);
+                    _diag.error("found '%s' which has compat version (%s) which is less than required (%s).  Needed by '%s'",
+                                foundImage->path(), foundStr, requiredStr, forImageChain.image.path());
+                }
+            }
+            if ( _diag.noError() )
+                _dependencies.push_back(Image::LinkedImage(kind, foundImage->imageNum));
+        }
+        else if ( isWeak ) {
+            _dependencies.push_back(Image::LinkedImage(Image::LinkKind::weak, kMissingWeakLinkedImage));
+        }
+        else {
+            BLOCK_ACCCESSIBLE_ARRAY(char, extra, 4096);
+            extra[0] = '\0';
+            const char* targetLeaf = strrchr(loadPath, '/');
+            if ( targetLeaf == nullptr )
+                targetLeaf = loadPath;
+            if ( _mustBeMissingPaths != nullptr ) {
+                strcpy(extra, ", tried: ");
+                _mustBeMissingPaths->forEachPath(^(const char* aPath) {
+                    const char* aLeaf = strrchr(aPath, '/');
+                    if ( aLeaf == nullptr )
+                        aLeaf = aPath;
+                  if ( strcmp(targetLeaf, aLeaf) == 0 ) {
+                        strlcat(extra, "'", 4096);
+                        strlcat(extra, aPath, 4096);
+                        strlcat(extra, "' ", 4096);
+                    }
+                });
+            }
+            if ( _diag.hasError() ) {
+        #if BUILDING_CACHE_BUILDER
+                std::string errorMessageBuffer = _diag.errorMessage();
+                const char* msg = errorMessageBuffer.c_str();
+        #else
+                const char* msg = _diag.errorMessage();
+        #endif
+                char msgCopy[strlen(msg)+4];
+                strcpy(msgCopy, msg);
+                _diag.error("dependent dylib '%s' not found for '%s'. %s", loadPath, forImageChain.image.path(), msgCopy);
+            }
+            else {
+                _diag.error("dependent dylib '%s' not found for '%s'%s", loadPath, forImageChain.image.path(), extra);
+            }
+            if ( _launchErrorInfo != nullptr ) {
+                _launchErrorInfo->kind              = DYLD_EXIT_REASON_DYLIB_MISSING;
+                _launchErrorInfo->clientOfDylibPath = forImageChain.image.path();
+                _launchErrorInfo->targetDylibPath   = loadPath;
+                _launchErrorInfo->symbol            = nullptr;
+           }
+        }
+        ++depIndex;
+        if ( _diag.hasError() )
+            stop = true;
+    });
+    if ( _diag.hasError() )
+        return;
+    forImageChain.image.dependents = _dependencies.subArray(startDepIndex, depIndex);
+
+    // breadth first recurse
+    for (Image::LinkedImage dep : forImageChain.image.dependents) {
+        // don't recurse upwards
+        if ( dep.kind() == Image::LinkKind::upward )
+            continue;
+        // don't recurse down missing weak links
+        if ( (dep.kind() == Image::LinkKind::weak) && (dep.imageNum() == kMissingWeakLinkedImage) )
+            continue;
+        BuilderLoadedImage& depLoadedImage = findLoadedImage(dep.imageNum());
+        LoadedImageChain chain = { &forImageChain, depLoadedImage };
+        recursiveLoadDependents(chain);
+        if ( _diag.hasError() )
+            break;
+    }
+}
+
+void ClosureBuilder::loadDanglingUpwardLinks()
+{
+    bool danglingFixed;
+    do {
+        danglingFixed = false;
+        for (BuilderLoadedImage& li : _loadedImages) {
+            if ( li.dependents.begin() == nullptr ) {
+                // this image has not have dependents set (probably a dangling upward link or referenced by upward link)
+                LoadedImageChain chain = { nullptr, li };
+                recursiveLoadDependents(chain);
+                danglingFixed = true;
+                break;
+            }
+        }
+    } while (danglingFixed && _diag.noError());
+}
+
+bool ClosureBuilder::overridableDylib(const BuilderLoadedImage& forImage)
+{
+    // only set on dylibs in the dyld shared cache
+    if ( !_makingDyldCacheImages )
+        return false;
+
+    // on macOS dylibs always override cache
+    if ( _platform == Platform::macOS )
+        return true;
+
+    // on embedded platforms with Internal cache, allow overrides
+    if ( !_makingCustomerCache )
+        return true;
+
+    // embedded platform customer caches, no overrides
+    return false;  // FIXME, allow libdispatch.dylib to be overridden
+}
+
+void ClosureBuilder::buildImage(ImageWriter& writer, BuilderLoadedImage& forImage)
+{
+    const MachOAnalyzer* macho = forImage.loadAddress();
+       // set ImageNum
+    writer.setImageNum(forImage.imageNum);
+
+    // set flags
+    writer.setHasWeakDefs(macho->hasWeakDefs());
+    writer.setIsBundle(macho->isBundle());
+    writer.setIsDylib(macho->isDylib());
+    writer.setIs64(macho->is64());
+    writer.setIsExecutable(macho->isMainExecutable());
+    writer.setUses16KPages(macho->uses16KPages());
+    writer.setOverridableDylib(overridableDylib(forImage));
+    writer.setInDyldCache(macho->inDyldCache());
+    if ( macho->hasObjC() ) {
+        writer.setHasObjC(true);
+        bool hasPlusLoads = macho->hasPlusLoadMethod(_diag);
+        writer.setHasPlusLoads(hasPlusLoads);
+        if ( hasPlusLoads )
+            forImage.hasInits = true;
+    }
+    else {
+        writer.setHasObjC(false);
+        writer.setHasPlusLoads(false);
+    }
+
+    if ( forImage.markNeverUnload ) {
+        writer.setNeverUnload(true);
+    }
+
+#if BUILDING_DYLD || BUILDING_LIBDYLD
+    // shared cache not built by dyld or libdyld.dylib, so must be real file
+    writer.setFileInfo(forImage.loadedFileInfo.inode, forImage.loadedFileInfo.mtime);
+#else
+    if ( _platform == Platform::macOS ) {
+        if ( macho->inDyldCache() && !_dyldCache->header.dylibsExpectedOnDisk ) {
+            // don't add file info for shared cache files mastered out of final file system
+        }
+        else {
+            // file is either not in cache or is in cache but not mastered out
+            writer.setFileInfo(forImage.loadedFileInfo.inode, forImage.loadedFileInfo.mtime);
+        }
+    }
+    else {
+        // all other platforms, cache is built off-device, so inodes are not known
+    }
+#endif
+
+    // add info on how to load image
+    if ( !macho->inDyldCache() ) {
+        writer.setMappingInfo(forImage.loadedFileInfo.sliceOffset, macho->mappedSize());
+        // add code signature, if signed
+        uint32_t codeSigFileOffset;
+        uint32_t codeSigSize;
+        if ( macho->hasCodeSignature(codeSigFileOffset, codeSigSize) ) {
+            writer.setCodeSignatureLocation(codeSigFileOffset, codeSigSize);
+            uint8_t cdHash[20];
+            if ( macho->getCDHash(cdHash) )
+                writer.setCDHash(cdHash);
+        }
+        // add FairPlay encryption range if encrypted
+        uint32_t fairPlayFileOffset;
+        uint32_t fairPlaySize;
+        if ( macho->isFairPlayEncrypted(fairPlayFileOffset, fairPlaySize) ) {
+            writer.setFairPlayEncryptionRange(fairPlayFileOffset, fairPlaySize);
+        }
+    }
+
+    // set path
+    writer.addPath(forImage.path());
+    if ( _aliases != nullptr ) {
+        for (const CachedDylibAlias& alias : *_aliases) {
+            if ( strcmp(alias.realPath, forImage.path()) == 0 )
+                writer.addPath(alias.aliasPath);
+        }
+    }
+
+    // set uuid, if has one
+    uuid_t uuid;
+    if ( macho->getUuid(uuid) )
+        writer.setUUID(uuid);
+
+    // set dependents
+    writer.setDependents(forImage.dependents);
+
+    // set segments
+    addSegments(writer, macho);
+
+    // record if this dylib overrides something in the cache
+    if ( forImage.overrideImageNum != 0 ) {
+        writer.setAsOverrideOf(forImage.overrideImageNum);
+        const char* overridePath = _dyldImageArray->imageForNum(forImage.overrideImageNum)->path();
+        writer.addPath(overridePath);
+        if ( strcmp(overridePath, "/usr/lib/system/libdyld.dylib") == 0 )
+            _libDyldImageNum = forImage.imageNum;
+        else if ( strcmp(overridePath, "/usr/lib/libSystem.B.dylib") == 0 )
+            _libSystemImageNum = forImage.imageNum;
+    }
+
+
+    // do fix up info for non-cached, and cached if building cache
+    if ( !macho->inDyldCache() || _makingDyldCacheImages ) {
+        if ( macho->hasChainedFixups() ) {
+            addChainedFixupInfo(writer, forImage);
+        }
+        else {
+            if ( _handlers != nullptr ) {
+                reportRebasesAndBinds(writer, forImage);
+            }
+            else {
+                addRebaseInfo(writer, macho);
+                if ( _diag.noError() )
+                    addBindInfo(writer, forImage);
+            }
+        }
+    }
+    if ( _diag.hasError() ) {
+        writer.setInvalid();
+        return;
+    }
+
+    // add initializers
+    bool contentRebased = forImage.contentRebased;
+    __block unsigned initCount = 0;
+    macho->forEachInitializer(_diag, contentRebased, ^(uint32_t offset) {
+        ++initCount;
+    }, _dyldCache);
+    if ( initCount != 0 ) {
+        BLOCK_ACCCESSIBLE_ARRAY(uint32_t, initOffsets, initCount);
+        __block unsigned index = 0;
+       macho->forEachInitializer(_diag, contentRebased, ^(uint32_t offset) {
+            initOffsets[index++] = offset;
+        }, _dyldCache);
+        writer.setInitOffsets(initOffsets, initCount);
+        forImage.hasInits = true;
+    }
+
+    // record if image has DOF sections
+    STACK_ALLOC_ARRAY(uint32_t, dofSectionOffsets, 256);
+    macho->forEachDOFSection(_diag, ^(uint32_t offset) {
+        dofSectionOffsets.push_back(offset);
+    });
+    if ( !dofSectionOffsets.empty() ) {
+        writer.setDofOffsets(dofSectionOffsets);
+    }
+
+}
+
+void ClosureBuilder::addSegments(ImageWriter& writer, const MachOAnalyzer* mh)
+{
+    const uint32_t segCount = mh->segmentCount();
+    if ( mh->inDyldCache() ) {
+        uint64_t cacheUnslideBaseAddress = _dyldCache->unslidLoadAddress();
+        BLOCK_ACCCESSIBLE_ARRAY(Image::DyldCacheSegment, segs, segCount);
+        mh->forEachSegment(^(const MachOAnalyzer::SegmentInfo& info, bool& stop) {
+            segs[info.segIndex] = { (uint32_t)(info.vmAddr-cacheUnslideBaseAddress), (uint32_t)info.vmSize, info.protections };
+        });
+        writer.setCachedSegments(segs, segCount);
+    }
+    else {
+        const uint32_t   pageSize          = (mh->uses16KPages() ? 0x4000 : 0x1000);
+        __block uint32_t diskSegIndex      = 0;
+        __block uint32_t totalPageCount    = 0;
+        __block uint32_t lastFileOffsetEnd = 0;
+        __block uint64_t lastVmAddrEnd     = 0;
+        BLOCK_ACCCESSIBLE_ARRAY(Image::DiskSegment, dsegs, segCount*3); // room for padding
+        mh->forEachSegment(^(const MachOAnalyzer::SegmentInfo& info, bool& stop) {
+            if ( (info.fileOffset != 0) && (info.fileOffset != lastFileOffsetEnd) ) {
+                Image::DiskSegment filePadding;
+                filePadding.filePageCount   = (info.fileOffset - lastFileOffsetEnd)/pageSize;
+                filePadding.vmPageCount     = 0;
+                filePadding.permissions     = 0;
+                filePadding.paddingNotSeg   = 1;
+                dsegs[diskSegIndex++] = filePadding;
+            }
+            if ( (lastVmAddrEnd != 0) && (info.vmAddr != lastVmAddrEnd) ) {
+                Image::DiskSegment vmPadding;
+                vmPadding.filePageCount   = 0;
+                vmPadding.vmPageCount     = (info.vmAddr - lastVmAddrEnd)/pageSize;
+                vmPadding.permissions     = 0;
+                vmPadding.paddingNotSeg   = 1;
+                dsegs[diskSegIndex++] = vmPadding;
+                totalPageCount += vmPadding.vmPageCount;
+            }
+            {
+                Image::DiskSegment segInfo;
+                segInfo.filePageCount   = (info.fileSize+pageSize-1)/pageSize;
+                segInfo.vmPageCount     = (info.vmSize+pageSize-1)/pageSize;
+                segInfo.permissions     = info.protections & 7;
+                segInfo.paddingNotSeg   = 0;
+                dsegs[diskSegIndex++] = segInfo;
+                totalPageCount   += segInfo.vmPageCount;
+                if ( info.fileSize != 0 )
+                    lastFileOffsetEnd = (uint32_t)(info.fileOffset + info.fileSize);
+                if ( info.vmSize != 0 )
+                    lastVmAddrEnd     = info.vmAddr + info.vmSize;
+            }
+        });
+        writer.setDiskSegments(dsegs, diskSegIndex);
+    }
+}
+
+void ClosureBuilder::addInterposingTuples(LaunchClosureWriter& writer, const Image* image, const MachOAnalyzer* mh)
+{
+    const unsigned pointerSize  = mh->pointerSize();
+    mh->forEachInterposingSection(_diag, ^(uint64_t sectVmOffset, uint64_t sectVmSize, bool &stop) {
+        const uint32_t entrySize = 2*pointerSize;
+        const uint32_t tupleCount = (uint32_t)(sectVmSize/entrySize);
+        BLOCK_ACCCESSIBLE_ARRAY(InterposingTuple, resolvedTuples, tupleCount);
+        for (uint32_t i=0; i < tupleCount; ++i) {
+            resolvedTuples[i].stockImplementation.absolute.kind  = Image::ResolvedSymbolTarget::kindAbsolute;
+            resolvedTuples[i].stockImplementation.absolute.value = 0;
+            resolvedTuples[i].newImplementation.absolute.kind    = Image::ResolvedSymbolTarget::kindAbsolute;
+            resolvedTuples[i].newImplementation.absolute.value   = 0;
+        }
+        image->forEachFixup(^(uint64_t imageOffsetToRebase, bool &rebaseStop) {
+            if ( imageOffsetToRebase < sectVmOffset )
+                return;
+            if ( imageOffsetToRebase > sectVmOffset+sectVmSize )
+                return;
+            uint64_t offsetIntoSection = imageOffsetToRebase - sectVmOffset;
+            uint64_t rebaseIndex = offsetIntoSection/entrySize;
+            if ( rebaseIndex*entrySize != offsetIntoSection )
+                return;
+            const void* content = (uint8_t*)mh + imageOffsetToRebase;
+            uint64_t unslidTargetAddress = mh->is64() ?  *(uint64_t*)content : *(uint32_t*)content;
+            resolvedTuples[rebaseIndex].newImplementation.image.kind     = Image::ResolvedSymbolTarget::kindImage;
+            resolvedTuples[rebaseIndex].newImplementation.image.imageNum = image->imageNum();
+            resolvedTuples[rebaseIndex].newImplementation.image.offset   = unslidTargetAddress - mh->preferredLoadAddress();
+        }, ^(uint64_t imageOffsetToBind, Image::ResolvedSymbolTarget bindTarget, bool &bindStop) {
+           if ( imageOffsetToBind < sectVmOffset )
+                return;
+            if ( imageOffsetToBind > sectVmOffset+sectVmSize )
+                return;
+            uint64_t offsetIntoSection = imageOffsetToBind - sectVmOffset;
+            uint64_t bindIndex = offsetIntoSection/entrySize;
+            if ( bindIndex*entrySize + pointerSize != offsetIntoSection )
+                return;
+            resolvedTuples[bindIndex].stockImplementation = bindTarget;
+        }, ^(uint64_t imageOffsetStart, const Array<Image::ResolvedSymbolTarget>& targets, bool& chainStop) {
+            // walk each fixup in the chain
+            image->forEachChainedFixup((void*)mh, imageOffsetStart, ^(uint64_t* fixupLoc, MachOLoaded::ChainedFixupPointerOnDisk fixupInfo, bool& stopChain) {
+                uint64_t imageOffsetToFixup = (uint64_t)fixupLoc - (uint64_t)mh;
+                if ( fixupInfo.authRebase.auth ) {
+#if SUPPORT_ARCH_arm64e
+                    if ( fixupInfo.authBind.bind ) {
+                        closure::Image::ResolvedSymbolTarget bindTarget = targets[fixupInfo.authBind.ordinal];
+                        if ( imageOffsetToFixup < sectVmOffset )
+                            return;
+                        if ( imageOffsetToFixup > sectVmOffset+sectVmSize )
+                            return;
+                        uint64_t offsetIntoSection = imageOffsetToFixup - sectVmOffset;
+                        uint64_t bindIndex = offsetIntoSection/entrySize;
+                        if ( bindIndex*entrySize + pointerSize != offsetIntoSection )
+                            return;
+                        resolvedTuples[bindIndex].stockImplementation = bindTarget;
+                    }
+                    else {
+                        if ( imageOffsetToFixup < sectVmOffset )
+                            return;
+                        if ( imageOffsetToFixup > sectVmOffset+sectVmSize )
+                            return;
+                        uint64_t offsetIntoSection = imageOffsetToFixup - sectVmOffset;
+                        uint64_t rebaseIndex = offsetIntoSection/entrySize;
+                        if ( rebaseIndex*entrySize != offsetIntoSection )
+                            return;
+                        uint64_t unslidTargetAddress = (uint64_t)mh->preferredLoadAddress() + fixupInfo.authRebase.target;
+                        resolvedTuples[rebaseIndex].newImplementation.image.kind     = Image::ResolvedSymbolTarget::kindImage;
+                        resolvedTuples[rebaseIndex].newImplementation.image.imageNum = image->imageNum();
+                        resolvedTuples[rebaseIndex].newImplementation.image.offset   = unslidTargetAddress - mh->preferredLoadAddress();
+                    }
+#else
+                    _diag.error("malformed chained pointer");
+                    stop = true;
+                    stopChain = true;
+#endif
+                }
+                else {
+                    if ( fixupInfo.plainRebase.bind ) {
+                        closure::Image::ResolvedSymbolTarget bindTarget = targets[fixupInfo.plainBind.ordinal];
+                        if ( imageOffsetToFixup < sectVmOffset )
+                            return;
+                        if ( imageOffsetToFixup > sectVmOffset+sectVmSize )
+                            return;
+                        uint64_t offsetIntoSection = imageOffsetToFixup - sectVmOffset;
+                        uint64_t bindIndex = offsetIntoSection/entrySize;
+                        if ( bindIndex*entrySize + pointerSize != offsetIntoSection )
+                            return;
+                        resolvedTuples[bindIndex].stockImplementation = bindTarget;
+                    }
+                    else {
+                        if ( imageOffsetToFixup < sectVmOffset )
+                            return;
+                        if ( imageOffsetToFixup > sectVmOffset+sectVmSize )
+                            return;
+                        uint64_t offsetIntoSection = imageOffsetToFixup - sectVmOffset;
+                        uint64_t rebaseIndex = offsetIntoSection/entrySize;
+                        if ( rebaseIndex*entrySize != offsetIntoSection )
+                            return;
+                        uint64_t unslidTargetAddress = fixupInfo.plainRebase.signExtendedTarget();
+                        resolvedTuples[rebaseIndex].newImplementation.image.kind     = Image::ResolvedSymbolTarget::kindImage;
+                        resolvedTuples[rebaseIndex].newImplementation.image.imageNum = image->imageNum();
+                        resolvedTuples[rebaseIndex].newImplementation.image.offset   = unslidTargetAddress - mh->preferredLoadAddress();
+                    }
+                }
+            });
+        });
+
+        // remove any tuples in which both sides are not set (or target is weak-import NULL)
+        STACK_ALLOC_ARRAY(InterposingTuple, goodTuples, tupleCount);
+        for (uint32_t i=0; i < tupleCount; ++i) {
+            if ( (resolvedTuples[i].stockImplementation.image.kind != Image::ResolvedSymbolTarget::kindAbsolute)
+              && (resolvedTuples[i].newImplementation.image.kind != Image::ResolvedSymbolTarget::kindAbsolute) )
+                goodTuples.push_back(resolvedTuples[i]);
+        }
+        writer.addInterposingTuples(goodTuples);
+
+        // if the target of the interposing is in the dyld shared cache, add a PatchEntry so the cache is fixed up at launch
+        STACK_ALLOC_ARRAY(Closure::PatchEntry, patches, goodTuples.count());
+        for (const InterposingTuple& aTuple : goodTuples) {
+            if ( aTuple.stockImplementation.sharedCache.kind == Image::ResolvedSymbolTarget::kindSharedCache ) {
+                uint32_t imageIndex;
+                assert(_dyldCache->addressInText((uint32_t)aTuple.stockImplementation.sharedCache.offset, &imageIndex));
+                ImageNum imageInCache = imageIndex+1;
+                Closure::PatchEntry patch;
+                patch.exportCacheOffset      = (uint32_t)aTuple.stockImplementation.sharedCache.offset;
+                patch.overriddenDylibInCache = imageInCache;
+                patch.replacement            = aTuple.newImplementation;
+                patches.push_back(patch);
+            }
+        }
+        writer.addCachePatches(patches);
+    });
+}
+
+void ClosureBuilder::addRebaseInfo(ImageWriter& writer, const MachOAnalyzer* mh)
+{
+       const uint64_t ptrSize = mh->pointerSize();
+    Image::RebasePattern maxLeapPattern = { 0xFFFFF, 0, 0xF };
+    const uint64_t maxLeapCount = maxLeapPattern.repeatCount * maxLeapPattern.skipCount;
+    STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::RebasePattern, rebaseEntries, 1024);
+    __block uint64_t lastLocation = -ptrSize;
+       mh->forEachRebase(_diag, true, ^(uint64_t runtimeOffset, bool& stop) {
+        const uint64_t delta   = runtimeOffset - lastLocation;
+        const bool     aligned = ((delta % ptrSize) == 0);
+        if ( delta == ptrSize ) {
+            // this rebase location is contiguous to previous
+            if ( rebaseEntries.back().contigCount < 255 ) {
+                // just bump previous's contigCount
+                rebaseEntries.back().contigCount++;
+            }
+            else {
+                // previous contiguous run already has max 255, so start a new run
+                rebaseEntries.push_back({ 1, 1, 0 });
+            }
+        }
+        else if ( aligned && (delta <= (ptrSize*15)) ) {
+            // this rebase is within skip distance of last rebase
+            rebaseEntries.back().skipCount = (uint8_t)((delta-ptrSize)/ptrSize);
+            int lastIndex = (int)(rebaseEntries.count() - 1);
+            if ( lastIndex > 1 ) {
+                if ( (rebaseEntries[lastIndex].contigCount == rebaseEntries[lastIndex-1].contigCount)
+                  && (rebaseEntries[lastIndex].skipCount   == rebaseEntries[lastIndex-1].skipCount) ) {
+                    // this entry as same contig and skip as prev, so remove it and bump repeat count of previous
+                    rebaseEntries.pop_back();
+                    rebaseEntries.back().repeatCount += 1;
+                }
+            }
+            rebaseEntries.push_back({ 1, 1, 0 });
+        }
+        else {
+            uint64_t advanceCount = (delta-ptrSize);
+            if ( (runtimeOffset < lastLocation) && (lastLocation != -ptrSize) ) {
+                // out of rebases! handle this be resting rebase offset to zero
+                rebaseEntries.push_back({ 0, 0, 0 });
+                advanceCount = runtimeOffset;
+            }
+            // if next rebase is too far to reach with one pattern, use series
+            while ( advanceCount > maxLeapCount ) {
+                rebaseEntries.push_back(maxLeapPattern);
+                advanceCount -= maxLeapCount;
+            }
+            // if next rebase is not reachable with skipCount==1 or skipCount==15, add intermediate
+            while ( advanceCount > maxLeapPattern.repeatCount ) {
+                uint64_t count = advanceCount / maxLeapPattern.skipCount;
+                rebaseEntries.push_back({ (uint32_t)count, 0, maxLeapPattern.skipCount });
+                advanceCount -= (count*maxLeapPattern.skipCount);
+            }
+            if ( advanceCount != 0 )
+                rebaseEntries.push_back({ (uint32_t)advanceCount, 0, 1 });
+            rebaseEntries.push_back({ 1, 1, 0 });
+        }
+        lastLocation = runtimeOffset;
+       });
+    writer.setRebaseInfo(rebaseEntries);
+
+    // i386 programs also use text relocs to rebase stubs
+    if ( mh->cputype == CPU_TYPE_I386 ) {
+        STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::TextFixupPattern, textRebases, 512);
+        __block uint64_t lastOffset = -4;
+        mh->forEachTextRebase(_diag, ^(uint64_t runtimeOffset, bool& stop) {
+            if ( textRebases.freeCount() < 2 ) {
+                _diag.error("too many text rebase locations (%ld) in %s", textRebases.maxCount(), writer.currentImage()->path());
+                stop = true;
+            }
+            bool mergedIntoPrevious = false;
+            if ( (runtimeOffset > lastOffset) && !textRebases.empty() ) {
+                uint32_t skipAmount = (uint32_t)(runtimeOffset - lastOffset);
+                if ( (textRebases.back().repeatCount == 1) && (textRebases.back().skipCount == 0) ) {
+                    textRebases.back().repeatCount = 2;
+                    textRebases.back().skipCount   = skipAmount;
+                    mergedIntoPrevious             = true;
+                }
+                else if ( textRebases.back().skipCount == skipAmount ) {
+                    textRebases.back().repeatCount += 1;
+                    mergedIntoPrevious = true;
+                }
+            }
+            if ( !mergedIntoPrevious ) {
+                Image::TextFixupPattern pattern;
+                pattern.target.raw    = 0;
+                pattern.startVmOffset = (uint32_t)runtimeOffset;
+                pattern.repeatCount   = 1;
+                pattern.skipCount     = 0;
+                textRebases.push_back(pattern);
+            }
+            lastOffset = runtimeOffset;
+        });
+        writer.setTextRebaseInfo(textRebases);
+    }
+}
+
+
+void ClosureBuilder::forEachBind(BuilderLoadedImage& forImage, void (^handler)(uint64_t runtimeOffset, Image::ResolvedSymbolTarget target, const ResolvedTargetInfo& targetInfo, bool& stop),
+                                                               void (^strongHandler)(const char* strongSymbolName))
+{
+    __block int                         lastLibOrdinal  = 256;
+    __block const char*                 lastSymbolName  = nullptr;
+    __block uint64_t                    lastAddend      = 0;
+    __block Image::ResolvedSymbolTarget target;
+    __block ResolvedTargetInfo          targetInfo;
+    forImage.loadAddress()->forEachBind(_diag, ^(uint64_t runtimeOffset, int libOrdinal, const char* symbolName, bool weakImport, uint64_t addend, bool& stop) {
+        if ( (symbolName == lastSymbolName) && (libOrdinal == lastLibOrdinal) && (addend == lastAddend) ) {
+            // same symbol lookup as last location
+            handler(runtimeOffset, target, targetInfo, stop);
+        }
+        else if ( findSymbol(forImage, libOrdinal, symbolName, weakImport, addend, target, targetInfo) ) {
+            handler(runtimeOffset, target, targetInfo, stop);
+            lastSymbolName = symbolName;
+            lastLibOrdinal = libOrdinal;
+            lastAddend     = addend;
+        }
+        else {
+            stop = true;
+        }
+    }, ^(const char* symbolName) {
+        strongHandler(symbolName);
+    });
+}
+
+void ClosureBuilder::addBindInfo(ImageWriter& writer, BuilderLoadedImage& forImage)
+{
+    const uint32_t ptrSize = forImage.loadAddress()->pointerSize();
+       STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::BindPattern, binds, 512);
+    __block uint64_t                    lastOffset = -ptrSize;
+       __block Image::ResolvedSymbolTarget lastTarget = { {0, 0} };
+    forEachBind(forImage, ^(uint64_t runtimeOffset, Image::ResolvedSymbolTarget target, const ResolvedTargetInfo& targetInfo, bool& stop) {
+        if ( targetInfo.weakBindCoalese )  {
+            // may be previous bind to this location
+            // if so, update that rather create new BindPattern
+            for (Image::BindPattern& aBind : binds) {
+                if ( (aBind.startVmOffset == runtimeOffset) && (aBind.repeatCount == 1)  && (aBind.skipCount == 0) ) {
+                    aBind.target = target;
+                    return;
+                }
+            }
+        }
+        bool mergedIntoPrevious = false;
+        if ( !mergedIntoPrevious && (target == lastTarget) && (runtimeOffset > lastOffset) && !binds.empty() ) {
+            uint64_t skipAmount = (runtimeOffset - lastOffset - ptrSize)/ptrSize;
+            if ( skipAmount*ptrSize != (runtimeOffset - lastOffset - ptrSize) ) {
+                // misaligned pointer means we cannot optimize 
+            }
+            else {
+                if ( (binds.back().repeatCount == 1) && (binds.back().skipCount == 0) && (skipAmount <= 255) ) {
+                    binds.back().repeatCount = 2;
+                    binds.back().skipCount   = skipAmount;
+                    assert(binds.back().skipCount == skipAmount); // check overflow
+                    mergedIntoPrevious       = true;
+                }
+                else if ( (binds.back().skipCount == skipAmount) && (binds.back().repeatCount < 0xfff) ) {
+                    uint32_t prevRepeatCount = binds.back().repeatCount;
+                    binds.back().repeatCount += 1;
+                    assert(binds.back().repeatCount > prevRepeatCount); // check overflow
+                    mergedIntoPrevious       = true;
+                }
+            }
+        }
+        if ( (target == lastTarget) && (runtimeOffset == lastOffset) && !binds.empty() ) {
+            // duplicate bind for same location, ignore this one
+            mergedIntoPrevious = true;
+        }
+        if ( !mergedIntoPrevious ) {
+            Image::BindPattern pattern;
+            pattern.target        = target;
+            pattern.startVmOffset = runtimeOffset;
+            pattern.repeatCount   = 1;
+            pattern.skipCount     = 0;
+            assert(pattern.startVmOffset == runtimeOffset);
+            binds.push_back(pattern);
+        }
+        lastTarget = target;
+        lastOffset = runtimeOffset;
+       }, ^(const char* strongSymbolName) {
+        if ( !_makingDyldCacheImages ) {
+            // something has a strong symbol definition that may override a weak impl in the dyld cache
+            Image::ResolvedSymbolTarget strongOverride;
+            ResolvedTargetInfo          strongTargetInfo;
+            if ( findSymbolInImage(forImage.loadAddress(), strongSymbolName, 0, false, strongOverride, strongTargetInfo) ) {
+                for (const BuilderLoadedImage& li : _loadedImages) {
+                    if ( li.loadAddress()->inDyldCache() && li.loadAddress()->hasWeakDefs() ) {
+                        Image::ResolvedSymbolTarget implInCache;
+                        ResolvedTargetInfo          implInCacheInfo;
+                        if ( findSymbolInImage(li.loadAddress(), strongSymbolName, 0, false, implInCache, implInCacheInfo) ) {
+                            // found another instance in some dylib in dyld cache, will need to patch it
+                            Closure::PatchEntry patch;
+                            patch.exportCacheOffset      = (uint32_t)implInCache.sharedCache.offset;
+                            patch.overriddenDylibInCache = li.imageNum;
+                            patch.replacement            = strongOverride;
+                            _weakDefCacheOverrides.push_back(patch);
+                        }
+                    }
+                }
+            }
+        }
+       });
+    writer.setBindInfo(binds);
+}
+
+void ClosureBuilder::reportRebasesAndBinds(ImageWriter& writer, BuilderLoadedImage& forImage)
+{
+    // report all rebases
+    forImage.loadAddress()->forEachRebase(_diag, true, ^(uint64_t runtimeOffset, bool& stop) {
+        _handlers->rebase(forImage.imageNum, forImage.loadAddress(), (uint32_t)runtimeOffset);
+    });
+
+    // report all binds
+    forEachBind(forImage, ^(uint64_t runtimeOffset, Image::ResolvedSymbolTarget target, const ResolvedTargetInfo& targetInfo, bool& stop) {
+        _handlers->bind(forImage.imageNum, forImage.loadAddress(), (uint32_t)runtimeOffset, target, targetInfo);
+    },
+    ^(const char* strongSymbolName) {});
+
+    // i386 programs also use text relocs to rebase stubs
+    if ( forImage.loadAddress()->cputype == CPU_TYPE_I386 ) {
+        // FIX ME
+    }
+}
+
+// These are mangled symbols for all the variants of operator new and delete
+// which a main executable can define (non-weak) and override the
+// weak-def implementation in the OS.
+static const char* sTreatAsWeak[] = {
+    "__Znwm", "__ZnwmRKSt9nothrow_t",
+    "__Znam", "__ZnamRKSt9nothrow_t",
+    "__ZdlPv", "__ZdlPvRKSt9nothrow_t", "__ZdlPvm",
+    "__ZdaPv", "__ZdaPvRKSt9nothrow_t", "__ZdaPvm",
+    "__ZnwmSt11align_val_t", "__ZnwmSt11align_val_tRKSt9nothrow_t",
+    "__ZnamSt11align_val_t", "__ZnamSt11align_val_tRKSt9nothrow_t",
+    "__ZdlPvSt11align_val_t", "__ZdlPvSt11align_val_tRKSt9nothrow_t", "__ZdlPvmSt11align_val_t",
+    "__ZdaPvSt11align_val_t", "__ZdaPvSt11align_val_tRKSt9nothrow_t", "__ZdaPvmSt11align_val_t"
+};
+
+
+void ClosureBuilder::addChainedFixupInfo(ImageWriter& writer, const BuilderLoadedImage& forImage)
+{
+    // calculate max page starts
+    __block uint32_t dataPageCount = 1;
+    forImage.loadAddress()->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool& stop) {
+        if ( info.protections & VM_PROT_WRITE ) {
+            dataPageCount += ((info.fileSize+4095) / 4096);
+        }
+    });
+
+    // build array of starts
+    STACK_ALLOC_ARRAY(uint64_t, starts, dataPageCount);
+    forImage.loadAddress()->forEachChainedFixupStart(_diag, ^(uint64_t runtimeOffset, bool& stop) {
+        starts.push_back(runtimeOffset);
+    });
+
+    // build array of targets
+    STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::ResolvedSymbolTarget, targets,     1024);
+    STACK_ALLOC_OVERFLOW_SAFE_ARRAY(ResolvedTargetInfo,          targetInfos, 1024);
+    forImage.loadAddress()->forEachChainedFixupTarget(_diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
+        Image::ResolvedSymbolTarget target;
+        ResolvedTargetInfo          targetInfo;
+        if ( !findSymbol(forImage, libOrdinal, symbolName, weakImport, addend, target, targetInfo) ) {
+            const char* expectedInPath = forImage.loadAddress()->dependentDylibLoadPath(libOrdinal-1);
+            _diag.error("symbol '%s' not found, expected in '%s', needed by '%s'", symbolName, expectedInPath, forImage.path());
+            stop = true;
+            return;
+        }
+        if ( libOrdinal == BIND_SPECIAL_DYLIB_WEAK_DEF_COALESCE ) {
+            // add if not already in array
+            bool alreadyInArray = false;
+            for (const char* sym : _weakDefsFromChainedBinds) {
+                if ( strcmp(sym, symbolName) == 0 ) {
+                    alreadyInArray = true;
+                    break;
+                }
+            }
+            if ( !alreadyInArray )
+                _weakDefsFromChainedBinds.push_back(symbolName);
+        }
+        targets.push_back(target);
+        targetInfos.push_back(targetInfo);
+    });
+    if ( _diag.hasError() )
+        return;
+
+    if ( _handlers != nullptr )
+        _handlers->chainedBind(forImage.imageNum, forImage.loadAddress(), starts, targets, targetInfos);
+    else
+        writer.setChainedFixups(starts, targets); // store results in Image object
+
+    // with chained fixups, main executable may define symbol that overrides weak-defs but has no fixup
+    if ( _isLaunchClosure && forImage.loadAddress()->hasWeakDefs() && forImage.loadAddress()->isMainExecutable() ) {
+        for (const char* weakSymbolName : sTreatAsWeak) {
+            Diagnostics exportDiag;
+            dyld3::MachOAnalyzer::FoundSymbol foundInfo;
+            if ( forImage.loadAddress()->findExportedSymbol(exportDiag, weakSymbolName, foundInfo, nullptr) ) {
+                _weakDefsFromChainedBinds.push_back(weakSymbolName);
+            }
+        }
+    }
+}
+
+
+bool ClosureBuilder::findSymbolInImage(const MachOAnalyzer* macho, const char* symbolName, uint64_t addend, bool followReExports,
+                                       Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo)
+{
+    targetInfo.foundInDylib        = nullptr;
+    targetInfo.requestedSymbolName = symbolName;
+    targetInfo.addend              = addend;
+    targetInfo.isWeakDef           = false;
+    MachOLoaded::DependentToMachOLoaded reexportFinder = ^(const MachOLoaded* mh, uint32_t depIndex) {
+        return (const MachOLoaded*)findDependent(mh, depIndex);
+    };
+    MachOAnalyzer::DependentToMachOLoaded finder = nullptr;
+    if ( followReExports )
+        finder = reexportFinder;
+
+    dyld3::MachOAnalyzer::FoundSymbol foundInfo;
+    if ( macho->findExportedSymbol(_diag, symbolName, foundInfo, finder) ) {
+        const MachOAnalyzer* impDylib = (const MachOAnalyzer*)foundInfo.foundInDylib;
+        targetInfo.foundInDylib    = foundInfo.foundInDylib;
+        targetInfo.foundSymbolName = foundInfo.foundSymbolName;
+        if ( foundInfo.isWeakDef )
+            targetInfo.isWeakDef = true;
+        if ( foundInfo.kind == MachOAnalyzer::FoundSymbol::Kind::absolute ) {
+            target.absolute.kind   = Image::ResolvedSymbolTarget::kindAbsolute;
+            target.absolute.value  = foundInfo.value + addend;
+        }
+        else if ( impDylib->inDyldCache() ) {
+            target.sharedCache.kind   = Image::ResolvedSymbolTarget::kindSharedCache;
+            target.sharedCache.offset = (uint8_t*)impDylib - (uint8_t*)_dyldCache + foundInfo.value + addend;
+        }
+        else {
+            target.image.kind     = Image::ResolvedSymbolTarget::kindImage;
+            target.image.imageNum = findLoadedImage(impDylib).imageNum;
+            target.image.offset   = foundInfo.value + addend;
+        }
+        return true;
+    }
+    return false;
+}
+
+bool ClosureBuilder::findSymbol(const BuilderLoadedImage& fromImage, int libOrdinal, const char* symbolName, bool weakImport, uint64_t addend,
+                                Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo)
+{
+    targetInfo.weakBindCoalese      = false;
+    targetInfo.weakBindSameImage    = false;
+    targetInfo.requestedSymbolName  = symbolName;
+    targetInfo.libOrdinal           = libOrdinal;
+    if ( libOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) {
+        for (const BuilderLoadedImage& li : _loadedImages) {
+            if ( !li.rtldLocal && findSymbolInImage(li.loadAddress(), symbolName, addend, true, target, targetInfo) )
+                return true;
+        }
+        if ( weakImport ) {
+            target.absolute.kind  = Image::ResolvedSymbolTarget::kindAbsolute;
+            target.absolute.value = 0;
+            return true;
+        }
+        _diag.error("symbol '%s' not found, expected in flat namespace by '%s'", symbolName, fromImage.path());
+    }
+    else if ( libOrdinal == BIND_SPECIAL_DYLIB_WEAK_DEF_COALESCE ) {
+        // to resolve weakDef coalesing, we need to search all images in order and use first definition
+        // but, if first found is a weakDef, a later non-weak def overrides that
+        bool foundWeakDefImpl   = false;
+        bool foundStrongDefImpl = false;
+        bool foundImpl          = false;
+        Image::ResolvedSymbolTarget  aTarget;
+        ResolvedTargetInfo           aTargetInfo;
+        STACK_ALLOC_ARRAY(const BuilderLoadedImage*, cachedDylibsUsingSymbol, 1024);
+        for (const BuilderLoadedImage& li : _loadedImages) {
+            // only search images with weak-defs that were not loaded with RTLD_LOCAL
+            if ( li.loadAddress()->hasWeakDefs() && !li.rtldLocal ) {
+                if ( findSymbolInImage(li.loadAddress(), symbolName, addend, false, aTarget, aTargetInfo) ) {
+                    foundImpl = true;
+                    // with non-chained images, weak-defs first have a rebase to their local impl, and a weak-bind which allows earlier impls to override
+                    if ( !li.loadAddress()->hasChainedFixups() && (aTargetInfo.foundInDylib == fromImage.loadAddress()) )
+                        targetInfo.weakBindSameImage = true;
+                    if ( aTargetInfo.isWeakDef ) {
+                        // found a weakDef impl, if this is first found, set target to this
+                        if ( !foundWeakDefImpl && !foundStrongDefImpl ) {
+                            target      = aTarget;
+                            targetInfo  = aTargetInfo;
+                        }
+                        foundWeakDefImpl = true;
+                    }
+                    else {
+                        // found a non-weak impl, use this (unless early strong found)
+                        if ( !foundStrongDefImpl ) {
+                            target      = aTarget;
+                            targetInfo  = aTargetInfo;
+                        }
+                        foundStrongDefImpl = true;
+                    }
+                }
+                if ( foundImpl && !_makingDyldCacheImages && li.loadAddress()->inDyldCache() )
+                    cachedDylibsUsingSymbol.push_back(&li);
+            }
+        }
+        // now that final target found, if any dylib in dyld cache uses that symbol name, redirect it to new target
+        if ( !cachedDylibsUsingSymbol.empty() ) {
+            for (const BuilderLoadedImage* li : cachedDylibsUsingSymbol) {
+                Image::ResolvedSymbolTarget implInCache;
+                ResolvedTargetInfo          implInCacheInfo;
+                if ( findSymbolInImage(li->loadAddress(), symbolName, addend, false, implInCache, implInCacheInfo) ) {
+                    if ( implInCache != target ) {
+                        // found another instance in some dylib in dyld cache, will need to patch it
+                        Closure::PatchEntry patch;
+                        patch.exportCacheOffset      = (uint32_t)implInCache.sharedCache.offset;
+                        patch.overriddenDylibInCache = li->imageNum;
+                        patch.replacement            = target;
+                        _weakDefCacheOverrides.push_back(patch);
+                    }
+                }
+            }
+        }
+        targetInfo.weakBindCoalese = true;
+
+        if ( foundImpl )
+            return true;
+        _diag.error("symbol '%s' not found, expected to be weak-def coalesced", symbolName);
+    }
+    else {
+        const BuilderLoadedImage* targetLoadedImage = nullptr;
+        if ( (libOrdinal > 0) && (libOrdinal <= (int)fromImage.dependents.count()) ) {
+            ImageNum childNum = fromImage.dependents[libOrdinal - 1].imageNum();
+            if ( childNum != kMissingWeakLinkedImage ) {
+                targetLoadedImage = &findLoadedImage(childNum);
+            }
+        }
+        else if ( libOrdinal == BIND_SPECIAL_DYLIB_SELF ) {
+            targetLoadedImage = &fromImage;
+        }
+        else if ( libOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) {
+            targetLoadedImage = &_loadedImages[_mainProgLoadIndex];
+        }
+        else {
+            _diag.error("unknown special ordinal %d in %s", libOrdinal, fromImage.path());
+            return false;
+        }
+
+        if ( targetLoadedImage != nullptr ) {
+            if ( findSymbolInImage(targetLoadedImage->loadAddress(), symbolName, addend, true, target, targetInfo) )
+                return true;
+        }
+
+        if ( weakImport ) {
+            target.absolute.kind  = Image::ResolvedSymbolTarget::kindAbsolute;
+            target.absolute.value = 0;
+            return true;
+        }
+        const char* expectedInPath = targetLoadedImage ? targetLoadedImage->path() : "unknown";
+        _diag.error("symbol '%s' not found, expected in '%s', needed by '%s'", symbolName, expectedInPath, fromImage.path());
+        if ( _launchErrorInfo != nullptr ) {
+            _launchErrorInfo->kind              = DYLD_EXIT_REASON_SYMBOL_MISSING;
+            _launchErrorInfo->clientOfDylibPath = fromImage.path();
+            _launchErrorInfo->targetDylibPath   = expectedInPath;
+            _launchErrorInfo->symbol            = symbolName;
+        }
+    }
+    return false;
+}
+
+
+void ClosureBuilder::depthFirstRecurseSetInitInfo(uint32_t loadIndex, InitInfo initInfos[], uint32_t& initOrder, bool& hasError)
+{
+    if ( initInfos[loadIndex].visited )
+        return;
+    initInfos[loadIndex].visited        = true;
+    initInfos[loadIndex].danglingUpward = false;
+
+    if (_loadedImages[loadIndex].isBadImage) {
+        hasError = true;
+        return;
+    }
+
+    for (const Image::LinkedImage& dep : _loadedImages[loadIndex].dependents) {
+        if ( dep.imageNum() == kMissingWeakLinkedImage )
+            continue;
+        ClosureBuilder::BuilderLoadedImage& depLi = findLoadedImage(dep.imageNum());
+        uint32_t depLoadIndex = (uint32_t)_loadedImages.index(depLi);
+        if ( dep.kind() == Image::LinkKind::upward ) {
+            if ( !initInfos[depLoadIndex].visited )
+                initInfos[depLoadIndex].danglingUpward = true;
+        }
+        else {
+            depthFirstRecurseSetInitInfo(depLoadIndex, initInfos, initOrder, hasError);
+            if (hasError)
+                return;
+        }
+    }
+    initInfos[loadIndex].initOrder = initOrder++;
+}
+
+void ClosureBuilder::computeInitOrder(ImageWriter& imageWriter, uint32_t loadIndex)
+{
+    // allocate array to track initializers
+    InitInfo initInfos[_loadedImages.count()];
+    bzero(initInfos, sizeof(initInfos));
+
+    // recurse all images and build initializer list from bottom up
+    uint32_t initOrder = 1;
+    bool hasMissingDependent = false;
+    depthFirstRecurseSetInitInfo(loadIndex, initInfos, initOrder, hasMissingDependent);
+    if (hasMissingDependent) {
+        imageWriter.setInvalid();
+        return;
+    }
+
+    // any images not visited yet are are danging, force add them to end of init list
+    for (uint32_t i=0; i < (uint32_t)_loadedImages.count(); ++i) {
+        if ( !initInfos[i].visited && initInfos[i].danglingUpward ) {
+            depthFirstRecurseSetInitInfo(i, initInfos, initOrder, hasMissingDependent);
+        }
+    }
+
+    if (hasMissingDependent) {
+        imageWriter.setInvalid();
+        return;
+    }
+    
+    // build array of just images with initializer
+    STACK_ALLOC_ARRAY(uint32_t, indexOfImagesWithInits, _loadedImages.count());
+    uint32_t index = 0;
+    for (const BuilderLoadedImage& li : _loadedImages) {
+        if ( initInfos[index].visited && li.hasInits ) {
+            indexOfImagesWithInits.push_back(index);
+        }
+        ++index;
+    }
+
+    // bubble sort (FIXME)
+    if ( indexOfImagesWithInits.count() > 1 ) {
+        for (uint32_t i=0; i < indexOfImagesWithInits.count()-1; ++i) {
+            for (uint32_t j=0; j < indexOfImagesWithInits.count()-i-1; ++j) {
+                if ( initInfos[indexOfImagesWithInits[j]].initOrder > initInfos[indexOfImagesWithInits[j+1]].initOrder ) {
+                    uint32_t temp               = indexOfImagesWithInits[j];
+                    indexOfImagesWithInits[j]   = indexOfImagesWithInits[j+1];
+                    indexOfImagesWithInits[j+1] = temp;
+                }
+            }
+        }
+    }
+
+    // copy ImageNum of each image with initializers into array
+    ImageNum initNums[indexOfImagesWithInits.count()];
+    for (uint32_t i=0; i < indexOfImagesWithInits.count(); ++i) {
+        initNums[i] = _loadedImages[indexOfImagesWithInits[i]].imageNum;
+    }
+
+    // add to closure info
+    imageWriter.setInitsOrder(initNums, (uint32_t)indexOfImagesWithInits.count());
+}
+
+void ClosureBuilder::addCachePatchInfo(ImageWriter& imageWriter, const BuilderLoadedImage& forImage)
+{
+    assert(_handlers != nullptr);
+    _handlers->forEachExportsPatch(forImage.imageNum, ^(const CacheDylibsBindingHandlers::PatchInfo& info) {
+        assert(info.usesCount != 0);
+        imageWriter.addExportPatchInfo(info.exportCacheOffset, info.exportSymbolName, info.usesCount, info.usesArray);
+    });
+}
+
+void ClosureBuilder::addClosureInfo(LaunchClosureWriter& closureWriter)
+{
+    // record which is libSystem
+    assert(_libSystemImageNum != 0);
+       closureWriter.setLibSystemImageNum(_libSystemImageNum);
+
+    // record which is libdyld
+    assert(_libDyldImageNum != 0);
+    Image::ResolvedSymbolTarget entryLocation;
+    ResolvedTargetInfo          entryInfo;
+    if ( findSymbolInImage(findLoadedImage(_libDyldImageNum).loadAddress(), "__ZN5dyld318entryVectorForDyldE", 0, false, entryLocation, entryInfo) ) {
+        const dyld3::LibDyldEntryVector* libDyldEntry = nullptr;
+        switch ( entryLocation.image.kind ) {
+            case Image::ResolvedSymbolTarget::kindSharedCache:
+                libDyldEntry = (dyld3::LibDyldEntryVector*)((uint8_t*)_dyldCache + entryLocation.sharedCache.offset);
+                break;
+            case Image::ResolvedSymbolTarget::kindImage:
+                libDyldEntry = (dyld3::LibDyldEntryVector*)((uint8_t*)findLoadedImage(entryLocation.image.imageNum).loadAddress() + entryLocation.image.offset);
+                break;
+        }
+        if ( (libDyldEntry != nullptr) && (libDyldEntry->binaryFormatVersion == dyld3::closure::kFormatVersion) )
+            closureWriter.setLibDyldEntry(entryLocation);
+        else
+            _diag.error("libdyld.dylib entry vector is incompatible");
+    }
+    else {
+        _diag.error("libdyld.dylib is missing entry vector");
+    }
+
+    // record which is main executable
+    ImageNum mainProgImageNum = _loadedImages[_mainProgLoadIndex].imageNum;
+    closureWriter.setTopImageNum(mainProgImageNum);
+
+    // add entry
+    uint32_t    entryOffset;
+    bool        usesCRT;
+    if ( _loadedImages[_mainProgLoadIndex].loadAddress()->getEntry(entryOffset, usesCRT) ) {
+        Image::ResolvedSymbolTarget location;
+        location.image.kind     = Image::ResolvedSymbolTarget::kindImage;
+        location.image.imageNum = mainProgImageNum;
+        location.image.offset   = entryOffset;
+        if ( usesCRT )
+            closureWriter.setStartEntry(location);
+        else
+            closureWriter.setMainEntry(location);
+    }
+
+    // add env vars that must match at launch time
+    _pathOverrides.forEachEnvVar(^(const char* envVar) {
+        closureWriter.addEnvVar(envVar);
+    });
+
+    // add list of files which must be missing
+    STACK_ALLOC_ARRAY(const char*, paths, 8192);
+    if ( _mustBeMissingPaths != nullptr ) {
+        _mustBeMissingPaths->forEachPath(^(const char* aPath) {
+            paths.push_back(aPath);
+        });
+    }
+       closureWriter.setMustBeMissingFiles(paths);
+}
+
+
+// used at launch by dyld when kernel has already mapped main executable
+const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fileInfo, bool allowInsertFailures)
+{
+    dyld3::ScopedTimer timer(DBG_DYLD_TIMING_BUILD_CLOSURE, 0, 0, 0);
+    const mach_header* mainMH = (const mach_header*)fileInfo.fileContent;
+    // set up stack based storage for all arrays
+    BuilderLoadedImage  loadImagesStorage[512];
+    Image::LinkedImage  dependenciesStorage[512*8];
+    InterposingTuple    tuplesStorage[64];
+    Closure::PatchEntry cachePatchStorage[64];
+    const char*         weakDefNameStorage[64];
+    _loadedImages.setInitialStorage(loadImagesStorage, 512);
+    _dependencies.setInitialStorage(dependenciesStorage, 512*8);
+    _interposingTuples.setInitialStorage(tuplesStorage, 64);
+    _weakDefCacheOverrides.setInitialStorage(cachePatchStorage, 64);
+    _weakDefsFromChainedBinds.setInitialStorage(weakDefNameStorage, 64);
+    ArrayFinalizer<BuilderLoadedImage> scopedCleanup(_loadedImages, ^(BuilderLoadedImage& li) { if (li.unmapWhenDone) {_fileSystem.unloadFile(li.loadedFileInfo); li.unmapWhenDone=false;} });
+
+    const MachOAnalyzer* mainExecutable = MachOAnalyzer::validMainExecutable(_diag, mainMH, fileInfo.path, fileInfo.sliceLen, _archName, _platform);
+    if ( mainExecutable == nullptr )
+        return nullptr;
+    if ( !mainExecutable->isDynamicExecutable() ) {
+        _diag.error("not a main executable");
+        return nullptr;
+    }
+    _isLaunchClosure   = true;
+
+    // add any DYLD_INSERT_LIBRARIES
+    _nextIndex = 0;
+    _pathOverrides.forEachInsertedDylib(^(const char* dylibPath) {
+        BuilderLoadedImage insertEntry;
+        insertEntry.loadedFileInfo.path = strdup_temp(dylibPath);
+        insertEntry.imageNum            = _startImageNum + _nextIndex++;
+        insertEntry.unmapWhenDone       = true;
+        insertEntry.contentRebased      = false;
+        insertEntry.hasInits            = false;
+        insertEntry.markNeverUnload     = true;
+        insertEntry.rtldLocal           = false;
+        insertEntry.isBadImage          = false;
+        insertEntry.overrideImageNum    = 0;
+        _loadedImages.push_back(insertEntry);
+    });
+    _mainProgLoadIndex = (uint32_t)_loadedImages.count();
+
+    // add main executable
+    BuilderLoadedImage mainEntry;
+    mainEntry.loadedFileInfo   = fileInfo;
+    mainEntry.imageNum         = _startImageNum + _nextIndex++;
+    mainEntry.unmapWhenDone    = false;
+    mainEntry.contentRebased   = false;
+    mainEntry.hasInits         = false;
+    mainEntry.markNeverUnload  = true;
+    mainEntry.rtldLocal        = false;
+    mainEntry.isBadImage       = false;
+    mainEntry.overrideImageNum = 0;
+    _loadedImages.push_back(mainEntry);
+
+       // get mach_headers for all images needed to launch this main executable
+    LoadedImageChain chainStart = { nullptr, _loadedImages[_mainProgLoadIndex] };
+    recursiveLoadDependents(chainStart);
+    if ( _diag.hasError() )
+        return nullptr;
+    for (uint32_t i=0; i < _mainProgLoadIndex; ++i) {
+        closure::LoadedFileInfo loadedFileInfo = MachOAnalyzer::load(_diag, _fileSystem, _loadedImages[i].loadedFileInfo.path, _archName, _platform);
+        const char* originalLoadPath = _loadedImages[i].loadedFileInfo.path;
+        _loadedImages[i].loadedFileInfo = loadedFileInfo;
+        if ( _loadedImages[i].loadAddress() != nullptr ) {
+            LoadedImageChain insertChainStart = { nullptr, _loadedImages[i] };
+            recursiveLoadDependents(insertChainStart);
+        }
+        if ( _diag.hasError() || (_loadedImages[i].loadAddress() == nullptr) ) {
+            if ( !allowInsertFailures ) {
+                if ( _diag.noError() )
+                    _diag.error("could not load inserted dylib %s", originalLoadPath);
+                return nullptr;
+            }
+            _diag.clearError(); // FIXME add way to plumb back warning
+            // remove slot for inserted image that could not loaded
+            _loadedImages.remove(i);
+            i -= 1;
+            _mainProgLoadIndex -= 1;
+            _nextIndex -= 1;
+            // renumber images in this closure
+            for (uint32_t j=i+1; j < _loadedImages.count(); ++j) {
+                if ( (_loadedImages[j].imageNum >= _startImageNum) && (_loadedImages[j].imageNum <= _startImageNum+_nextIndex) )
+                    _loadedImages[j].imageNum -= 1;
+            }
+        }
+    }
+    loadDanglingUpwardLinks();
+
+    // only some images need to go into closure (ones from dyld cache do not)
+    STACK_ALLOC_ARRAY(ImageWriter, writers, _loadedImages.count());
+    for (BuilderLoadedImage& li : _loadedImages) {
+        if ( li.imageNum >= _startImageNum ) {
+            writers.push_back(ImageWriter());
+            buildImage(writers.back(), li);
+            if ( _diag.hasError() )
+                return nullptr;
+        }
+        if ( li.loadAddress()->isDylib() && (strcmp(li.loadAddress()->installName(), "/usr/lib/system/libdyld.dylib") == 0) )
+            _libDyldImageNum = li.imageNum;
+        else if ( strcmp(li.path(), "/usr/lib/libSystem.B.dylib") == 0 )
+            _libSystemImageNum = li.imageNum;
+   }
+
+    // add initializer order into top level Images (may be inserted dylibs before main executable)
+    for (uint32_t i=0; i <= _mainProgLoadIndex; ++i)
+        computeInitOrder(writers[i], i);
+
+    // combine all Image objects into one ImageArray
+    ImageArrayWriter imageArrayWriter(_startImageNum, (uint32_t)writers.count());
+    for (ImageWriter& writer : writers) {
+        imageArrayWriter.appendImage(writer.finalize());
+        writer.deallocate();
+    }
+    const ImageArray* imageArray = imageArrayWriter.finalize();
+
+    // merge ImageArray object into LaunchClosure object
+    __block LaunchClosureWriter closureWriter(imageArray);
+
+    // record shared cache info
+    if ( _dyldCache != nullptr ) {
+        // record cache UUID
+        uuid_t cacheUUID;
+        _dyldCache->getUUID(cacheUUID);
+        closureWriter.setDyldCacheUUID(cacheUUID);
+
+        // record any cache patching needed because of dylib overriding cache
+        for (const BuilderLoadedImage& li : _loadedImages) {
+            if ( li.overrideImageNum != 0 ) {
+                const Image* cacheImage = _dyldImageArray->imageForNum(li.overrideImageNum);
+                STACK_ALLOC_ARRAY(Closure::PatchEntry, patches, cacheImage->patchableExportCount());
+                //fprintf(stderr, "'%s' overrides '%s'\n", li.loadedFileInfo.path, cacheImage->path());
+                cacheImage->forEachPatchableExport(^(uint32_t cacheOffsetOfImpl, const char* symbolName) {
+                    dyld3::MachOAnalyzer::FoundSymbol foundInfo;
+                    Diagnostics                       patchDiag;
+                    Closure::PatchEntry               patch;
+                    patch.overriddenDylibInCache  = li.overrideImageNum;
+                    patch.exportCacheOffset       = cacheOffsetOfImpl;
+                    if ( li.loadAddress()->findExportedSymbol(patchDiag, symbolName, foundInfo, nullptr) ) {
+                        patch.replacement.image.kind     = Image::ResolvedSymbolTarget::kindImage;
+                        patch.replacement.image.imageNum = li.imageNum;
+                        patch.replacement.image.offset   = foundInfo.value;
+                    }
+                    else {
+                        // this means the symbol is missing in the cache override dylib, so set any uses to NULL
+                        patch.replacement.absolute.kind    = Image::ResolvedSymbolTarget::kindAbsolute;
+                        patch.replacement.absolute.value   = 0;
+                    }
+                    patches.push_back(patch);
+                });
+                closureWriter.addCachePatches(patches);
+            }
+        }
+
+        // handle any extra weak-def coalescing needed by chained fixups
+        if ( !_weakDefsFromChainedBinds.empty() ) {
+            for (const char* symbolName : _weakDefsFromChainedBinds) {
+                Image::ResolvedSymbolTarget cacheOverrideTarget;
+                bool haveCacheOverride = false;
+                bool foundCachOverrideIsWeakDef = false;
+                for (const BuilderLoadedImage& li : _loadedImages) {
+                    if ( !li.loadAddress()->hasWeakDefs() )
+                        continue;
+                    Image::ResolvedSymbolTarget target;
+                    ResolvedTargetInfo          targetInfo;
+                    if ( findSymbolInImage(li.loadAddress(), symbolName, 0, false, target, targetInfo) ) {
+                        if ( li.loadAddress()->inDyldCache() ) {
+                            if ( haveCacheOverride ) {
+                                Closure::PatchEntry patch;
+                                patch.exportCacheOffset      = (uint32_t)target.sharedCache.offset;
+                                patch.overriddenDylibInCache = li.imageNum;
+                                patch.replacement            = cacheOverrideTarget;
+                                _weakDefCacheOverrides.push_back(patch);
+                            }
+                            else {
+                                // found first in cached dylib, so no need to patch cache for this symbol
+                                break;
+                            }
+                        }
+                        else {
+                            // found image that exports this symbol and is not in cache
+                            if ( !haveCacheOverride || (foundCachOverrideIsWeakDef && !targetInfo.isWeakDef) ) {
+                                // update cache to use this symbol if it if first found or it is first non-weak found
+                                cacheOverrideTarget         = target;
+                                foundCachOverrideIsWeakDef  = targetInfo.isWeakDef;
+                                haveCacheOverride           = true;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        // record any cache patching needed because weak-def C++ symbols override dyld cache
+        if ( !_weakDefCacheOverrides.empty() )
+            closureWriter.addCachePatches(_weakDefCacheOverrides);
+
+   }
+
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+    // if closure is built on-device for iOS, then record boot UUID
+    char bootSessionUUID[256] = { 0 };
+    size_t bootSize = sizeof(bootSessionUUID);
+    if ( sysctlbyname("kern.bootsessionuuid", bootSessionUUID, &bootSize, NULL, 0) == 0 )
+        closureWriter.setBootUUID(bootSessionUUID);
+#endif
+
+     // record any interposing info
+    imageArray->forEachImage(^(const Image* image, bool &stop) {
+        if ( !image->inDyldCache() )
+            addInterposingTuples(closureWriter, image, findLoadedImage(image->imageNum()).loadAddress());
+    });
+
+    // modify fixups in contained Images by applying interposing tuples
+    closureWriter.applyInterposing();
+
+    // set flags
+    closureWriter.setUsedAtPaths(_atPathUsed);
+    closureWriter.setUsedFallbackPaths(_fallbackPathUsed);
+    closureWriter.setInitImageCount((uint32_t)_loadedImages.count());
+
+    // add other closure attributes
+    addClosureInfo(closureWriter);
+
+    // make result
+    const LaunchClosure* result = closureWriter.finalize();
+    imageArrayWriter.deallocate();
+
+    return result;
+}
+
+// used by libdyld for dlopen()
+const DlopenClosure* ClosureBuilder::makeDlopenClosure(const char* path, const LaunchClosure* mainClosure, const Array<LoadedImage>& alreadyLoadedList,
+                                                       closure::ImageNum callerImageNum, bool noLoad, bool canUseSharedCacheClosure, closure::ImageNum* topImageNum)
+{
+    dyld3::ScopedTimer timer(DBG_DYLD_TIMING_BUILD_CLOSURE, 0, 0, 0);
+    // set up stack based storage for all arrays
+    BuilderLoadedImage  loadImagesStorage[512];
+    Image::LinkedImage  dependenciesStorage[512*8];
+    Closure::PatchEntry cachePatchStorage[64];
+    _loadedImages.setInitialStorage(loadImagesStorage, 512);
+    _dependencies.setInitialStorage(dependenciesStorage, 512*8);
+    _weakDefCacheOverrides.setInitialStorage(cachePatchStorage, 64);
+    ArrayFinalizer<BuilderLoadedImage> scopedCleanup(_loadedImages, ^(BuilderLoadedImage& li) { if (li.unmapWhenDone) {_fileSystem.unloadFile(li.loadedFileInfo); li.unmapWhenDone=false;} });
+
+    // fill in builder array from already loaded images
+    bool cachedDylibsExpectedOnDisk = _dyldCache ? _dyldCache->header.dylibsExpectedOnDisk : true;
+    uintptr_t callerImageIndex = UINTPTR_MAX;
+    for (const LoadedImage& ali : alreadyLoadedList) {
+        const Image*          image       = ali.image();
+        const MachOAnalyzer*  ma          = (MachOAnalyzer*)(ali.loadedAddress());
+        bool                  inDyldCache = ma->inDyldCache();
+        BuilderLoadedImage entry;
+        ImageNum overrideImageNum;
+        entry.loadedFileInfo.path        = image->path();
+        entry.loadedFileInfo.fileContent = ma;
+        entry.loadedFileInfo.sliceOffset = 0;
+        entry.loadedFileInfo.inode       = 0;
+        entry.loadedFileInfo.mtime       = 0;
+        entry.imageNum                   = image->imageNum();
+        entry.dependents                 = image->dependentsArray();
+        entry.unmapWhenDone              = false;
+        entry.contentRebased             = inDyldCache;
+        entry.hasInits                   = false;
+        entry.markNeverUnload            = image->neverUnload();
+        entry.rtldLocal                  = ali.hideFromFlatSearch();
+        entry.isBadImage                 = false;
+        entry.overrideImageNum           = 0;
+        if ( !inDyldCache && image->isOverrideOfDyldCacheImage(overrideImageNum) ) {
+            entry.overrideImageNum  = overrideImageNum;
+            canUseSharedCacheClosure = false;
+        }
+        if ( !inDyldCache || cachedDylibsExpectedOnDisk )
+            image->hasFileModTimeAndInode(entry.loadedFileInfo.inode, entry.loadedFileInfo.mtime);
+        if ( entry.imageNum == callerImageNum )
+            callerImageIndex = _loadedImages.count();
+        _loadedImages.push_back(entry);
+   }
+    _alreadyInitedIndex = (uint32_t)_loadedImages.count();
+
+    // find main executable (may be needed for @executable_path)
+    _isLaunchClosure = false;
+    for (uint32_t i=0; i < alreadyLoadedList.count(); ++i) {
+        if ( _loadedImages[i].loadAddress()->isMainExecutable() )  {
+            _mainProgLoadIndex = i;
+            break;
+        }
+    }
+
+    // add top level dylib being dlopen()ed
+    BuilderLoadedImage* foundTopImage;
+    _nextIndex = 0;
+    // @rpath has caller's LC_PRATH, then main executable's LC_RPATH
+    BuilderLoadedImage& callerImage = (callerImageIndex != UINTPTR_MAX) ? _loadedImages[callerImageIndex]  : _loadedImages[_mainProgLoadIndex];
+    LoadedImageChain chainCaller = { nullptr, callerImage };
+    LoadedImageChain chainMain = { &chainCaller, _loadedImages[_mainProgLoadIndex] };
+    if ( !findImage(path, chainMain, foundTopImage, false, canUseSharedCacheClosure) ) {
+        // If we didn't find the image, but its a shared cache path, then try again with realpath.
+        if ( (strncmp(path, "/usr/lib/", 9) == 0) || (strncmp(path, "/System/Library/", 16) == 0) ) {
+            char resolvedPath[PATH_MAX];
+            if ( _fileSystem.getRealPath(path, resolvedPath) ) {
+                if ( !findImage(resolvedPath, chainMain, foundTopImage, false, canUseSharedCacheClosure) ) {
+                    return nullptr;
+                }
+            } else {
+                // We didn't find a new path from realpath
+                return nullptr;
+            }
+        } else {
+            // Not in /usr/lib/ or /System/Library/
+            return nullptr;
+        }
+    }
+
+    // exit early in RTLD_NOLOAD mode
+    if ( noLoad ) {
+        // if no new images added to _loadedImages, then requested path was already loaded
+        if ( (uint32_t)_loadedImages.count() == _alreadyInitedIndex )
+            *topImageNum = foundTopImage->imageNum;
+        else
+            *topImageNum = 0;
+        return nullptr;
+    }
+
+    // fast path if roots are not allowed and target is in dyld cache or is other
+    if ( (_dyldCache != nullptr) && (_dyldCache->header.cacheType == kDyldSharedCacheTypeProduction) ) {
+        if ( foundTopImage->imageNum < closure::kFirstLaunchClosureImageNum ) {
+            *topImageNum = foundTopImage->imageNum;
+            return nullptr;
+        }
+    }
+
+    // recursive load dependents
+    // @rpath for stuff top dylib depends on uses LC_RPATH from caller, main exe, and dylib being dlopen()ed
+    LoadedImageChain chainTopDylib = { &chainMain, *foundTopImage };
+    recursiveLoadDependents(chainTopDylib);
+    if ( _diag.hasError() )
+        return nullptr;
+    loadDanglingUpwardLinks();
+
+    // only some images need to go into closure (ones from dyld cache do not)
+    STACK_ALLOC_ARRAY(ImageWriter, writers, _loadedImages.count());
+    for (BuilderLoadedImage& li : _loadedImages) {
+        if ( li.imageNum >= _startImageNum ) {
+            writers.push_back(ImageWriter());
+            buildImage(writers.back(), li);
+        }
+    }
+
+    // check if top image loaded is in shared cache along with everything it depends on
+    *topImageNum = foundTopImage->imageNum;
+    if ( writers.count() == 0 ) {
+        return nullptr;
+    } else if ( canUseSharedCacheClosure && ( foundTopImage->imageNum < closure::kFirstLaunchClosureImageNum ) ) {
+        // We used a shared cache built closure, but now discovered roots.  We need to try again
+        topImageNum = 0;
+        return sRetryDlopenClosure;
+    }
+
+    // add initializer order into top level Image
+    computeInitOrder(writers[0], (uint32_t)alreadyLoadedList.count());
+
+    // combine all Image objects into one ImageArray
+    ImageArrayWriter imageArrayWriter(_startImageNum, (uint32_t)writers.count());
+    for (ImageWriter& writer : writers) {
+        imageArrayWriter.appendImage(writer.finalize());
+        writer.deallocate();
+    }
+    const ImageArray* imageArray = imageArrayWriter.finalize();
+
+    // merge ImageArray object into LaunchClosure object
+    DlopenClosureWriter closureWriter(imageArray);
+
+    // add other closure attributes
+    closureWriter.setTopImageNum(foundTopImage->imageNum);
+
+    // record any cache patching needed because of dylib overriding cache
+    if ( _dyldCache != nullptr ) {
+        for (const BuilderLoadedImage& li : _loadedImages) {
+            if ( (li.overrideImageNum != 0) && (li.imageNum >= _startImageNum) ) {
+                const Image* cacheImage = _dyldImageArray->imageForNum(li.overrideImageNum);
+                STACK_ALLOC_ARRAY(Closure::PatchEntry, patches, cacheImage->patchableExportCount());
+                //fprintf(stderr, "'%s' overrides '%s'\n", li.loadedFileInfo.path, cacheImage->path());
+                cacheImage->forEachPatchableExport(^(uint32_t cacheOffsetOfImpl, const char* symbolName) {
+                    dyld3::MachOAnalyzer::FoundSymbol foundInfo;
+                    Diagnostics                       patchDiag;
+                    Closure::PatchEntry               patch;
+                    patch.overriddenDylibInCache  = li.overrideImageNum;
+                    patch.exportCacheOffset       = cacheOffsetOfImpl;
+                    if ( li.loadAddress()->findExportedSymbol(patchDiag, symbolName, foundInfo, nullptr) ) {
+                        patch.replacement.image.kind     = Image::ResolvedSymbolTarget::kindImage;
+                        patch.replacement.image.imageNum = li.imageNum;
+                        patch.replacement.image.offset   = foundInfo.value;
+                    }
+                    else {
+                        patch.replacement.absolute.kind    = Image::ResolvedSymbolTarget::kindAbsolute;
+                        patch.replacement.absolute.value   = 0;
+                    }
+                    patches.push_back(patch);
+                });
+                closureWriter.addCachePatches(patches);
+            }
+        }
+    }
+
+    // Dlopen's should never keep track of missing paths as we don't cache these closures.
+    assert(_mustBeMissingPaths == nullptr);
+
+    // make final DlopenClosure object
+    const DlopenClosure* result = closureWriter.finalize();
+    imageArrayWriter.deallocate();
+    return result;
+}
+
+
+// used by dyld_closure_util
+const LaunchClosure* ClosureBuilder::makeLaunchClosure(const char* mainPath, bool allowInsertFailures)
+{
+    closure::LoadedFileInfo loadedFileInfo = MachOAnalyzer::load(_diag, _fileSystem, mainPath, _archName, _platform);
+    const MachOAnalyzer* mh = (const MachOAnalyzer*)loadedFileInfo.fileContent;
+    loadedFileInfo.path = mainPath;
+    if (_diag.hasError())
+        return nullptr;
+    if (mh == nullptr) {
+        _diag.error("could not load file");
+        return nullptr;
+    }
+    if (!mh->isDynamicExecutable()) {
+        _diag.error("file is not an executable");
+        return nullptr;
+    }
+    const_cast<PathOverrides*>(&_pathOverrides)->setMainExecutable(mh, mainPath);
+    const LaunchClosure* launchClosure = makeLaunchClosure(loadedFileInfo, allowInsertFailures);
+    loadedFileInfo.unload(loadedFileInfo);
+    return launchClosure;
+}
+
+
+// used by dyld shared cache builder
+const ImageArray* ClosureBuilder::makeDyldCacheImageArray(bool customerCache, const Array<CachedDylibInfo>& dylibs, const Array<CachedDylibAlias>& aliases)
+{
+    // because this is run in cache builder using dispatch_apply() there is minimal stack space
+    // so set up storage for all arrays to be vm_allocated
+    uintptr_t maxImageCount = dylibs.count() + 16;
+    _loadedImages.reserve(maxImageCount);
+    _dependencies.reserve(maxImageCount*16);
+
+    _makingDyldCacheImages = true;
+    _makingCustomerCache   = customerCache;
+    _aliases               = &aliases;
+
+    // build _loadedImages[] with every dylib in cache
+    __block ImageNum imageNum = _startImageNum;
+    for (const CachedDylibInfo& aDylibInfo : dylibs)  {
+        BuilderLoadedImage entry;
+        entry.loadedFileInfo                = aDylibInfo.fileInfo;
+        entry.imageNum                      = imageNum++;
+        entry.unmapWhenDone                 = false;
+        entry.contentRebased                = false;
+        entry.hasInits                      = false;
+        entry.markNeverUnload               = true;
+        entry.rtldLocal                     = false;
+        entry.isBadImage                    = false;
+        entry.overrideImageNum              = 0;
+        _loadedImages.push_back(entry);
+    }
+
+    // wire up dependencies between cached dylibs
+    for (BuilderLoadedImage& li : _loadedImages) {
+        LoadedImageChain chainStart = { nullptr, li };
+        recursiveLoadDependents(chainStart);
+        if ( _diag.hasError() )
+            break;
+    }
+    assert(_loadedImages.count() == dylibs.count());
+
+    // create an ImageWriter for each cached dylib
+    STACK_ALLOC_ARRAY(ImageWriter, writers, _loadedImages.count());
+    for (BuilderLoadedImage& li : _loadedImages) {
+        writers.push_back(ImageWriter());
+        buildImage(writers.back(), li);
+    }
+
+    // add initializer order into each dylib
+    for (const BuilderLoadedImage& li : _loadedImages) {
+        uint32_t index = li.imageNum - _startImageNum;
+        computeInitOrder(writers[index], index);
+    }
+
+    // add exports patch info for each dylib
+    for (const BuilderLoadedImage& li : _loadedImages) {
+        uint32_t index = li.imageNum - _startImageNum;
+        addCachePatchInfo(writers[index], li);
+    }
+
+    // combine all Image objects into one ImageArray
+    ImageArrayWriter imageArrayWriter(_startImageNum, (uint32_t)writers.count());
+    for (ImageWriter& writer : writers) {
+        imageArrayWriter.appendImage(writer.finalize());
+        writer.deallocate();
+    }
+    const ImageArray* imageArray = imageArrayWriter.finalize();
+
+    return imageArray;
+}
+
+
+#if BUILDING_CACHE_BUILDER
+const ImageArray* ClosureBuilder::makeOtherDylibsImageArray(const Array<LoadedFileInfo>& otherDylibs, uint32_t cachedDylibsCount)
+{
+    // because this is run in cache builder using dispatch_apply() there is minimal stack space
+    // so set up storage for all arrays to be vm_allocated
+    uintptr_t maxImageCount = otherDylibs.count() + cachedDylibsCount + 128;
+    _loadedImages.reserve(maxImageCount);
+    _dependencies.reserve(maxImageCount*16);
+
+    // build _loadedImages[] with every dylib in cache, followed by others
+    _nextIndex = 0;
+    for (const LoadedFileInfo& aDylibInfo : otherDylibs)  {
+        BuilderLoadedImage entry;
+        entry.loadedFileInfo                = aDylibInfo;
+        entry.imageNum                      = _startImageNum + _nextIndex++;
+        entry.unmapWhenDone                 = false;
+        entry.contentRebased                = false;
+        entry.hasInits                      = false;
+        entry.markNeverUnload               = false;
+        entry.rtldLocal                     = false;
+        entry.isBadImage                    = false;
+        entry.overrideImageNum              = 0;
+        _loadedImages.push_back(entry);
+    }
+
+    // wire up dependencies between cached dylibs
+    // Note, _loadedImages can grow when we call recursiveLoadDependents so we need
+    // to check the count on each iteration.
+    for (uint64_t index = 0; index != _loadedImages.count(); ++index) {
+        BuilderLoadedImage& li = _loadedImages[index];
+        LoadedImageChain chainStart = { nullptr, li };
+        recursiveLoadDependents(chainStart);
+        if ( _diag.hasError() ) {
+            _diag.warning("while building dlopen closure for %s: %s", li.loadedFileInfo.path, _diag.errorMessage().c_str());
+            //fprintf(stderr, "while building dlopen closure for %s: %s\n", li.loadedFileInfo.path, _diag.errorMessage().c_str());
+            _diag.clearError();
+            li.isBadImage = true;    // mark bad
+        }
+    }
+
+    auto invalidateBadImages = [&]() {
+        // Invalidate images with bad dependencies
+        while (true) {
+            bool madeChange = false;
+            for (BuilderLoadedImage& li : _loadedImages) {
+                if (li.isBadImage) {
+                    // Already invalidated
+                    continue;
+                }
+                for (Image::LinkedImage depIndex : li.dependents) {
+                    if ( depIndex.imageNum() == kMissingWeakLinkedImage )
+                        continue;
+                    if ( depIndex.imageNum() < dyld3::closure::kLastDyldCacheImageNum )
+                        continue;
+                    BuilderLoadedImage& depImage = findLoadedImage(depIndex.imageNum());
+                    if (depImage.isBadImage) {
+                        _diag.warning("while building dlopen closure for %s: dependent dylib had error", li.loadedFileInfo.path);
+                        li.isBadImage = true;    // mark bad
+                        madeChange = true;
+                    }
+                }
+            }
+            if (!madeChange)
+                break;
+        }
+    };
+
+    invalidateBadImages();
+
+    // create an ImageWriter for each cached dylib
+    STACK_ALLOC_ARRAY(ImageWriter, writers, _loadedImages.count());
+    for (BuilderLoadedImage& li : _loadedImages) {
+        if ( li.imageNum == 0 )  {
+            writers.push_back(ImageWriter());
+            writers.back().setInvalid();
+            continue;
+        }
+        if ( li.imageNum < dyld3::closure::kLastDyldCacheImageNum )
+            continue;
+        writers.push_back(ImageWriter());
+        buildImage(writers.back(), li);
+        if ( _diag.hasError() ) {
+            _diag.warning("while building dlopen closure for %s: %s", li.loadedFileInfo.path, _diag.errorMessage().c_str());
+            //fprintf(stderr, "while building dlopen closure for %s: %s\n", li.loadedFileInfo.path, _diag.errorMessage().c_str());
+            _diag.clearError();
+            li.isBadImage = true;    // mark bad
+            writers.back().setInvalid();
+        }
+    }
+
+    invalidateBadImages();
+
+    // add initializer order into each dylib
+    for (const BuilderLoadedImage& li : _loadedImages) {
+        if ( li.imageNum < dyld3::closure::kLastDyldCacheImageNum )
+            continue;
+        if (li.isBadImage)
+            continue;
+        uint32_t index = li.imageNum - _startImageNum;
+        computeInitOrder(writers[index], index);
+    }
+
+    // combine all Image objects into one ImageArray
+    ImageArrayWriter imageArrayWriter(_startImageNum, (uint32_t)writers.count());
+    for (ImageWriter& writer : writers) {
+        imageArrayWriter.appendImage(writer.finalize());
+        writer.deallocate();
+    }
+    const ImageArray* imageArray = imageArrayWriter.finalize();
+
+    return imageArray;
+}
+#endif
+
+
+bool ClosureBuilder::inLoadedImageArray(const Array<LoadedImage>& loadedList, ImageNum imageNum)
+{
+    for (const LoadedImage& ali : loadedList) {
+        if ( ali.image()->representsImageNum(imageNum) )
+            return true;
+    }
+    return false;
+}
+
+void ClosureBuilder::buildLoadOrderRecurse(Array<LoadedImage>& loadedList, const Array<const ImageArray*>& imagesArrays, const Image* image)
+{
+    // breadth first load
+    STACK_ALLOC_ARRAY(const Image*, needToRecurse, 256);
+    image->forEachDependentImage(^(uint32_t dependentIndex, dyld3::closure::Image::LinkKind kind, ImageNum depImageNum, bool &stop) {
+        if ( !inLoadedImageArray(loadedList, depImageNum) ) {
+            const Image* depImage = ImageArray::findImage(imagesArrays, depImageNum);
+            loadedList.push_back(LoadedImage::make(depImage));
+            needToRecurse.push_back(depImage);
+        }
+    });
+
+    // recurse load
+    for (const Image* img : needToRecurse) {
+        buildLoadOrderRecurse(loadedList, imagesArrays, img);
+    }
+}
+
+void ClosureBuilder::buildLoadOrder(Array<LoadedImage>& loadedList, const Array<const ImageArray*>& imagesArrays, const Closure* toAdd)
+{
+    const dyld3::closure::Image* topImage = ImageArray::findImage(imagesArrays, toAdd->topImage());
+       loadedList.push_back(LoadedImage::make(topImage));
+       buildLoadOrderRecurse(loadedList, imagesArrays, topImage);
+}
+
+
+
+} // namespace closure
+} // namespace dyld3
diff --git a/dyld3/ClosureBuilder.h b/dyld3/ClosureBuilder.h
new file mode 100644 (file)
index 0000000..dfb9b2b
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * 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 ClosureBuilder_h
+#define ClosureBuilder_h
+
+
+#include "Closure.h"
+#include "ClosureFileSystem.h"
+#include "ClosureWriter.h"
+#include "PathOverrides.h"
+#include "DyldSharedCache.h"
+#include "MachOAnalyzer.h"
+#include "Loading.h"
+
+
+
+namespace dyld3 {
+
+
+namespace closure {
+
+
+
+class VIS_HIDDEN ClosureBuilder
+{
+public:
+
+    struct LaunchErrorInfo
+    {
+        uintptr_t       kind;
+        const char*     clientOfDylibPath;
+        const char*     targetDylibPath;
+        const char*     symbol;
+    };
+
+    struct ResolvedTargetInfo
+    {
+        const MachOLoaded* foundInDylib;
+        const char*        requestedSymbolName;
+        const char*        foundSymbolName;
+        uint64_t           addend;
+        bool               weakBindCoalese;
+        bool               weakBindSameImage;
+        bool               isWeakDef;
+        int                libOrdinal;
+    };
+
+    typedef Image::PatchableExport::PatchLocation  PatchLocation;
+
+    struct CacheDylibsBindingHandlers
+    {
+        struct PatchInfo
+        {
+            const char*             exportSymbolName;
+            uint32_t                exportCacheOffset;
+            uint32_t                usesCount;
+            const PatchLocation*    usesArray;
+        };
+
+        void        (^rebase)(ImageNum, const MachOLoaded* imageToFix, uint32_t runtimeOffset);
+        void        (^bind)(ImageNum, const MachOLoaded* imageToFix, uint32_t runtimeOffset, Image::ResolvedSymbolTarget target, const ResolvedTargetInfo& targetInfo);
+        void        (^chainedBind)(ImageNum, const MachOLoaded*, const Array<uint64_t>& starts, const Array<Image::ResolvedSymbolTarget>& targets, const Array<ResolvedTargetInfo>& targetInfos);
+        void        (^forEachExportsPatch)(ImageNum, void (^handler)(const PatchInfo&));
+   };
+
+    enum class AtPath { none, all, onlyInRPaths };
+
+                                ClosureBuilder(uint32_t startImageNum, const FileSystem& fileSystem, const DyldSharedCache* dyldCache, bool dyldCacheIsLive,
+                                               const PathOverrides& pathOverrides, AtPath atPathHandling=AtPath::all,
+                                               LaunchErrorInfo* errorInfo=nullptr,
+                                               const char* archName=MachOFile::currentArchName(), Platform platform=MachOFile::currentPlatform(),
+                                               const CacheDylibsBindingHandlers* handlers=nullptr);
+                                ~ClosureBuilder();
+    Diagnostics&                diagnostics() { return _diag; }
+
+    const LaunchClosure*        makeLaunchClosure(const LoadedFileInfo& fileInfo, bool allowInsertFailures);
+
+    const LaunchClosure*        makeLaunchClosure(const char* mainPath,bool allowInsertFailures);
+
+
+    static const DlopenClosure* sRetryDlopenClosure;
+    const DlopenClosure*        makeDlopenClosure(const char* dylibPath, const LaunchClosure* mainClosure, const Array<LoadedImage>& loadedList,
+                                                  closure::ImageNum callerImageNum, bool noLoad, bool canUseSharedCacheClosure,
+                                                  closure::ImageNum* topImageNum);
+
+    ImageNum                    nextFreeImageNum() const { return _startImageNum + _nextIndex; }
+
+
+    struct PatchableExport
+    {
+        uint32_t                cacheOffsetOfImpl;
+        uint32_t                cacheOffsetOfName;
+        uint32_t                patchLocationsCount;
+        const PatchLocation*    patchLocations;
+    };
+
+    struct CachedDylibInfo
+    {
+        LoadedFileInfo          fileInfo;
+    };
+
+    struct CachedDylibAlias
+    {
+        const char*             realPath;
+        const char*             aliasPath;
+    };
+
+    const ImageArray*           makeDyldCacheImageArray(bool customerCache, const Array<CachedDylibInfo>& dylibs, const Array<CachedDylibAlias>& aliases);
+
+    const ImageArray*           makeOtherDylibsImageArray(const Array<LoadedFileInfo>& otherDylibs, uint32_t cachedDylibsCount);
+
+    static void                 buildLoadOrder(Array<LoadedImage>& loadedList, const Array<const ImageArray*>& imagesArrays, const Closure* toAdd);
+
+private:
+
+
+    struct InitInfo
+    {
+        uint32_t    initOrder;
+        bool        danglingUpward;
+        bool        visited;
+    };
+
+    struct BuilderLoadedImage
+    {
+        Array<Image::LinkedImage>   dependents;
+        ImageNum                    imageNum;
+        uint32_t                    unmapWhenDone      : 1,
+                                    contentRebased     : 1,
+                                    hasInits           : 1,
+                                    markNeverUnload    : 1,
+                                    rtldLocal          : 1,
+                                    isBadImage         : 1,
+                                    padding            : 14,
+                                    overrideImageNum   : 12;
+        LoadedFileInfo              loadedFileInfo;
+
+        // Convenience method to get the information from the loadedFileInfo
+        const MachOAnalyzer*        loadAddress() const { return (const MachOAnalyzer*)loadedFileInfo.fileContent; }
+        const char*                 path() const { return loadedFileInfo.path; }
+    };
+
+    struct LoadedImageChain
+    {
+        LoadedImageChain*    previous;
+        BuilderLoadedImage&  image;
+    };
+
+
+    void                    recursiveLoadDependents(LoadedImageChain& forImageChain);
+    void                    loadDanglingUpwardLinks();
+    const char*             resolvePathVar(const char* loadPath, const LoadedImageChain& forImageChain, bool implictRPath);
+    bool                    findImage(const char* loadPath, const LoadedImageChain& forImageChain, BuilderLoadedImage*& foundImage, bool mustBeDylib, bool allowOther=true);
+    void                    buildImage(ImageWriter& writer, BuilderLoadedImage& forImage);
+    void                    addSegments(ImageWriter& writer, const MachOAnalyzer* mh);
+    void                    addRebaseInfo(ImageWriter& writer, const MachOAnalyzer* mh);
+    void                    addSynthesizedRebaseInfo(ImageWriter& writer, const MachOAnalyzer* mh);
+    void                    addSynthesizedBindInfo(ImageWriter& writer, const MachOAnalyzer* mh);
+    void                    addBindInfo(ImageWriter& writer, BuilderLoadedImage& forImage);
+    void                    reportRebasesAndBinds(ImageWriter& writer, BuilderLoadedImage& forImage);
+    void                    addChainedFixupInfo(ImageWriter& writer, const BuilderLoadedImage& forImage);
+    void                    addInterposingTuples(LaunchClosureWriter& writer, const Image* image, const MachOAnalyzer* mh);
+    void                    computeInitOrder(ImageWriter& writer, uint32_t loadIndex);
+    void                    addCachePatchInfo(ImageWriter& writer, const BuilderLoadedImage& forImage);
+    void                    addClosureInfo(LaunchClosureWriter& closureWriter);
+    void                    depthFirstRecurseSetInitInfo(uint32_t loadIndex, InitInfo initInfos[], uint32_t& initOrder, bool& hasError);
+    bool                    findSymbol(const BuilderLoadedImage& fromImage, int libraryOrdinal, const char* symbolName, bool weakImport, uint64_t addend,
+                                       Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo);
+    bool                    findSymbolInImage(const MachOAnalyzer* macho, const char* symbolName, uint64_t addend, bool followReExports, Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo);
+    const MachOAnalyzer*    machOForImageNum(ImageNum imageNum);
+    ImageNum                imageNumForMachO(const MachOAnalyzer* mh);
+    const MachOAnalyzer*    findDependent(const MachOLoaded* mh, uint32_t depIndex);
+    BuilderLoadedImage&     findLoadedImage(ImageNum imageNum);
+    BuilderLoadedImage&     findLoadedImage(const MachOAnalyzer* mh);
+    uint32_t                index(const BuilderLoadedImage&);
+    bool                    expandAtLoaderPath(const char* loadPath, bool fromLCRPATH, const BuilderLoadedImage& loadedImage, char fixedPath[]);
+    bool                    expandAtExecutablePath(const char* loadPath, bool fromLCRPATH, char fixedPath[]);
+    void                    addMustBeMissingPath(const char* path);
+    const char*             strdup_temp(const char* path);
+    bool                    overridableDylib(const BuilderLoadedImage& forImage);
+    void                    forEachBind(BuilderLoadedImage& forImage, void (^handler)(uint64_t runtimeOffset, Image::ResolvedSymbolTarget target, const ResolvedTargetInfo& targetInfo, bool& stop),
+                                                                      void (^strongHandler)(const char* strongSymbolName));
+
+    static bool             inLoadedImageArray(const Array<LoadedImage>& loadedList, ImageNum imageNum);
+    static void             buildLoadOrderRecurse(Array<LoadedImage>& loadedList, const Array<const ImageArray*>& imagesArrays, const Image* toAdd);
+
+    const FileSystem&                       _fileSystem;
+    const DyldSharedCache* const            _dyldCache;
+    const PathOverrides&                    _pathOverrides;
+    const char* const                       _archName;
+    Platform const                          _platform;
+    uint32_t const                          _startImageNum;
+    const ImageArray*                       _dyldImageArray        = nullptr;
+    const CacheDylibsBindingHandlers*       _handlers              = nullptr;
+    const Array<CachedDylibAlias>*          _aliases               = nullptr;
+    const AtPath                            _atPathHandling        = AtPath::none;
+    uint32_t                                _mainProgLoadIndex     = 0;
+    Diagnostics                             _diag;
+    LaunchErrorInfo*                        _launchErrorInfo       = nullptr;
+    PathPool*                               _tempPaths             = nullptr;
+    PathPool*                               _mustBeMissingPaths    = nullptr;
+    uint32_t                                _nextIndex             = 0;
+    OverflowSafeArray<BuilderLoadedImage>   _loadedImages;
+    OverflowSafeArray<Image::LinkedImage,65536> _dependencies;                  // all dylibs in cache need ~20,000 edges
+    OverflowSafeArray<InterposingTuple>     _interposingTuples;
+    OverflowSafeArray<Closure::PatchEntry>  _weakDefCacheOverrides;
+    OverflowSafeArray<const char*>          _weakDefsFromChainedBinds;
+    uint32_t                                _alreadyInitedIndex    = 0;
+    bool                                    _isLaunchClosure       = false;
+    bool                                    _makingDyldCacheImages = false;
+    bool                                    _dyldCacheIsLive       = false;    // means kernel is rebasing dyld cache content being viewed
+    bool                                    _makingClosuresInCache = false;
+    bool                                    _makingCustomerCache   = false;
+    bool                                    _atPathUsed            = false;
+    bool                                    _fallbackPathUsed      = false;
+    ImageNum                                _libDyldImageNum       = 0;
+    ImageNum                                _libSystemImageNum     = 0;
+};
+
+
+
+
+
+} //  namespace closure
+} //  namespace dyld3
+
+
+#endif /* ClosureBuilder_h */
diff --git a/dyld3/ClosureFileSystem.h b/dyld3/ClosureFileSystem.h
new file mode 100644 (file)
index 0000000..bb9fe8c
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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 ClosureFileSystem_h
+#define ClosureFileSystem_h
+
+// For MAXPATHLEN
+#include <sys/param.h>
+// For va_list
+#include <stdarg.h>
+// For uint64_t
+#include <stdint.h>
+
+namespace dyld3 {
+namespace closure {
+
+struct LoadedFileInfo {
+    const void*  fileContent                = nullptr;
+    uint64_t     fileContentLen             = 0;
+    uint64_t     sliceOffset                = 0;
+    uint64_t     sliceLen                   = 0;
+    uint64_t     inode                      = 0;
+    uint64_t     mtime                      = 0;
+    void (*unload)(const LoadedFileInfo&)   = nullptr;
+    const char*  path                       = nullptr;
+};
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+class FileSystem {
+protected:
+    FileSystem() { }
+
+public:
+
+    // Get the real path for a given path, if it exists.
+    // Returns true if the real path was found and updates the given buffer iff that is the case
+    virtual bool getRealPath(const char possiblePath[MAXPATHLEN], char realPath[MAXPATHLEN]) const = 0;
+
+    // Returns true on success.  If an error occurs the given callback will be called with the reason.
+    // On success, info is filled with info about the loaded file.  If the path supplied includes a symlink,
+    // the supplier realerPath is filled in with the real path of the file, otherwise it is set to the empty string.
+    virtual bool loadFile(const char* path, LoadedFileInfo& info, char realerPath[MAXPATHLEN], void (^error)(const char* format, ...)) const = 0;
+
+    // Frees the buffer allocated by loadFile()
+    virtual void unloadFile(const LoadedFileInfo& info) const = 0;
+
+    // Frees all but the requested range and adjusts info to new buffer location
+    // Remaining buffer can be freed later with unloadFile()
+    virtual void unloadPartialFile(LoadedFileInfo& info, uint64_t keepStartOffset, uint64_t keepLength) const = 0;
+
+    // If a file exists at path, returns true and sets inode and mtime
+    virtual bool fileExists(const char* path, uint64_t* inode=nullptr, uint64_t* mtime=nullptr, bool* issetuid=nullptr) const = 0;
+};
+#pragma clang diagnostic pop
+
+} //  namespace closure
+} //  namespace dyld3
+
+#endif /* ClosureFileSystem_h */
diff --git a/dyld3/ClosureFileSystemPhysical.cpp b/dyld3/ClosureFileSystemPhysical.cpp
new file mode 100644 (file)
index 0000000..d399bdf
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * 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 "ClosureFileSystemPhysical.h"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sandbox.h>
+#include <sandbox/private.h>
+#include <unistd.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+using dyld3::closure::FileSystemPhysical;
+
+bool FileSystemPhysical::getRealPath(const char possiblePath[MAXPATHLEN], char realPath[MAXPATHLEN]) const {
+    bool success = false;
+    int fd = ::open(possiblePath, O_RDONLY);
+    if ( fd != -1 ) {
+        success = fcntl(fd, F_GETPATH, realPath) == 0;
+        ::close(fd);
+    }
+    if (success)
+        return success;
+    realpath(possiblePath, realPath);
+    int realpathErrno = errno;
+    // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT
+    return (realpathErrno == ENOENT) || (realpathErrno == 0);
+}
+
+static bool sandboxBlocked(const char* path, const char* kind)
+{
+#if TARGET_IPHONE_SIMULATOR
+    // sandbox calls not yet supported in dyld_sim
+    return false;
+#else
+    sandbox_filter_type filter = (sandbox_filter_type)(SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT);
+    return ( sandbox_check(getpid(), kind, filter, path) > 0 );
+#endif
+}
+
+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");
+}
+
+// Returns true on success.  If an error occurs the given callback will be called with the reason.
+// On success, info is filled with info about the loaded file.  If the path supplied includes a symlink,
+// the supplier realerPath is filled in with the real path of the file, otherwise it is set to the empty string.
+bool FileSystemPhysical::loadFile(const char* path, LoadedFileInfo& info, char realerPath[MAXPATHLEN], void (^error)(const char* format, ...)) const {
+    // open file
+    const char* originalPath    = path;
+    char altPath[PATH_MAX];
+    int fd = -1;
+    if  ( _fileSystemPrefix != nullptr ) {
+        strlcpy(altPath, _fileSystemPrefix, PATH_MAX);
+        strlcat(altPath, path, PATH_MAX);
+        fd = ::open(altPath, O_RDONLY, 0);
+        if ( fd != -1 )
+            path = altPath;
+    }
+    if ( fd == -1 ) {
+        fd = ::open(path, O_RDONLY, 0);
+        if ( fd == -1 ) {
+            int openErrno = errno;
+            if ( (openErrno == EPERM) && sandboxBlockedOpen(path) )
+                error("file system sandbox blocked open(\"%s\", O_RDONLY)", path);
+            else if ( (openErrno != ENOENT) && (openErrno != ENOTDIR) )
+                error("open(\"%s\", O_RDONLY) failed with errno=%d", path, openErrno);
+            return false;
+        }
+    }
+
+    // Get the realpath of the file if it is a symlink
+    if ( fcntl(fd, F_GETPATH, realerPath) == 0 ) {
+        // Don't set the realpath if it is just the same as the regular path
+        if ( strcmp(originalPath, realerPath) == 0 )
+            realerPath[0] = '\0';
+    } else {
+        error("Could not get real path for \"%s\"\n", path);
+        ::close(fd);
+        return false;
+    }
+
+    // get file info
+    struct stat statBuf;
+#if TARGET_IPHONE_SIMULATOR
+    if ( ::stat(path, &statBuf) != 0 ) {
+#else
+    if ( ::fstat(fd, &statBuf) != 0 ) {
+#endif
+        int statErr = errno;
+        if ( (statErr == EPERM) && sandboxBlockedStat(path) )
+            error("file system sandbox blocked stat(\"%s\")", path);
+        else
+            error("stat(\"%s\") failed with errno=%d", path, errno);
+        ::close(fd);
+        return false;
+    }
+
+    // only regular files can be loaded
+    if ( !S_ISREG(statBuf.st_mode) ) {
+        error("not a file for %s", path);
+        ::close(fd);
+        return false;
+    }
+
+    // mach-o files must be at list one page in size
+    if ( statBuf.st_size < 4096  ) {
+        error("file too short %s", path);
+        ::close(fd);
+        return false;
+    }
+
+    info.fileContent = nullptr;
+    info.fileContentLen = statBuf.st_size;
+    info.sliceOffset = 0;
+    info.sliceLen = statBuf.st_size;
+    info.inode = statBuf.st_ino;
+    info.mtime = statBuf.st_mtime;
+    info.path  = originalPath;
+
+    // mmap() whole file
+    void* wholeFile = ::mmap(nullptr, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE|MAP_RESILIENT_CODESIGN, fd, 0);
+    if ( wholeFile == MAP_FAILED ) {
+        int mmapErr = errno;
+        if ( mmapErr == EPERM ) {
+            if ( sandboxBlockedMmap(path) )
+                error("file system sandbox blocked mmap() of '%s'", path);
+            else
+                error("code signing blocked mmap() of '%s'", path);
+        }
+        else {
+            error("mmap() failed with errno=%d for %s", errno, path);
+        }
+        ::close(fd);
+        return false;
+    }
+    info.fileContent = wholeFile;
+
+    // Set unmap as the unload method.
+    info.unload = [](const LoadedFileInfo& info) {
+        ::munmap((void*)info.fileContent, (size_t)info.fileContentLen);
+    };
+
+    ::close(fd);
+    return true;
+}
+
+void FileSystemPhysical::unloadFile(const LoadedFileInfo& info) const {
+    if (info.unload)
+        info.unload(info);
+}
+
+void FileSystemPhysical::unloadPartialFile(LoadedFileInfo& info, uint64_t keepStartOffset, uint64_t keepLength) const {
+    // Unmap from 0..keepStartOffset and (keepStartOffset+keepLength)..info.fileContentLen
+    if (keepStartOffset)
+        ::munmap((void*)info.fileContent, (size_t)keepStartOffset);
+    if ((keepStartOffset + keepLength) != info.fileContentLen) {
+        // Round up to page alignment
+        keepLength = (keepLength + PAGE_SIZE - 1) & (-PAGE_SIZE);
+        ::munmap((void*)((char*)info.fileContent + keepStartOffset + keepLength), (size_t)(info.fileContentLen - (keepStartOffset + keepLength)));
+    }
+    info.fileContent = (const void*)((char*)info.fileContent + keepStartOffset);
+    info.fileContentLen = keepLength;
+}
+
+bool FileSystemPhysical::fileExists(const char* path, uint64_t* inode, uint64_t* mtime, bool* issetuid) const {
+    struct stat statBuf;
+    if ( _fileSystemPrefix != nullptr ) {
+        char altPath[PATH_MAX];
+        strlcpy(altPath, _fileSystemPrefix, PATH_MAX);
+        strlcat(altPath, path, PATH_MAX);
+        if ( ::stat(altPath, &statBuf) == 0 ) {
+            if (inode)
+                *inode = statBuf.st_ino;
+            if (mtime)
+                *mtime = statBuf.st_mtime;
+            if (issetuid)
+                *issetuid = (statBuf.st_mode & (S_ISUID|S_ISGID));
+            return true;
+        }
+    }
+    if ( ::stat(path, &statBuf) != 0 )
+        return false;
+    if (inode)
+        *inode = statBuf.st_ino;
+    if (mtime)
+        *mtime = statBuf.st_mtime;
+    if (issetuid)
+        *issetuid = (statBuf.st_mode & (S_ISUID|S_ISGID));
+    return true;
+}
diff --git a/dyld3/ClosureFileSystemPhysical.h b/dyld3/ClosureFileSystemPhysical.h
new file mode 100644 (file)
index 0000000..ecb0fbf
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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 ClosureFileSystemPhysical_h
+#define ClosureFileSystemPhysical_h
+
+#include "ClosureFileSystem.h"
+
+namespace dyld3 {
+namespace closure {
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+class __attribute__((visibility("hidden"))) FileSystemPhysical : public FileSystem {
+public:
+    FileSystemPhysical(const char* fileSystemPrefix = nullptr) : FileSystem(), _fileSystemPrefix(fileSystemPrefix) { }
+
+    bool getRealPath(const char possiblePath[MAXPATHLEN], char realPath[MAXPATHLEN]) const override;
+
+    bool loadFile(const char* path, LoadedFileInfo& info, char realerPath[MAXPATHLEN], void (^error)(const char* format, ...)) const override;
+
+    void unloadFile(const LoadedFileInfo& info) const override;
+
+    void unloadPartialFile(LoadedFileInfo& info, uint64_t keepStartOffset, uint64_t keepLength) const override;
+
+    bool fileExists(const char* path, uint64_t* inode=nullptr, uint64_t* mtime=nullptr, bool* issetuid=nullptr) const override;
+
+private:
+    const char* _fileSystemPrefix;
+};
+#pragma clang diagnostic pop
+
+} //  namespace closure
+} //  namespace dyld3
+
+#endif /* ClosureFileSystemPhysical_h */
diff --git a/dyld3/ClosurePrinter.cpp b/dyld3/ClosurePrinter.cpp
new file mode 100644 (file)
index 0000000..98b27df
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+ * 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 "ClosurePrinter.h"
+#include "JSONWriter.h"
+
+using namespace dyld3::json;
+
+namespace dyld3 {
+namespace closure {
+
+static std::string printTarget(const Array<const ImageArray*>& imagesArrays, Image::ResolvedSymbolTarget target)
+{
+       const Image* targetImage;
+       uint64_t value;
+    switch ( target.image.kind ) {
+        case Image::ResolvedSymbolTarget::kindImage:
+            targetImage = ImageArray::findImage(imagesArrays, target.image.imageNum);
+            if ( target.image.offset & 0x8000000000ULL ) {
+                uint64_t signExtend = target.image.offset | 0xFFFFFF0000000000ULL;
+                return std::string("bind to ") + targetImage->leafName() + " - " + hex8(-signExtend);
+            }
+            else
+                return std::string("bind to ") + targetImage->leafName() + " + " + hex8(target.image.offset);
+            break;
+        case Image::ResolvedSymbolTarget::kindSharedCache:
+            return std::string("bind to dyld cache + ") + hex8(target.sharedCache.offset);
+            break;
+        case Image::ResolvedSymbolTarget::kindAbsolute:
+            value = target.absolute.value;
+            if ( value & 0x2000000000000000LL )
+                value |= 0xC000000000000000LL;
+            return std::string("bind to absolute ") + hex(value);
+            break;
+    }
+    return "???";
+}
+
+
+static Node buildImageNode(const Image* image, const Array<const ImageArray*>& imagesArrays, bool printFixups, bool printDependentsDetails, const uint8_t* cacheStart=nullptr)
+{
+    __block Node imageNode;
+
+    if ( image->isInvalid() )
+        return imageNode;
+
+    imageNode.map["image-num"].value = hex4(image->imageNum());
+    imageNode.map["path"].value = image->path();
+    __block Node imageAliases;
+    image->forEachAlias(^(const char* aliasPath, bool& stop) {
+        Node anAlias;
+        anAlias.value = aliasPath;
+        imageAliases.array.push_back(anAlias);
+    });
+    if ( !imageAliases.array.empty() )
+        imageNode.map["aliases"] = imageAliases;
+    uuid_t uuid;
+    if ( image->getUuid(uuid) ) {
+        uuid_string_t uuidStr;
+        uuid_unparse(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["has-plus-loads"].value = (image->mayHavePlusLoads() ? "true" : "false");
+    imageNode.map["never-unload"].value = (image->neverUnload() ? "true" : "false");
+//    imageNode.map["platform-binary"].value = (image->isPlatformBinary() ? "true" : "false");
+//    if ( image->cwdMustBeThisDir() )
+//        imageNode.map["cwd-must-be-this-dir"].value = "true";
+    if ( !image->inDyldCache() ) {
+        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);
+//        }
+        uint64_t inode;
+        uint64_t mTime;
+        if ( image->hasFileModTimeAndInode(inode, mTime) ) {
+            imageNode.map["file-mod-time"].value = hex(inode);
+            imageNode.map["file-inode"].value = hex(mTime);
+        }
+        uint8_t cdHash[20];
+        if ( image->hasCdHash(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" )
+                imageNode.map["cd-hash"].value = cdHashStr;
+        }
+        else {
+    #if 0
+            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;
+    #endif
+        }
+        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->forEachFixup(^(uint64_t imageOffsetToRebase, bool &stop) {
+                // rebase
+                imageNode.map["fixups"].map[hex8(imageOffsetToRebase)].value = "rebase";
+            }, ^(uint64_t imageOffsetToBind, Image::ResolvedSymbolTarget target, bool &stop) {
+                // bind
+                imageNode.map["fixups"].map[hex8(imageOffsetToBind)].value = printTarget(imagesArrays, target);
+            }, ^(uint64_t imageOffsetStart, const Array<Image::ResolvedSymbolTarget>& targets, bool& stop) {
+                // chain
+                imageNode.map["fixups"].map[hex8(imageOffsetStart)].value = "chain-start";
+                for (const Image::ResolvedSymbolTarget& target: targets) {
+                    Node targetNode;
+                    targetNode.value = printTarget(imagesArrays, target);
+                    imageNode.map["fixups-targets"].array.push_back(targetNode);
+                }
+            });
+            image->forEachTextReloc(^(uint32_t imageOffsetToRebase, bool &stop) {
+                // rebase
+                imageNode.map["fixups"].map[hex8(imageOffsetToRebase)].value = "text rebase";
+            }, ^(uint32_t imageOffsetToBind, Image::ResolvedSymbolTarget target, bool &stop) {
+                imageNode.map["fixups"].map[hex8(imageOffsetToBind)].value = "text " + printTarget(imagesArrays, target);
+            });
+        }
+    }
+    else {
+        if ( printFixups ) {
+            image->forEachPatchableExport(^(uint32_t cacheOffsetOfImpl, const char* name) {
+                __block Node implNode;
+                implNode.map["name"].value = name;
+                implNode.map["impl-cache-offset"].value = hex8(cacheOffsetOfImpl);
+                image->forEachPatchableUseOfExport(cacheOffsetOfImpl, ^(Image::PatchableExport::PatchLocation patchLocation) {
+                    Node siteNode;
+                    siteNode.map["cache-offset"].value = hex8(patchLocation.cacheOffset);
+                    if ( patchLocation.addend != 0 )
+                        siteNode.map["addend"].value = hex(patchLocation.addend);
+                    if ( patchLocation.authenticated != 0 ) {
+                        siteNode.map["key"].value = patchLocation.keyName();
+                        siteNode.map["address-diversity"].value = patchLocation.usesAddressDiversity ? "true" : "false";
+                        siteNode.map["discriminator"].value = hex4(patchLocation.discriminator);
+                    }
+                    implNode.map["usage-sites"].array.push_back(siteNode);
+                });
+                imageNode.map["patches"].array.push_back(implNode);
+            });
+        }
+    }
+
+    // add dependents
+    image->forEachDependentImage(^(uint32_t depIndex, Image::LinkKind kind, ImageNum imageNum, bool& stop) {
+        Node depMapNode;
+        const Image* depImage = ImageArray::findImage(imagesArrays, imageNum);
+        depMapNode.map["image-num"].value = hex4(imageNum);
+        if ( depImage != nullptr )
+            depMapNode.map["path"].value      = depImage->path();
+        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 initializers
+    image->forEachInitializer(nullptr, ^(const void* initializer) {
+        Node initNode;
+        initNode.value = hex((long)initializer);
+        imageNode.map["initializer-offsets"].array.push_back(initNode);
+    });
+
+       __block Node initBeforeNode;
+    image->forEachImageToInitBefore(^(ImageNum imageToInit, bool& stop) {
+        Node beforeNode;
+        const Image* initImage = ImageArray::findImage(imagesArrays, imageToInit);
+        assert(initImage != nullptr);
+        beforeNode.value = initImage->path();
+        imageNode.map["initializer-order"].array.push_back(beforeNode);
+    });
+
+    ImageNum cacheImageNum;
+       if ( image->isOverrideOfDyldCacheImage(cacheImageNum) ) {
+        imageNode.map["override-of-dyld-cache-image"].value = ImageArray::findImage(imagesArrays, cacheImageNum)->path();
+       }
+
+
+#if 0
+    // 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 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);
+    });
+#endif
+
+    return imageNode;
+}
+
+
+static Node buildImageArrayNode(const ImageArray* imageArray, const Array<const ImageArray*>& imagesArrays, bool printFixups, bool printDependentsDetails, const uint8_t* cacheStart=nullptr)
+{
+    __block Node images;
+    imageArray->forEachImage(^(const Image* image, bool& stop) {
+         images.array.push_back(buildImageNode(image, imagesArrays, printFixups, printDependentsDetails, cacheStart));
+    });
+     return images;
+}
+
+
+static Node buildClosureNode(const DlopenClosure* closure, const Array<const ImageArray*>& imagesArrays, bool printFixups, bool printDependentsDetails)
+{
+    __block Node root;
+    root.map["images"] = buildImageArrayNode(closure->images(), imagesArrays, printFixups, printDependentsDetails);
+
+    closure->forEachPatchEntry(^(const Closure::PatchEntry& patchEntry) {
+        Node patchNode;
+        patchNode.map["func-dyld-cache-offset"].value = hex8(patchEntry.exportCacheOffset);
+        patchNode.map["func-image-num"].value         = hex8(patchEntry.overriddenDylibInCache);
+        patchNode.map["replacement"].value            = printTarget(imagesArrays, patchEntry.replacement);
+        root.map["dyld-cache-fixups"].array.push_back(patchNode);
+    });
+
+    return root;
+}
+
+static Node buildClosureNode(const LaunchClosure* closure, const Array<const ImageArray*>& imagesArrays, bool printFixups, bool printDependentsDetails)
+{
+    __block Node root;
+    root.map["images"] = buildImageArrayNode(closure->images(), imagesArrays, printFixups, printDependentsDetails);
+
+     Image::ResolvedSymbolTarget entry;
+    if ( closure->mainEntry(entry) )
+        root.map["main"].value = printTarget(imagesArrays, entry);
+    else if ( closure->startEntry(entry) )
+        root.map["start"].value = printTarget(imagesArrays, entry);
+
+    Image::ResolvedSymbolTarget libdyldEntry;
+    closure->libDyldEntry(libdyldEntry);
+    root.map["libdyld-entry"].value = printTarget(imagesArrays, libdyldEntry);
+
+    root.map["uses-@paths"].value = (closure->usedAtPaths() ? "true" : "false");
+    root.map["uses-fallback-paths"].value = (closure->usedFallbackPaths() ? "true" : "false");
+
+   // 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);
+    });
+
+    // add interposing info, if any
+    closure->forEachInterposingTuple(^(const InterposingTuple& tuple, bool& stop) {
+        Node tupleNode;
+        tupleNode.map["stock"].value   = printTarget(imagesArrays, tuple.stockImplementation);
+        tupleNode.map["replace"].value = printTarget(imagesArrays, tuple.newImplementation);
+        root.map["interposing-tuples"].array.push_back(tupleNode);
+    });
+
+    closure->forEachPatchEntry(^(const Closure::PatchEntry& patchEntry) {
+        Node patchNode;
+        patchNode.map["func-dyld-cache-offset"].value = hex8(patchEntry.exportCacheOffset);
+        patchNode.map["func-image-num"].value         = hex8(patchEntry.overriddenDylibInCache);
+        patchNode.map["replacement"].value            = printTarget(imagesArrays, patchEntry.replacement);
+        root.map["dyld-cache-fixups"].array.push_back(patchNode);
+    });
+
+    root.map["initial-image-count"].value = decimal(closure->initialLoadCount());
+
+#if 0
+
+
+    // 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 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["group-num"].value = decimal(closure.group().groupNum());
+
+
+    __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;
+#endif
+    return root;
+}
+
+void printImageAsJSON(const Image* image, const Array<const ImageArray*>& imagesArrays, bool printFixups, FILE* out)
+{
+    Node root = buildImageNode(image, imagesArrays, printFixups, false);
+    printJSON(root, 0, out);
+}
+
+void printDyldCacheImagesAsJSON(const DyldSharedCache* dyldCache, bool printFixups, FILE* out)
+{
+    const dyld3::closure::ImageArray* dylibs = dyldCache->cachedDylibsImageArray();
+    STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 2);
+    imagesArrays.push_back(dylibs);
+
+    Node root = buildImageArrayNode(dylibs, imagesArrays, printFixups, false, (uint8_t*)dyldCache);
+    printJSON(root, 0, out);
+}
+
+void printClosureAsJSON(const LaunchClosure* cls, const Array<const ImageArray*>& imagesArrays, bool printFixups, FILE* out)
+{
+    Node root = buildClosureNode(cls, imagesArrays, printFixups, false);
+    printJSON(root, 0, out);
+}
+
+void printClosureAsJSON(const DlopenClosure* cls, const Array<const ImageArray*>& imagesArrays, bool printFixups, FILE* out)
+{
+    Node root = buildClosureNode(cls, imagesArrays, printFixups, false);
+    printJSON(root, 0, out);
+}
+
+
+} // namespace closure
+} // namespace dyld3
diff --git a/dyld3/ClosurePrinter.h b/dyld3/ClosurePrinter.h
new file mode 100644 (file)
index 0000000..56f63d8
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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 ClosurePrinter_h
+#define ClosurePrinter_h
+
+#include <stdio.h>
+
+#include "Closure.h"
+#include "DyldSharedCache.h"
+
+
+namespace dyld3 {
+namespace closure {
+
+
+void printClosureAsJSON(   const LaunchClosure* cls,     const Array<const ImageArray*>& imagesArrays, bool printFixups=false, FILE* out=stdout);
+void printClosureAsJSON(   const DlopenClosure* cls,     const Array<const ImageArray*>& imagesArrays, bool printFixups=false, FILE* out=stdout);
+void printImageAsJSON(     const Image* image,           const Array<const ImageArray*>& imagesArrays, bool printFixups=false, FILE* out=stdout);
+
+void printDyldCacheImagesAsJSON(const DyldSharedCache* dyldCache, bool printFixups=false, FILE* out=stdout);
+
+
+} //  namespace closure
+} //  namespace dyld3
+
+
+#endif /* ClosurePrinter_h */
diff --git a/dyld3/ClosureWriter.cpp b/dyld3/ClosureWriter.cpp
new file mode 100644 (file)
index 0000000..17f4c91
--- /dev/null
@@ -0,0 +1,516 @@
+/*
+ * 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 <mach/vm_page_size.h>
+
+#include "ClosureWriter.h"
+#include "MachOFile.h"
+
+
+namespace dyld3 {
+namespace closure {
+
+
+////////////////////////////  ContainerTypedBytesWriter ////////////////////////////////////////
+
+
+void ContainerTypedBytesWriter::setContainerType(TypedBytes::Type containerType)
+{
+    assert(_vmAllocationStart == 0);
+    _vmAllocationSize = 1024 * 1024;
+    vm_address_t allocationAddr;
+    ::vm_allocate(mach_task_self(), &allocationAddr, _vmAllocationSize, VM_FLAGS_ANYWHERE);
+    assert(allocationAddr != 0);
+    _vmAllocationStart = (void*)allocationAddr;
+    _containerTypedBytes =  (TypedBytes*)_vmAllocationStart;
+    _containerTypedBytes->type = (uint32_t)containerType;
+    _containerTypedBytes->payloadLength = 0;
+    _end = (uint8_t*)_vmAllocationStart + sizeof(TypedBytes);
+}
+
+void* ContainerTypedBytesWriter::append(TypedBytes::Type t, const void* payload, uint32_t payloadSize)
+{
+    assert((payloadSize & 0x3) == 0);
+    if ( (uint8_t*)_end + payloadSize >= (uint8_t*)_vmAllocationStart + _vmAllocationSize ) {
+        // if current buffer too small, grow it
+        size_t growth = _vmAllocationSize;
+        if ( growth < payloadSize )
+            growth = _vmAllocationSize*((payloadSize/_vmAllocationSize)+1);
+        vm_address_t newAllocationAddr;
+        size_t newAllocationSize = _vmAllocationSize+growth;
+        ::vm_allocate(mach_task_self(), &newAllocationAddr, newAllocationSize, VM_FLAGS_ANYWHERE);
+           assert(newAllocationAddr != 0);
+        size_t currentInUse = (char*)_end - (char*)_vmAllocationStart;
+        memcpy((void*)newAllocationAddr, _vmAllocationStart, currentInUse);
+        ::vm_deallocate(mach_task_self(), (vm_address_t)_vmAllocationStart, _vmAllocationSize);
+        _end                 = (void*)(newAllocationAddr + currentInUse);
+        _vmAllocationStart   = (void*)newAllocationAddr;
+        _containerTypedBytes = (TypedBytes*)_vmAllocationStart;
+        _vmAllocationSize    = newAllocationSize;
+    }
+    assert( (uint8_t*)_end + payloadSize < (uint8_t*)_vmAllocationStart + _vmAllocationSize);
+    TypedBytes* tb = (TypedBytes*)_end;
+    tb->type   = (uint32_t)t;
+    tb->payloadLength = payloadSize;
+    if ( payload != nullptr )
+        ::memcpy(tb->payload(), payload, payloadSize);
+    _end = (uint8_t*)_end + sizeof(TypedBytes) + payloadSize;
+    _containerTypedBytes->payloadLength += sizeof(TypedBytes) + payloadSize;
+    return tb->payload();
+}
+
+const void* ContainerTypedBytesWriter::finalizeContainer()
+{
+    // trim vm allocation down to just what is needed
+    uintptr_t bufferStart = (uintptr_t)_vmAllocationStart;
+    uintptr_t used = round_page((uintptr_t)_end - bufferStart);
+    if ( used < _vmAllocationSize ) {
+        uintptr_t deallocStart = bufferStart + used;
+        ::vm_deallocate(mach_task_self(), deallocStart, _vmAllocationSize - used);
+        _end = nullptr;
+        _vmAllocationSize = used;
+    }
+    // mark vm region read-only
+    ::vm_protect(mach_task_self(), bufferStart, used, false, VM_PROT_READ);
+    return (void*)_vmAllocationStart;
+}
+
+const void* ContainerTypedBytesWriter::currentTypedBytes()
+{
+    return (void*)_vmAllocationStart;
+}
+
+void ContainerTypedBytesWriter::deallocate()
+{
+    ::vm_deallocate(mach_task_self(), (long)_vmAllocationStart, _vmAllocationSize);
+}
+
+////////////////////////////  ImageWriter ////////////////////////////////////////
+
+
+const Image* ImageWriter::finalize()
+{
+    return (Image*)finalizeContainer();
+}
+
+const Image* ImageWriter::currentImage()
+{
+    return (Image*)currentTypedBytes();
+}
+
+void ImageWriter::addPath(const char* path)
+{
+    uint32_t roundedPathLen = ((uint32_t)strlen(path) + 1 + 3) & (-4);
+    Image::PathAndHash* ph = (Image::PathAndHash*)append(TypedBytes::Type::pathWithHash, nullptr, sizeof(Image::PathAndHash)+roundedPathLen);
+    ph->hash = Image::hashFunction(path);
+    strcpy(ph->path, path);
+}
+
+Image::Flags& ImageWriter::getFlags()
+{
+    if ( _flagsOffset == -1 ) {
+        setContainerType(TypedBytes::Type::image);
+        Image::Flags flags;
+        ::bzero(&flags, sizeof(flags));
+        uint8_t* p = (uint8_t*)append(TypedBytes::Type::imageFlags, &flags, sizeof(flags));
+        _flagsOffset = (int)(p - (uint8_t*)currentTypedBytes());
+    }
+    return *((Image::Flags*)((uint8_t*)currentTypedBytes() + _flagsOffset));
+}
+
+void ImageWriter::setImageNum(ImageNum num)
+{
+   getFlags().imageNum = num;
+}
+
+void ImageWriter::setHasObjC(bool value)
+{
+    getFlags().hasObjC = value;
+}
+
+void ImageWriter::setIs64(bool value)
+{
+    getFlags().is64 = value;
+}
+
+void ImageWriter::setHasPlusLoads(bool value)
+{
+    getFlags().mayHavePlusLoads = value;
+}
+
+void ImageWriter::setIsBundle(bool value)
+{
+    getFlags().isBundle = value;
+}
+
+void ImageWriter::setIsDylib(bool value)
+{
+    getFlags().isDylib = value;
+}
+
+void ImageWriter::setIsExecutable(bool value)
+{
+    getFlags().isExecutable = value;
+}
+
+void ImageWriter::setHasWeakDefs(bool value)
+{
+    getFlags().hasWeakDefs = value;
+}
+
+void ImageWriter::setUses16KPages(bool value)
+{
+    getFlags().has16KBpages = value;
+}
+
+void ImageWriter::setOverridableDylib(bool value)
+{
+    getFlags().overridableDylib = value;
+}
+
+void ImageWriter::setInvalid()
+{
+    getFlags().isInvalid = true;
+}
+
+void ImageWriter::setInDyldCache(bool value)
+{
+    getFlags().inDyldCache = value;
+}
+
+void ImageWriter::setNeverUnload(bool value)
+{
+    getFlags().neverUnload = value;
+}
+
+void ImageWriter::setUUID(const uuid_t uuid)
+{
+    append(TypedBytes::Type::uuid, uuid, sizeof(uuid_t));
+}
+
+void ImageWriter::setCDHash(const uint8_t cdHash[20])
+{
+    append(TypedBytes::Type::cdHash, cdHash, 20);
+}
+
+void ImageWriter::setDependents(const Array<Image::LinkedImage>& deps)
+{
+    append(TypedBytes::Type::dependents, deps.begin(), (uint32_t)deps.count()*sizeof(Image::LinkedImage));
+}
+
+void ImageWriter::setDofOffsets(const Array<uint32_t>& dofSectionOffsets)
+{
+    append(TypedBytes::Type::dofOffsets, &dofSectionOffsets[0], (uint32_t)dofSectionOffsets.count()*sizeof(uint32_t));
+}
+
+void ImageWriter::setInitOffsets(const uint32_t initOffsets[], uint32_t count)
+{
+    append(TypedBytes::Type::initOffsets, initOffsets, count*sizeof(uint32_t));
+}
+
+void ImageWriter::setDiskSegments(const Image::DiskSegment segs[], uint32_t count)
+{
+    append(TypedBytes::Type::diskSegment, segs, count*sizeof(Image::DiskSegment));
+}
+
+void ImageWriter::setCachedSegments(const Image::DyldCacheSegment segs[], uint32_t count)
+{
+    append(TypedBytes::Type::cacheSegment, segs, count*sizeof(Image::DyldCacheSegment));
+}
+
+void ImageWriter::setCodeSignatureLocation(uint32_t fileOffset, uint32_t size)
+{
+    Image::CodeSignatureLocation loc;
+    loc.fileOffset = fileOffset;
+    loc.fileSize   = size;
+    append(TypedBytes::Type::codeSignLoc, &loc, sizeof(loc));
+}
+
+void ImageWriter::setFairPlayEncryptionRange(uint32_t fileOffset, uint32_t size)
+{
+    const uint32_t pageSize = getFlags().has16KBpages ? 0x4000 : 0x1000;
+    assert((fileOffset % pageSize) == 0);
+    assert((size % pageSize) == 0);
+    Image::FairPlayRange loc;
+    loc.textStartPage = fileOffset/pageSize;
+    loc.textPageCount = size/pageSize;
+    append(TypedBytes::Type::fairPlayLoc, &loc, sizeof(loc));
+}
+
+void ImageWriter::setMappingInfo(uint64_t sliceOffset, uint64_t vmSize)
+{
+    const uint32_t pageSize = getFlags().has16KBpages ? 0x4000 : 0x1000;
+    Image::MappingInfo info;
+    info.sliceOffsetIn4K = (uint32_t)(sliceOffset / 0x1000);
+    info.totalVmPages    = (uint32_t)(vmSize / pageSize);
+    append(TypedBytes::Type::mappingInfo, &info, sizeof(info));
+}
+
+void ImageWriter::setFileInfo(uint64_t inode, uint64_t mTime)
+{
+    Image::FileInfo info = { inode, mTime };
+    append(TypedBytes::Type::fileInodeAndTime, &info, sizeof(info));
+}
+
+void ImageWriter::setRebaseInfo(const Array<Image::RebasePattern>& fixups)
+{
+    append(TypedBytes::Type::rebaseFixups, fixups.begin(), (uint32_t)fixups.count()*sizeof(Image::RebasePattern));
+}
+
+void ImageWriter::setTextRebaseInfo(const Array<Image::TextFixupPattern>& fixups)
+{
+    append(TypedBytes::Type::textFixups, fixups.begin(), (uint32_t)fixups.count()*sizeof(Image::TextFixupPattern));
+}
+
+void ImageWriter::setBindInfo(const Array<Image::BindPattern>& fixups)
+{
+    append(TypedBytes::Type::bindFixups, fixups.begin(), (uint32_t)fixups.count()*sizeof(Image::BindPattern));
+}
+
+void ImageWriter::setChainedFixups(const Array<uint64_t>& starts, const Array<Image::ResolvedSymbolTarget>& targets)
+{
+    append(TypedBytes::Type::chainedFixupsStarts,  starts.begin(),  (uint32_t)starts.count()*sizeof(uint64_t));
+    append(TypedBytes::Type::chainedFixupsTargets, targets.begin(), (uint32_t)targets.count()*sizeof(Image::ResolvedSymbolTarget));
+}
+
+void ImageWriter::addExportPatchInfo(uint32_t implCacheOff, const char* name, uint32_t locCount, const Image::PatchableExport::PatchLocation* locs)
+{
+    uint32_t roundedNameLen = ((uint32_t)strlen(name) + 1 + 3) & (-4);
+    uint32_t payloadSize = sizeof(Image::PatchableExport) + locCount*sizeof(Image::PatchableExport::PatchLocation) + roundedNameLen;
+    Image::PatchableExport* buffer = (Image::PatchableExport*)append(TypedBytes::Type::cachePatchInfo, nullptr, payloadSize);
+    buffer->cacheOffsetOfImpl   = implCacheOff;
+    buffer->patchLocationsCount = locCount;
+    memcpy(&buffer->patchLocations[0], locs, locCount*sizeof(Image::PatchableExport::PatchLocation));
+    strcpy((char*)(&buffer->patchLocations[locCount]), name);
+}
+
+void ImageWriter::setAsOverrideOf(ImageNum imageNum)
+{
+    uint32_t temp = imageNum;
+    append(TypedBytes::Type::imageOverride, &temp, sizeof(temp));
+}
+
+void ImageWriter::setInitsOrder(const ImageNum images[], uint32_t count)
+{
+    append(TypedBytes::Type::initBefores, images, count*sizeof(ImageNum));
+}
+
+
+////////////////////////////  ImageArrayWriter ////////////////////////////////////////
+
+
+ImageArrayWriter::ImageArrayWriter(ImageNum startImageNum, unsigned count) : _index(0)
+{
+    setContainerType(TypedBytes::Type::imageArray);
+    _end = (void*)((uint8_t*)_end + sizeof(ImageArray) - sizeof(TypedBytes) + sizeof(uint32_t)*count);
+    _containerTypedBytes->payloadLength = sizeof(ImageArray) - sizeof(TypedBytes) + sizeof(uint32_t)*count;
+    ImageArray* ia = (ImageArray*)_containerTypedBytes;
+    ia->firstImageNum   = startImageNum;
+    ia->count           = count;
+}
+
+void ImageArrayWriter::appendImage(const Image* image)
+{
+    ImageArray* ia = (ImageArray*)_containerTypedBytes;
+    ia->offsets[_index++] = _containerTypedBytes->payloadLength;
+    append(TypedBytes::Type::image, image->payload(), image->payloadLength);
+}
+
+const ImageArray* ImageArrayWriter::finalize()
+{
+    return (ImageArray*)finalizeContainer();
+}
+
+
+////////////////////////////  ClosureWriter ////////////////////////////////////////
+
+void ClosureWriter::setTopImageNum(ImageNum imageNum)
+{
+    append(TypedBytes::Type::topImage, &imageNum, sizeof(ImageNum));
+}
+
+void ClosureWriter::addCachePatches(const Array<Closure::PatchEntry>& patches)
+{
+    append(TypedBytes::Type::cacheOverrides, patches.begin(), (uint32_t)patches.count()*sizeof(Closure::PatchEntry));
+}
+
+
+////////////////////////////  LaunchClosureWriter ////////////////////////////////////////
+
+LaunchClosureWriter::LaunchClosureWriter(const ImageArray* images)
+{
+    setContainerType(TypedBytes::Type::launchClosure);
+    append(TypedBytes::Type::imageArray, images->payload(), images->payloadLength);
+}
+
+const LaunchClosure* LaunchClosureWriter::finalize()
+{
+    return (LaunchClosure*)finalizeContainer();
+}
+
+void LaunchClosureWriter::setLibSystemImageNum(ImageNum imageNum)
+{
+    append(TypedBytes::Type::libSystemNum, &imageNum, sizeof(ImageNum));
+}
+
+void LaunchClosureWriter::setLibDyldEntry(Image::ResolvedSymbolTarget entry)
+{
+    append(TypedBytes::Type::libDyldEntry, &entry, sizeof(entry));
+}
+
+void LaunchClosureWriter::setMainEntry(Image::ResolvedSymbolTarget main)
+{
+    append(TypedBytes::Type::mainEntry, &main, sizeof(main));
+}
+
+void LaunchClosureWriter::setStartEntry(Image::ResolvedSymbolTarget start)
+{
+    append(TypedBytes::Type::startEntry, &start, sizeof(start));
+}
+
+void LaunchClosureWriter::setUsedFallbackPaths(bool value)
+{
+    getFlags().usedFallbackPaths = value;
+}
+
+void LaunchClosureWriter::setUsedAtPaths(bool value)
+{
+    getFlags().usedAtPaths = value;
+}
+
+void LaunchClosureWriter::setInitImageCount(uint32_t count)
+{
+    getFlags().initImageCount = count;
+}
+
+LaunchClosure::Flags& LaunchClosureWriter::getFlags()
+{
+    if ( _flagsOffset == -1 ) {
+        LaunchClosure::Flags flags;
+        ::bzero(&flags, sizeof(flags));
+        uint8_t* p = (uint8_t*)append(TypedBytes::Type::closureFlags, &flags, sizeof(flags));
+        _flagsOffset = (int)(p - (uint8_t*)currentTypedBytes());
+    }
+    return *((LaunchClosure::Flags*)((uint8_t*)currentTypedBytes() + _flagsOffset));
+}
+
+void LaunchClosureWriter::setMustBeMissingFiles(const Array<const char*>& paths)
+{
+    uint32_t totalSize = 0;
+    for (const char* s : paths)
+        totalSize += (strlen(s) +1);
+    totalSize = (totalSize + 3) & (-4); // align
+
+    char* buffer = (char*)append(TypedBytes::Type::missingFiles, nullptr, totalSize);
+    char* t = buffer;
+    for (const char* path : paths) {
+        for (const char* s=path; *s != '\0'; ++s)
+            *t++ = *s;
+        *t++ = '\0';
+    }
+    while (t < &buffer[totalSize])
+        *t++ = '\0';
+}
+
+void LaunchClosureWriter::addEnvVar(const char* envVar)
+{
+    unsigned len = (unsigned)strlen(envVar);
+    char temp[len+8];
+    strcpy(temp, envVar);
+    unsigned paddedSize = len+1;
+    while ( (paddedSize % 4) != 0 )
+        temp[paddedSize++] = '\0';
+    append(TypedBytes::Type::envVar, temp, paddedSize);
+}
+
+void LaunchClosureWriter::addInterposingTuples(const Array<InterposingTuple>& tuples)
+{
+    append(TypedBytes::Type::interposeTuples, tuples.begin(), (uint32_t)tuples.count()*sizeof(InterposingTuple));
+}
+
+void LaunchClosureWriter::setDyldCacheUUID(const uuid_t uuid)
+{
+    append(TypedBytes::Type::dyldCacheUUID, uuid, sizeof(uuid_t));
+}
+
+void LaunchClosureWriter::setBootUUID(const char* uuid)
+{
+    unsigned len = (unsigned)strlen(uuid);
+    char temp[len+8];
+    strcpy(temp, uuid);
+    unsigned paddedSize = len+1;
+    while ( (paddedSize % 4) != 0 )
+        temp[paddedSize++] = '\0';
+    append(TypedBytes::Type::bootUUID, temp, paddedSize);
+}
+
+void LaunchClosureWriter::applyInterposing()
+{
+    const LaunchClosure* currentClosure = (LaunchClosure*)currentTypedBytes();
+       const ImageArray*    images         = currentClosure->images();
+       currentClosure->forEachInterposingTuple(^(const InterposingTuple& tuple, bool&) {
+        images->forEachImage(^(const dyld3::closure::Image* image, bool&) {
+            for (const Image::BindPattern& bindPat : image->bindFixups()) {
+                if ( (bindPat.target == tuple.stockImplementation) && (tuple.newImplementation.image.imageNum != image->imageNum()) ) {
+                    Image::BindPattern* writePat = const_cast<Image::BindPattern*>(&bindPat);
+                    writePat->target = tuple.newImplementation;
+                }
+            }
+
+            // Chained fixups may also be interposed.  We can't change elements in the chain, but we can change
+            // the target list.
+            for (const Image::ResolvedSymbolTarget& symbolTarget : image->chainedTargets()) {
+                if ( (symbolTarget == tuple.stockImplementation) && (tuple.newImplementation.image.imageNum != image->imageNum()) ) {
+                    Image::ResolvedSymbolTarget* writeTarget = const_cast<Image::ResolvedSymbolTarget*>(&symbolTarget);
+                    *writeTarget = tuple.newImplementation;
+                }
+            }
+        });
+    });
+}
+
+////////////////////////////  DlopenClosureWriter ////////////////////////////////////////
+
+DlopenClosureWriter::DlopenClosureWriter(const ImageArray* images)
+{
+    setContainerType(TypedBytes::Type::dlopenClosure);
+    append(TypedBytes::Type::imageArray, images->payload(), images->payloadLength);
+}
+
+const DlopenClosure* DlopenClosureWriter::finalize()
+{
+    return (DlopenClosure*)finalizeContainer();
+}
+
+
+} // namespace closure
+} // namespace dyld3
+
+
+
diff --git a/dyld3/ClosureWriter.h b/dyld3/ClosureWriter.h
new file mode 100644 (file)
index 0000000..e147709
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * 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 ClosureWriter_h
+#define ClosureWriter_h
+
+
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+#include <uuid/uuid.h>
+#include <mach/mach.h>
+#include <mach-o/loader.h>
+#include <uuid/uuid.h>
+
+#include "Closure.h"
+
+
+
+namespace dyld3 {
+
+namespace closure {
+
+
+class VIS_HIDDEN ContainerTypedBytesWriter
+{
+public:
+    void        deallocate();
+
+protected:
+    void        setContainerType(TypedBytes::Type containerType);
+    void*       append(TypedBytes::Type t, const void* payload, uint32_t payloadSize);
+
+    const void* currentTypedBytes();
+    const void* finalizeContainer();
+
+    void*       _vmAllocationStart      = nullptr;
+    size_t      _vmAllocationSize       = 0;
+    TypedBytes* _containerTypedBytes    = nullptr;
+    void*       _end                    = nullptr;
+};
+
+
+class VIS_HIDDEN ImageWriter : public ContainerTypedBytesWriter
+{
+public:
+
+    void        setImageNum(ImageNum num);
+    void        addPath(const char* path); // first is canonical, others are aliases
+    void        setInvalid();
+    void        setInDyldCache(bool);
+    void        setIs64(bool);
+    void        setHasObjC(bool);
+    void        setHasPlusLoads(bool);
+    void        setIsBundle(bool);
+    void        setIsDylib(bool);
+    void        setIsExecutable(bool);
+    void        setIsRestricted(bool);
+    void        setHasWeakDefs(bool);
+    void        setUses16KPages(bool);
+    void        setOverridableDylib(bool);
+    void        setNeverUnload(bool);
+    void        setUUID(const uuid_t uuid);
+    void        setCDHash(const uint8_t cdHash[20]);
+    void        setDependents(const Array<Image::LinkedImage>& deps);
+    void        setDofOffsets(const Array<uint32_t>& dofSectionOffsets);
+    void        setInitOffsets(const uint32_t initOffsets[], uint32_t count);
+    void        setDiskSegments(const Image::DiskSegment segs[], uint32_t count);
+    void        setCachedSegments(const Image::DyldCacheSegment segs[], uint32_t count);
+    void        setCodeSignatureLocation(uint32_t fileOffset, uint32_t size);
+    void        setFairPlayEncryptionRange(uint32_t fileOffset, uint32_t size);
+    void        setMappingInfo(uint64_t sliceOffset, uint64_t vmSize);
+    void        setFileInfo(uint64_t inode, uint64_t modTime);
+    void        setRebaseInfo(const Array<Image::RebasePattern>&);
+    void        setTextRebaseInfo(const Array<Image::TextFixupPattern>&);
+    void        setBindInfo(const Array<Image::BindPattern>&);
+    void        setAsOverrideOf(ImageNum);
+    void        addExportPatchInfo(uint32_t implOff, const char* name, uint32_t locCount, const Image::PatchableExport::PatchLocation* locs);
+    void        setInitsOrder(const ImageNum images[], uint32_t count);
+    void        setChainedFixups(const Array<uint64_t>& starts, const Array<Image::ResolvedSymbolTarget>& targets);
+
+    const Image* currentImage();
+
+    const Image* finalize();
+
+private:
+    Image::Flags& getFlags();
+
+    int   _flagsOffset = -1;
+};
+
+
+class VIS_HIDDEN ImageArrayWriter : public ContainerTypedBytesWriter
+{
+public:
+                        ImageArrayWriter(ImageNum startImageNum, unsigned count);
+
+    void                appendImage(const Image*);
+    const ImageArray*   finalize();
+private:
+    unsigned            _index;
+};
+
+class VIS_HIDDEN ClosureWriter : public ContainerTypedBytesWriter
+{
+public:
+    void                    setTopImageNum(ImageNum imageNum);
+    void                    addCachePatches(const Array<Closure::PatchEntry>&);
+};
+
+class VIS_HIDDEN LaunchClosureWriter : public ClosureWriter
+{
+public:
+                            LaunchClosureWriter(const ImageArray* images);
+
+    const LaunchClosure*    finalize();
+    void                    setLibSystemImageNum(ImageNum imageNum);
+    void                    setInitImageCount(uint32_t count);
+    void                    setLibDyldEntry(Image::ResolvedSymbolTarget dyldEntry);
+    void                    setMainEntry(Image::ResolvedSymbolTarget main);
+    void                    setStartEntry(Image::ResolvedSymbolTarget start);
+    void                    setUsedFallbackPaths(bool);
+    void                    setUsedAtPaths(bool);
+    void                    setMustBeMissingFiles(const Array<const char*>& paths);
+    void                    addInterposingTuples(const Array<InterposingTuple>& tuples);
+    void                    setDyldCacheUUID(const uuid_t);
+    void                    setBootUUID(const char* uuid);
+    void                    applyInterposing();
+    void                    addEnvVar(const char* envVar);
+
+private:
+    LaunchClosure::Flags&   getFlags();
+
+    int                      _flagsOffset = -1;
+};
+
+
+class VIS_HIDDEN DlopenClosureWriter : public ClosureWriter
+{
+public:
+                             DlopenClosureWriter(const ImageArray* images);
+
+    const DlopenClosure*     finalize();
+
+};
+
+
+} //  namespace closure
+} //  namespace dyld3
+
+
+#endif // ClosureWriter_h
+
index c84a70837a0ab19dce276092198655ae1c5c303c..22af412b253eda30cc17ffa729ceddf190e45d3c 100644 (file)
@@ -47,6 +47,7 @@ enum {
     CS_HASHTYPE_SHA1              = 1,
     CS_HASHTYPE_SHA256            = 2,
     CS_HASHTYPE_SHA256_TRUNCATED  = 3,
+    CS_HASHTYPE_SHA384 = 4,
 
     CS_HASH_SIZE_SHA1             = 20,
     CS_HASH_SIZE_SHA256           = 32,
index a8e4bca27b854eac3f5889a1833c067f5de66370..06a0401ec3ac6de1c725a6402729bb8793b4a211 100644 (file)
 #endif
 
 Diagnostics::Diagnostics(bool verbose)
-#if !DYLD_IN_PROCESS
+#if BUILDING_CACHE_BUILDER
     : _verbose(verbose)
     , _prefix("")
 #endif
 {
 }
 
-#if !DYLD_IN_PROCESS
+#if BUILDING_CACHE_BUILDER
 Diagnostics::Diagnostics(const std::string& prefix, bool verbose)
     : _verbose(verbose)
     , _prefix(prefix)
@@ -77,20 +77,23 @@ Diagnostics::~Diagnostics()
 
 void Diagnostics::error(const char* format, ...)
 {
-    _buffer = _simple_salloc();
     va_list    list;
     va_start(list, format);
-    _simple_vsprintf(_buffer, format, list);
+    error(format, list);
     va_end(list);
+}
+
+void Diagnostics::error(const char* format, va_list list)
+{
+    _buffer = _simple_salloc();
+    _simple_vsprintf(_buffer, format, list);
 
-#if !DYLD_IN_PROCESS
+#if BUILDING_CACHE_BUILDER
     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);
@@ -123,7 +126,7 @@ void Diagnostics::assertNoError() const
         abort_report_np("%s", _simple_string(_buffer));
 }
 
-#if DYLD_IN_PROCESS
+#if !BUILDING_CACHE_BUILDER
 const char* Diagnostics::errorMessage() const
 {
     return _simple_string(_buffer);
index 81fcf82ae78d54a1c25da5183a61cf7be5c18215..21cdf41222848743e3fd09c55fb15b7cd789e53a 100644 (file)
@@ -27,7 +27,7 @@
 
 #include <stdint.h>
 
-#if !DYLD_IN_PROCESS
+#if BUILDING_CACHE_BUILDER
 #include <set>
 #include <string>
 #include <vector>
@@ -44,7 +44,8 @@ public:
                     ~Diagnostics();
 
     void            error(const char* format, ...)  __attribute__((format(printf, 2, 3)));
-#if !DYLD_IN_PROCESS
+    void            error(const char* format, va_list list);
+#if BUILDING_CACHE_BUILDER
                     Diagnostics(const std::string& prefix, bool verbose=false);
 
     void            warning(const char* format, ...)  __attribute__((format(printf, 2, 3)));
@@ -57,7 +58,7 @@ public:
     void            clearError();
     void            assertNoError() const;
 
-#if DYLD_IN_PROCESS
+#if !BUILDING_CACHE_BUILDER
     const char*                     errorMessage() const;
 #else
     const std::string               prefix() const;
@@ -68,7 +69,7 @@ public:
 
 private:
     void*                    _buffer = nullptr;
-#if !DYLD_IN_PROCESS
+#if BUILDING_CACHE_BUILDER
     std::string              _prefix;
     std::set<std::string>    _warnings;
     bool                     _verbose = false;
diff --git a/dyld3/DyldCacheParser.cpp b/dyld3/DyldCacheParser.cpp
deleted file mode 100644 (file)
index 4547027..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * 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
deleted file mode 100644 (file)
index 34deee4..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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/JSONWriter.h b/dyld3/JSONWriter.h
new file mode 100644 (file)
index 0000000..e25cb2e
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * 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>
+
+namespace dyld3 {
+namespace json {
+
+struct Node
+{
+    std::string                 value;
+    std::map<std::string, Node> map;
+    std::vector<Node>           array;
+};
+
+static inline std::string hex(uint64_t value) {
+    char buff[64];
+    sprintf(buff, "0x%llX", value);
+    return buff;
+}
+
+static inline std::string hex4(uint64_t value) {
+    char buff[64];
+    sprintf(buff, "0x%04llX", value);
+    return buff;
+}
+
+static inline std::string hex8(uint64_t value) {
+    char buff[64];
+    sprintf(buff, "0x%08llX", value);
+    return buff;
+}
+
+static inline std::string decimal(uint64_t value) {
+    char buff[64];
+    sprintf(buff, "%llu", value);
+    return buff;
+}
+
+static inline void indentBy(uint32_t spaces, FILE* out) {
+    for (int i=0; i < spaces; ++i) {
+        fprintf(out, " ");
+    }
+}
+
+static inline 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");
+}
+
+
+} // namespace json
+} // namespace dyld3
diff --git a/dyld3/LaunchCache.h b/dyld3/LaunchCache.h
deleted file mode 100644 (file)
index 447a2c7..0000000
+++ /dev/null
@@ -1,382 +0,0 @@
-/*
- * 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
deleted file mode 100644 (file)
index bea67ad..0000000
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * 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
deleted file mode 100644 (file)
index f5efd16..0000000
+++ /dev/null
@@ -1,457 +0,0 @@
-/*
- * 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
deleted file mode 100644 (file)
index 5b99e75..0000000
+++ /dev/null
@@ -1,1503 +0,0 @@
-/*
- * 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);
-                        }) ) {
-                        switch (foundInfo.kind) {
-                            case MachOParser::FoundSymbol::Kind::headerOffset:
-                            case MachOParser::FoundSymbol::Kind::resolverOffset:
-                                result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value);
-                                break;
-                            case MachOParser::FoundSymbol::Kind::absolute:
-                                result = (uintptr_t)foundInfo.value;
-                                break;
-                        }
-                        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) ) {
-                            switch (foundInfo.kind) {
-                                case MachOParser::FoundSymbol::Kind::headerOffset:
-                                case MachOParser::FoundSymbol::Kind::resolverOffset:
-                                    result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value);
-                                    break;
-                                case MachOParser::FoundSymbol::Kind::absolute:
-                                    result = (uintptr_t)foundInfo.value;
-                                    break;
-                            }
-                            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) ) {
-                            switch (foundInfo.kind) {
-                                case MachOParser::FoundSymbol::Kind::headerOffset:
-                                case MachOParser::FoundSymbol::Kind::resolverOffset:
-                                    result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value);
-                                    break;
-                                case MachOParser::FoundSymbol::Kind::absolute:
-                                    result = (uintptr_t)foundInfo.value;
-                                    break;
-                            }
-                            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) ) {
-                            switch (foundInfo.kind) {
-                                case MachOParser::FoundSymbol::Kind::headerOffset:
-                                case MachOParser::FoundSymbol::Kind::resolverOffset:
-                                    result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value);
-                                    break;
-                                case MachOParser::FoundSymbol::Kind::absolute:
-                                    result = (uintptr_t)foundInfo.value;
-                                    break;
-                            }
-                            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
deleted file mode 100644 (file)
index e51fbdf..0000000
+++ /dev/null
@@ -1,1285 +0,0 @@
-/*
- * 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
deleted file mode 100644 (file)
index a00ea93..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * 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
-
-
index d34431a8e6f36bfcb8625c85bd713020b9e52379..2ac09decc11ac5a9e77cba14f1ee0468ca069e0e 100644 (file)
@@ -22,6 +22,7 @@
  */
 
 
+#include <bitset>
 
 #include <stdint.h>
 #include <string.h>
 #include <sandbox.h>
 #include <sandbox/private.h>
 #include <dispatch/dispatch.h>
+#include <mach/vm_page_size.h>
 
-#include "LaunchCache.h"
-#include "LaunchCacheFormat.h"
+#include "MachOFile.h"
+#include "MachOLoaded.h"
 #include "Logging.h"
 #include "Loading.h"
-#include "MachOParser.h"
+#include "Tracing.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
+namespace {
 
-static bool sandboxBlocked(const char* path, const char* kind)
+// utility to track a set of ImageNum's in use
+class VIS_HIDDEN ImageNumSet
 {
-#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
+public:
+    void    add(dyld3::closure::ImageNum num);
+    bool    contains(dyld3::closure::ImageNum num) const;
+
+private:
+    std::bitset<5120>                                   _bitmap;
+    dyld3::OverflowSafeArray<dyld3::closure::ImageNum>  _overflowArray;
+};
+
+void ImageNumSet::add(dyld3::closure::ImageNum num)
+{
+    if ( num < 5120 )
+        _bitmap.set(num);
+    else
+        _overflowArray.push_back(num);
+}
+
+bool ImageNumSet::contains(dyld3::closure::ImageNum num) const
+{
+    if ( num < 5120 )
+        return _bitmap.test(num);
+
+    for (dyld3::closure::ImageNum existingNum : _overflowArray) {
+        if ( existingNum == num )
+            return true;
+    }
     return false;
-#endif
 }
+} // namespace anonymous
+
 
-static bool sandboxBlockedMmap(const char* path)
+namespace dyld3 {
+
+Loader::Loader(Array<LoadedImage>& storage, const void* cacheAddress, const Array<const dyld3::closure::ImageArray*>& imagesArrays,
+               LogFunc logLoads, LogFunc logSegments, LogFunc logFixups, LogFunc logDofs)
+       : _allImages(storage), _imagesArrays(imagesArrays), _dyldCacheAddress(cacheAddress),
+         _logLoads(logLoads), _logSegments(logSegments), _logFixups(logFixups), _logDofs(logDofs)
 {
-    return sandboxBlocked(path, "file-map-executable");
 }
 
-static bool sandboxBlockedOpen(const char* path)
+void Loader::addImage(const LoadedImage& li)
 {
-    return sandboxBlocked(path, "file-read-data");
+    _allImages.push_back(li);
 }
 
-static bool sandboxBlockedStat(const char* path)
+LoadedImage* Loader::findImage(closure::ImageNum targetImageNum)
 {
-    return sandboxBlocked(path, "file-read-metadata");
+    for (LoadedImage& info : _allImages) {
+        if ( info.image()->representsImageNum(targetImageNum) )
+            return &info;
+    }
+    return nullptr;
 }
 
-#if TARGET_OS_WATCH || TARGET_OS_BRIDGE
-static uint64_t pageAlign(uint64_t value)
+uintptr_t Loader::resolveTarget(closure::Image::ResolvedSymbolTarget target)
 {
-  #if __arm64__
-    return (value + 0x3FFF) & (-0x4000);
-  #else
-       return (value + 0xFFF) & (-0x1000);
-  #endif
+    const LoadedImage* info;
+    switch ( target.sharedCache.kind ) {
+        case closure::Image::ResolvedSymbolTarget::kindSharedCache:
+            assert(_dyldCacheAddress != nullptr);
+            return (uintptr_t)_dyldCacheAddress + (uintptr_t)target.sharedCache.offset;
+
+        case closure::Image::ResolvedSymbolTarget::kindImage:
+            info = findImage(target.image.imageNum);
+            assert(info != nullptr);
+            return (uintptr_t)(info->loadedAddress()) + (uintptr_t)target.image.offset;
+
+        case closure::Image::ResolvedSymbolTarget::kindAbsolute:
+            if ( target.absolute.value & (1ULL << 62) )
+                return (uintptr_t)(target.absolute.value | 0xC000000000000000ULL);
+            else
+                return (uintptr_t)target.absolute.value;
+   }
+    assert(0 && "malformed ResolvedSymbolTarget");
+    return 0;
 }
-#endif
 
-static void updateSliceOffset(uint64_t& sliceOffset, uint64_t codeSignEndOffset, size_t fileLen)
+
+void Loader::completeAllDependents(Diagnostics& diag, uintptr_t topIndex)
 {
-#if TARGET_OS_WATCH || TARGET_OS_BRIDGE
-    if ( sliceOffset != 0 ) {
-        if ( pageAlign(codeSignEndOffset) == pageAlign(fileLen) ) {
-            // cache builder saw fat file, but file is now thin
-            sliceOffset = 0;
-            return;
+    // accumulate all image overrides
+    STACK_ALLOC_ARRAY(ImageOverride, overrides, _allImages.maxCount());
+    for (const auto anArray : _imagesArrays) {
+        // ignore prebuilt Image* in dyld cache
+        if ( anArray->startImageNum() < dyld3::closure::kFirstLaunchClosureImageNum )
+            continue;
+        anArray->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
+            ImageOverride overrideEntry;
+            if ( image->isOverrideOfDyldCacheImage(overrideEntry.inCache) ) {
+                overrideEntry.replacement = image->imageNum();
+                overrides.push_back(overrideEntry);
+            }
+        });
+    }
+
+    // make cache for fast lookup of already loaded images
+    __block ImageNumSet alreadyLoaded;
+    for (int i=0; i <= topIndex; ++i) {
+        alreadyLoaded.add(_allImages[i].image()->imageNum());
+    }
+
+    // for each image in _allImages, starting at topIndex, make sure its depenents are in _allImages
+    uintptr_t index = topIndex;
+    while ( (index < _allImages.count()) && diag.noError() ) {
+        const closure::Image* image = _allImages[index].image();
+        //fprintf(stderr, "completeAllDependents(): looking at dependents of %s\n", image->path());
+        image->forEachDependentImage(^(uint32_t depIndex, closure::Image::LinkKind kind, closure::ImageNum depImageNum, bool& stop) {
+            // check if imageNum needs to be changed to an override
+            for (const ImageOverride& entry : overrides) {
+                if ( entry.inCache == depImageNum ) {
+                    depImageNum = entry.replacement;
+                    break;
+                }
+            }
+            // check if this dependent is already loaded
+            if ( !alreadyLoaded.contains(depImageNum) ) {
+                // if not, look in imagesArrays
+                const closure::Image* depImage = closure::ImageArray::findImage(_imagesArrays, depImageNum);
+                if ( depImage != nullptr ) {
+                    //dyld::log("  load imageNum=0x%05X, image path=%s\n", depImageNum, depImage->path());
+                     if ( _allImages.freeCount() == 0 ) {
+                         diag.error("too many initial images");
+                         stop = true;
+                     }
+                     else {
+                         _allImages.push_back(LoadedImage::make(depImage));
+                     }
+                    alreadyLoaded.add(depImageNum);
+                }
+                else {
+                    diag.error("unable to locate imageNum=0x%04X, depIndex=%d of %s", depImageNum, depIndex, image->path());
+                    stop = true;
+                }
+            }
+        });
+        ++index;
+    }
+}
+
+void Loader::mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool fromOFI, uintptr_t topIndex)
+{
+    // scan array and map images not already loaded
+    for (uintptr_t i=topIndex; i < _allImages.count(); ++i) {
+        LoadedImage& info = _allImages[i];
+        if ( info.loadedAddress() != nullptr ) {
+            // log main executable's segments
+            if ( (info.loadedAddress()->filetype == MH_EXECUTE) && (info.state() == LoadedImage::State::mapped) ) {
+                if ( _logSegments("dyld: mapped by kernel %s\n", info.image()->path()) ) {
+                    info.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.loadedAddress() + vmOffset;
+                        uint64_t end   = start+vmSize-1;
+                        if ( (segIndex == 0) && (permissions == 0) ) {
+                            start = 0;
+                        }
+                        _logSegments("%14s (%c%c%c) 0x%012llX->0x%012llX \n", info.loadedAddress()->segmentName(segIndex),
+                                    (permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
+                                    start, end);
+                    });
+                }
+            }
+            // skip over ones already loaded
+            continue;
+        }
+        if ( info.image()->inDyldCache() ) {
+            if ( info.image()->overridableDylib() ) {
+                struct stat statBuf;
+                if ( stat(info.image()->path(), &statBuf) == 0 ) {
+                    // verify file has not changed since closure was built
+                    uint64_t inode;
+                    uint64_t mtime;
+                    if ( info.image()->hasFileModTimeAndInode(inode, mtime) ) {
+                        if ( (statBuf.st_mtime != mtime) || (statBuf.st_ino != inode) ) {
+                            diag.error("dylib file mtime/inode changed since closure was built for '%s'", info.image()->path());
+                        }
+                    }
+                    else {
+                        diag.error("dylib file not expected on disk, must be a root '%s'", info.image()->path());
+                    }
+                }
+                else if ( (_dyldCacheAddress != nullptr) && ((dyld_cache_header*)_dyldCacheAddress)->dylibsExpectedOnDisk ) {
+                    diag.error("dylib file missing, was in dyld shared cache '%s'", info.image()->path());
+                }
+            }
+            if ( diag.noError() ) {
+                info.setLoadedAddress((MachOLoaded*)((uintptr_t)_dyldCacheAddress + info.image()->cacheOffset()));
+                info.setState(LoadedImage::State::fixedUp);
+                if ( _logSegments("dyld: Using from dyld cache %s\n", info.image()->path()) ) {
+                    info.image()->forEachCacheSegment(^(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &stop) {
+                        _logSegments("%14s (%c%c%c) 0x%012lX->0x%012lX \n", info.loadedAddress()->segmentName(segIndex),
+                                        (permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
+                                        (long)info.loadedAddress()+(long)vmOffset, (long)info.loadedAddress()+(long)vmOffset+(long)vmSize-1);
+                     });
+                }
+            }
+        }
+        else {
+            mapImage(diag, info, fromOFI);
+            if ( diag.hasError() )
+                break; // out of for loop
+        }
+
+    }
+    if ( diag.hasError() )  {
+        // bummer, need to clean up by unmapping any images just mapped
+        for (LoadedImage& info : _allImages) {
+            if ( (info.state() == LoadedImage::State::mapped) && !info.image()->inDyldCache() && !info.leaveMapped() ) {
+                _logSegments("dyld: unmapping %s\n", info.image()->path());
+                unmapImage(info);
+            }
+        }
+        return;
+    }
+
+    // apply fixups
+    for (uintptr_t i=topIndex; i < _allImages.count(); ++i) {
+        LoadedImage& info = _allImages[i];
+        // images in shared cache do not need fixups applied
+        if ( info.image()->inDyldCache() )
+            continue;
+        // previously loaded images were previously fixed up
+        if ( info.state() < LoadedImage::State::fixedUp ) {
+            applyFixupsToImage(diag, info);
+            if ( diag.hasError() )
+                break;
+            info.setState(LoadedImage::State::fixedUp);
         }
     }
+
+    // find and register dtrace DOFs
+    if ( processDOFs ) {
+        STACK_ALLOC_OVERFLOW_SAFE_ARRAY(DOFInfo, dofImages, _allImages.count());
+        for (uintptr_t i=topIndex; i < _allImages.count(); ++i) {
+            LoadedImage& info = _allImages[i];
+            info.image()->forEachDOF(info.loadedAddress(), ^(const void* section) {
+                DOFInfo dofInfo;
+                dofInfo.dof            = section;
+                dofInfo.imageHeader    = info.loadedAddress();
+                dofInfo.imageShortName = info.image()->leafName();
+                dofImages.push_back(dofInfo);
+            });
+        }
+        registerDOFs(dofImages);
+    }
+}
+
+bool Loader::sandboxBlocked(const char* path, const char* kind)
+{
+#if TARGET_IPHONE_SIMULATOR
+    // sandbox calls not yet supported in dyld_sim
+    return false;
+#else
+    sandbox_filter_type filter = (sandbox_filter_type)(SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT);
+    return ( sandbox_check(getpid(), kind, filter, path) > 0 );
 #endif
 }
 
-static const mach_header* mapImage(const dyld3::launch_cache::Image image, Diagnostics& diag, LogFunc log_loads, LogFunc log_segments)
+bool Loader::sandboxBlockedMmap(const char* path)
 {
-    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;
+    return sandboxBlocked(path, "file-map-executable");
+}
+
+bool Loader::sandboxBlockedOpen(const char* path)
+{
+    return sandboxBlocked(path, "file-read-data");
+}
+
+bool Loader::sandboxBlockedStat(const char* path)
+{
+    return sandboxBlocked(path, "file-read-metadata");
+}
+
+void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI)
+{
+    dyld3::ScopedTimer timer(DBG_DYLD_TIMING_MAP_IMAGE, info.image()->path(), 0, 0);
+
+    const closure::Image*   image         = info.image();
+    uint64_t                sliceOffset   = image->sliceOffsetInFile();
+    const uint64_t          totalVMSize   = image->vmSizeToMap();
+    uint32_t                codeSignFileOffset;
+    uint32_t                codeSignFileSize;
+    bool                    isCodeSigned  = image->hasCodeSignature(codeSignFileOffset, codeSignFileSize);
 
     // open file
-    int fd = ::open(image.path(), O_RDONLY, 0);
+    int fd = ::open(info.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());
+        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;
+            diag.error("open(\"%s\", O_RDONLY) failed with errno=%d", image->path(), openErr);
+        return;
     }
 
     // get file info
     struct stat statBuf;
 #if TARGET_IPHONE_SIMULATOR
-    if ( stat(image.path(), &statBuf) != 0 ) {
+    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());
+        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);
+            diag.error("stat(\"%s\") failed with errno=%d", image->path(), statErr);
         close(fd);
-        return nullptr;
+        return;
     }
 
     // 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());
+    uint64_t inode;
+    uint64_t mtime;
+    if ( image->hasFileModTimeAndInode(inode, mtime) ) {
+        if ( (statBuf.st_mtime != mtime) || (statBuf.st_ino != inode) ) {
+            diag.error("file mtime/inode changed since closure was built for '%s'", image->path());
             close(fd);
-            return nullptr;
+            return;
         }
     }
 
-    // handle OS dylibs being thinned after closure was built
-    if ( image.group().groupNum() == 1 )
-        updateSliceOffset(sliceOffset, codeSignFileOffset+codeSignFileSize, (size_t)statBuf.st_size);
+    // handle case on iOS where sliceOffset in closure is wrong because file was thinned after cache was built
+    if ( (_dyldCacheAddress != nullptr) && !(((dyld_cache_header*)_dyldCacheAddress)->dylibsExpectedOnDisk) ) {
+        if ( sliceOffset != 0 ) {
+            if ( round_page_kernel(codeSignFileOffset+codeSignFileSize) == round_page_kernel(statBuf.st_size) ) {
+                // file is now thin
+                sliceOffset = 0;
+            }
+        }
+    }
 
     // register code signature
     uint64_t coveredCodeLength  = UINT64_MAX;
-    if ( codeSignFileOffset != 0 ) {
+    if ( isCodeSigned ) {
+        auto sigTimer = ScopedTimer(DBG_DYLD_TIMING_ATTACH_CODESIGNATURE, 0, 0, 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
@@ -174,22 +408,25 @@ static const mach_header* mapImage(const dyld3::launch_cache::Image image, Diagn
             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());
+                            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());
+                            errnoCopy, sliceOffset, codeSignFileOffset, codeSignFileSize, image->path());
             }
             close(fd);
-            return nullptr;
+            return;
         }
         coveredCodeLength = siginfo.fs_file_start;
-        if ( coveredCodeLength <  image.asDiskImage()->codeSignFileOffset ) {
+        if ( coveredCodeLength < codeSignFileOffset ) {
             diag.error("code signature does not cover entire file up to signature");
             close(fd);
-            return nullptr;
+            return;
         }
+    }
 
+    // <rdar://problem/41015217> dyld should use F_CHECK_LV even on unsigned binaries
+    {
         // <rdar://problem/32684903> always call F_CHECK_LV to preflight
         fchecklv checkInfo;
         char  messageBuffer[512];
@@ -199,9 +436,9 @@ static const mach_header* mapImage(const dyld3::launch_cache::Image image, Diagn
         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);
+             diag.error("code signature in (%s) not valid for use in process: %s", image->path(), messageBuffer);
              close(fd);
-             return nullptr;
+             return;
         }
     }
 
@@ -211,20 +448,20 @@ static const mach_header* mapImage(const dyld3::launch_cache::Image image, Diagn
     if ( r != KERN_SUCCESS ) {
         diag.error("vm_allocate(size=0x%0llX) failed with result=%d", totalVMSize, r);
         close(fd);
-        return nullptr;
+        return;
     }
 
     if ( sliceOffset != 0 )
-        log_segments("dyld: Mapping %s (slice offset=%llu)\n", image.path(), sliceOffset);
+        _logSegments("dyld: Mapping %s (slice offset=%llu)\n", image->path(), sliceOffset);
     else
-        log_segments("dyld: Mapping %s\n", image.path());
+        _logSegments("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) {
+    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;
@@ -232,13 +469,13 @@ static const mach_header* mapImage(const dyld3::launch_cache::Image image, Diagn
         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());
+                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());
+                    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());
+                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;
@@ -250,30 +487,32 @@ static const mach_header* mapImage(const dyld3::launch_cache::Image image, Diagn
         // sanity check first segment is mach-o header
         if ( (segAddress != MAP_FAILED) && !mappedFirstSegment ) {
             mappedFirstSegment = true;
-            if ( !MachOParser::isMachO(diag, segAddress, fileSize) ) {
+            const MachOFile* mf = (MachOFile*)segAddress;
+            if ( !mf->isMachO(diag, 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),
+            const MachOLoaded* lmo = (MachOLoaded*)loadAddress;
+            _logSegments("%14s (%c%c%c) 0x%012lX->0x%012lX \n", lmo->segmentName(segIndex),
                          (permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
-                         (long)segAddress, (long)segAddress+vmSize-1);
+                         (long)segAddress, (long)segAddress+(long)vmSize-1);
         }
     });
     if ( mmapFailure ) {
-        vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
-        close(fd);
-        return nullptr;
+        ::vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
+        ::close(fd);
+        return;
     }
 
     // close file
     close(fd);
 
- #if BUILDING_LIBDYLD
+#if BUILDING_LIBDYLD
     // verify file has not changed since closure was built by checking code signature has not changed
-    if ( image.validateUsingCdHash() ) {
+    uint8_t cdHashExpected[20];
+    if ( image->hasCdHash(cdHashExpected) ) {
         if ( codeSignatureStartAddress == nullptr ) {
             diag.error("code signature missing");
         }
@@ -281,18 +520,19 @@ static const mach_header* mapImage(const dyld3::launch_cache::Image image, Diagn
             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 )
+            uint8_t cdHashFound[20];
+            const MachOLoaded* lmo = (MachOLoaded*)loadAddress;
+            if ( lmo->cdHashOfCodeSignature(codeSignatureStartAddress, codeSignFileSize, cdHashFound) ) {
+                if ( ::memcmp(cdHashFound, cdHashExpected, 20) != 0 )
                     diag.error("code signature changed since closure was built");
             }
-            else{
+            else {
                 diag.error("code signature format invalid");
             }
         }
         if ( diag.hasError() ) {
-            vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
-            return nullptr;
+            ::vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
+            return;
         }
     }
 #endif
@@ -301,377 +541,223 @@ static const mach_header* mapImage(const dyld3::launch_cache::Image image, Diagn
     // tell kernel about fairplay encrypted regions
     uint32_t fpTextOffset;
     uint32_t fpSize;
-    if ( image.isFairPlayEncrypted(fpTextOffset, 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;
+        int result = ::mremap_encrypted(((uint8_t*)mh) + fpTextOffset, fpSize, 1, mh->cputype, mh->cpusubtype);
+        if ( result != 0 ) {
+            diag.error("could not register fairplay decryption, mremap_encrypted() => %d", result);
+            ::vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
+            return;
+        }
     }
 #endif
 
-    log_loads("dyld: load %s\n", image.path());
+    _logLoads("dyld: load %s\n", image->path());
 
-    return (mach_header*)loadAddress;
+    timer.setData4((uint64_t)loadAddress);
+    info.setLoadedAddress((MachOLoaded*)loadAddress);
+    info.setState(LoadedImage::State::mapped);
 }
 
-
-void unmapImage(const launch_cache::binary_format::Image* binImage, const mach_header* loadAddress)
+void Loader::unmapImage(LoadedImage& info)
 {
-    assert(loadAddress != nullptr);
-    launch_cache::Image image(binImage);
-    vm_deallocate(mach_task_self(), (vm_address_t)loadAddress, (vm_size_t)(image.vmSizeToMap()));
+    assert(info.loadedAddress() != nullptr);
+    ::vm_deallocate(mach_task_self(), (vm_address_t)info.loadedAddress(), (vm_size_t)(info.image()->vmSizeToMap()));
+    info.setLoadedAddress(nullptr);
 }
 
-
-static void applyFixupsToImage(Diagnostics& diag, const mach_header* imageMH, const launch_cache::binary_format::Image* imageData,
-                               launch_cache::TargetSymbolValue::LoadedImages& imageResolver, LogFunc log_fixups)
+void Loader::registerDOFs(const Array<DOFInfo>& dofs)
 {
-    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;
+    if ( dofs.empty() )
+        return;
+
+    int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR);
+    if ( fd < 0 ) {
+        _logDofs("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) + dofs.count()*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 = dofs.count();
+        for (unsigned int i=0; i < dofs.count(); ++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);
         }
-    #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;
+
+        // 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 < dofs.count(); ++i) {
+                _logDofs("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));
             }
         }
-    #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;
+        else {
+            _logDofs("dyld: ioctl to register dtrace DOF section failed\n");
         }
-    });
-    return result;
+        close(fd);
+    }
 }
 
-void CurrentLoadImages::forEachImage(void (^handler)(uint32_t anIndex, const launch_cache::binary_format::Image*, const mach_header*, bool& stop))
+bool Loader::dtraceUserProbesEnabled()
 {
-    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;
+#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR
+    int    dof_mode;
+    size_t dof_mode_size = sizeof(dof_mode);
+    if ( sysctlbyname("kern.dtrace.dof_mode", &dof_mode, &dof_mode_size, nullptr, 0) == 0 ) {
+        return ( dof_mode != 0 );
     }
+    return false;
+#else
+    // dtrace is always available for macOS and simulators
+    return true;
+#endif
 }
 
-struct DOFInfo {
-    const void*            dof;
-    const mach_header*     imageHeader;
-    const char*            imageShortName;
-};
 
-static void registerDOFs(const DOFInfo* dofs, uint32_t dofSectionCount, LogFunc log_dofs)
+void Loader::vmAccountingSetSuspended(bool suspend, LogFunc logger)
 {
-    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);
-        }
-    }
+#if __arm__ || __arm64__
+    // <rdar://problem/29099600> dyld should tell the kernel when it is doing fix-ups caused by roots
+    logger("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 mapAndFixupImages(Diagnostics& diag, launch_cache::DynArray<ImageInfo>& images, const uint8_t* cacheLoadAddress,
-                       LogFunc log_loads, LogFunc log_segments, LogFunc log_fixups, LogFunc log_dofs)
+void Loader::applyFixupsToImage(Diagnostics& diag, LoadedImage& info)
 {
-    // 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);
-                    });
+    dyld3::ScopedTimer timer(DBG_DYLD_TIMING_APPLY_FIXUPS, (uint64_t)info.loadedAddress(), 0, 0);
+    closure::ImageNum       cacheImageNum;
+    const char*             leafName         = info.image()->leafName();
+    const closure::Image*   image            = info.image();
+    const uint8_t*          imageLoadAddress = (uint8_t*)info.loadedAddress();
+    uintptr_t               slide            = info.loadedAddress()->getSlide();
+    bool                    overrideOfCache  = info.image()->isOverrideOfDyldCacheImage(cacheImageNum);
+    if ( overrideOfCache )
+        vmAccountingSetSuspended(true, _logFixups);
+    image->forEachFixup(^(uint64_t imageOffsetToRebase, bool &stop) {
+        uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToRebase);
+        *fixUpLoc += slide;
+        _logFixups("dyld: fixup: %s:%p += %p\n", leafName, fixUpLoc, (void*)slide);
+    },
+    ^(uint64_t imageOffsetToBind, closure::Image::ResolvedSymbolTarget bindTarget, bool &stop) {
+        uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToBind);
+        uintptr_t value = resolveTarget(bindTarget);
+        _logFixups("dyld: fixup: %s:%p = %p\n", leafName, fixUpLoc, (void*)value);
+        *fixUpLoc = value;
+    },
+    ^(uint64_t imageOffsetStart, const Array<closure::Image::ResolvedSymbolTarget>& targets, bool& stop) {
+        // walk each fixup in the chain
+        image->forEachChainedFixup((void*)imageLoadAddress, imageOffsetStart, ^(uint64_t* fixupLoc, MachOLoaded::ChainedFixupPointerOnDisk fixupInfo, bool& stopChain) {
+            if ( fixupInfo.authRebase.auth ) {
+    #if  __has_feature(ptrauth_calls)
+                if ( fixupInfo.authBind.bind ) {
+                    closure::Image::ResolvedSymbolTarget bindTarget = targets[fixupInfo.authBind.ordinal];
+                    uint64_t targetAddr = resolveTarget(bindTarget);
+                    // Don't sign missing weak imports.
+                    if (targetAddr != 0)
+                        targetAddr = fixupInfo.signPointer(fixupLoc, targetAddr);
+                    _logFixups("dyld: fixup: *%p = %p (JOP: diversity 0x%04X, addr-div=%d, key=%s)\n",
+                               fixupLoc, (void*)targetAddr, fixupInfo.authBind.diversity, fixupInfo.authBind.addrDiv, fixupInfo.authBind.keyName());
+                    *fixupLoc = targetAddr;
                 }
+                else {
+                    uint64_t targetAddr = (uint64_t)imageLoadAddress + fixupInfo.authRebase.target;
+                    targetAddr = fixupInfo.signPointer(fixupLoc, targetAddr);
+                    _logFixups("dyld: fixup: *%p = %p (JOP: diversity 0x%04X, addr-div=%d, key=%s)\n",
+                               fixupLoc, (void*)targetAddr, fixupInfo.authRebase.diversity, fixupInfo.authRebase.addrDiv, fixupInfo.authRebase.keyName());
+                    *fixupLoc = targetAddr;
+                }
+    #else
+                diag.error("malformed chained pointer");
+                stop = true;
+                stopChain = true;
+    #endif
             }
-            // 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 ( fixupInfo.plainRebase.bind ) {
+                    closure::Image::ResolvedSymbolTarget bindTarget = targets[fixupInfo.plainBind.ordinal];
+                    uint64_t targetAddr = resolveTarget(bindTarget) + fixupInfo.plainBind.signExtendedAddend();
+                    _logFixups("dyld: fixup: %s:%p = %p\n", leafName, fixupLoc, (void*)targetAddr);
+                    *fixupLoc = targetAddr;
                 }
                 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
-                    }
+                    uint64_t targetAddr = fixupInfo.plainRebase.signExtendedTarget() + slide;
+                    _logFixups("dyld: fixup: %s:%p += %p\n", leafName, fixupLoc, (void*)slide);
+                    *fixupLoc = targetAddr;
                 }
             }
-            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;
-    }
+#if __i386__
+    __block bool segmentsMadeWritable = false;
+    image->forEachTextReloc(^(uint32_t imageOffsetToRebase, bool& stop) {
+        if ( !segmentsMadeWritable )
+            setSegmentProtects(info, true);
+        uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToRebase);
+        *fixUpLoc += slide;
+        _logFixups("dyld: fixup: %s:%p += %p\n", leafName, fixUpLoc, (void*)slide);
+     },
+    ^(uint32_t imageOffsetToBind, closure::Image::ResolvedSymbolTarget bindTarget, bool& stop) {
+        // FIXME
+    });
+    if ( segmentsMadeWritable )
+        setSegmentProtects(info, false);
+#endif
 
-    // 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;
-            });
-        }
+    if ( overrideOfCache )
+        vmAccountingSetSuspended(false, _logFixups);
+}
 
-        // 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;
-            });
+#if __i386__
+void Loader::setSegmentProtects(const LoadedImage& info, bool write)
+{
+    info.image()->forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t protections, bool& segStop) {
+        if ( protections & VM_PROT_WRITE )
+            return;
+        uint32_t regionProt = protections;
+        if ( write )
+            regionProt = VM_PROT_WRITE | VM_PROT_READ;
+        kern_return_t r = vm_protect(mach_task_self(), ((uintptr_t)info.loadedAddress())+(uintptr_t)vmOffset, (uintptr_t)vmSize, false, regionProt);
+        assert( r == KERN_SUCCESS );
+    });
+}
+#endif
+
+#if BUILDING_DYLD
+void forEachLineInFile(const char* buffer, size_t bufferLen, void (^lineHandler)(const char* line, bool& stop))
+{
+    bool stop = false;
+    const char* const eof = &buffer[bufferLen];
+    for (const char* s = buffer; s < eof; ++s) {
+        char lineBuffer[MAXPATHLEN];
+        char* t = lineBuffer;
+        char* tEnd = &lineBuffer[MAXPATHLEN];
+        while ( (s < eof) && (t != tEnd) ) {
+            if ( *s == '\n' )
+            break;
+            *t++ = *s++;
         }
-        registerDOFs(dofImages, dofCount, log_dofs);
+        *t = '\0';
+        lineHandler(lineBuffer, stop);
+        if ( stop )
+        break;
     }
 }
 
-#if BUILDING_DYLD
 void forEachLineInFile(const char* path, void (^lineHandler)(const char* line, bool& stop))
 {
     int fd = dyld::my_open(path, O_RDONLY, 0);
@@ -680,22 +766,7 @@ void forEachLineInFile(const char* path, void (^lineHandler)(const char* line, b
         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;
-                }
+                forEachLineInFile(lines, (size_t)statBuf.st_size, lineHandler);
                 munmap((void*)lines, (size_t)statBuf.st_size);
             }
         }
@@ -796,10 +867,6 @@ void __cxa_pure_virtual()
 }
 #endif
 
-
-#endif // DYLD_IN_PROCESS
-
-} // namespace loader
 } // namespace dyld3
 
 
index 177543bfd57ffd217626d7853e9757c07b980a29..e5f22a6274a4c7c1f27069b2a9ec5e0516453150 100644 (file)
 #include <stdint.h>
 #include <mach/mach.h>
 #include <_simple.h>
-#include "LaunchCache.h"
-#include "LaunchCacheFormat.h"
-#include "MachOParser.h"
-#include "ClosureBuffer.h"
-
 
+#include "Closure.h"
+#include "MachOLoaded.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;
+//
+// Tuple of info about a loaded image. Contains the loaded address, Image*, and state.
+//
+class VIS_HIDDEN LoadedImage {
+public:
+    enum class State { unmapped=0, mapped=1, fixedUp=2, beingInited=3, inited=4 };
+
+    static LoadedImage      make(const closure::Image* img)         { LoadedImage result; result._image = img; return result; }
+    static LoadedImage      make(const closure::Image* img, const MachOLoaded* mh)
+                                                                    { LoadedImage result; result._image = img; result.setLoadedAddress(mh); return result; }
+
+    const closure::Image*   image() const                           { return _image; }
+    const MachOLoaded*      loadedAddress() const                   { return (MachOLoaded*)(_loadAddr & (-4096)); }
+    void                    setLoadedAddress(const MachOLoaded* a)  { _loadAddr |= ((uintptr_t)a & (-4096)); }
+    State                   state() const                           { return (State)(asBits().state); }
+    void                    setState(State s)                       { asBits().state = (int)s; }
+    bool                    hideFromFlatSearch() const              { return asBits().hide; }
+    void                    setHideFromFlatSearch(bool h)           { asBits().hide = h; }
+    bool                    leaveMapped() const                     { return asBits().leaveMapped; }
+    void                    markLeaveMapped()                       { asBits().leaveMapped = true; }
+
+private:
+    // since loaded MachO files are always page aligned, that means at least low 12-bits are always zero
+    // so we don't need to record the low 12 bits, instead those bits hold various flags in the _loadeAddr field
+    struct AddrBits {
+        uintptr_t    state       :  3,
+                     hide        :  1,
+                     leaveMapped :  1,
+                     extra       :  7,
+          #if __LP64__
+                     addr        : 52;
+          #else
+                     addr        : 20;
+          #endif
+    };
+    AddrBits&               asBits()       { return *((AddrBits*)&_loadAddr); }
+    const AddrBits&         asBits() const { return *((AddrBits*)&_loadAddr); }
+
+    // Note: this must be statically initializable so as to not cause static initializers
+    const closure::Image*   _image      = nullptr;
+    uintptr_t               _loadAddr   = 0;        // really AddrBits
 };
 
 
-#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;
+//
+// Utility class to recursively load dependents
+//
+class VIS_HIDDEN Loader {
+public:
+        typedef bool (*LogFunc)(const char*, ...) __attribute__((format(printf, 1, 2)));
+
+                        Loader(Array<LoadedImage>& storage, const void* cacheAddress, const Array<const dyld3::closure::ImageArray*>& imagesArrays,
+                               LogFunc log_loads, LogFunc log_segments, LogFunc log_fixups, LogFunc log_dofs);
+
+    void                addImage(const LoadedImage&);
+    void                completeAllDependents(Diagnostics& diag, uintptr_t topIndex=0);
+    void                mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool fromOFI=false, uintptr_t topIndex=0);
+    uintptr_t           resolveTarget(closure::Image::ResolvedSymbolTarget target);
+    LoadedImage*        findImage(closure::ImageNum targetImageNum);
+
+    static void         unmapImage(LoadedImage& info);
+    static bool         dtraceUserProbesEnabled();
+    static void         vmAccountingSetSuspended(bool suspend, LogFunc);
+
+private:
+
+    struct ImageOverride
+    {
+        closure::ImageNum  inCache;
+        closure::ImageNum  replacement;
+    };
+
+    struct DOFInfo {
+        const void*            dof;
+        const mach_header*     imageHeader;
+        const char*            imageShortName;
+    };
+
+    void                mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI);
+    void                applyFixupsToImage(Diagnostics& diag, LoadedImage& info);
+    void                registerDOFs(const Array<DOFInfo>& dofs);
+    void                setSegmentProtects(const LoadedImage& info, bool write);
+       bool                sandboxBlockedMmap(const char* path);
+    bool                sandboxBlockedOpen(const char* path);
+    bool                sandboxBlockedStat(const char* path);
+    bool                sandboxBlocked(const char* path, const char* kind);
+
+    Array<LoadedImage>&                         _allImages;
+    const Array<const closure::ImageArray*>&    _imagesArrays;
+    const void*                                 _dyldCacheAddress;
+    LogFunc                                     _logLoads;
+    LogFunc                                     _logSegments;
+    LogFunc                                     _logFixups;
+    LogFunc                                     _logDofs;
+};
 
 
-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));
+void forEachLineInFile(const char* buffer, size_t bufferLen, void (^lineHandler)(const char* line, bool& stop));
 #endif
-#endif
 
-} // namespace loader
+
 } // namespace dyld3
 
 
diff --git a/dyld3/MachOAnalyzer.cpp b/dyld3/MachOAnalyzer.cpp
new file mode 100644 (file)
index 0000000..8b3b72b
--- /dev/null
@@ -0,0 +1,2541 @@
+/*
+ * 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 <sys/types.h>
+#include <mach/mach.h>
+#include <assert.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <mach-o/reloc.h>
+#include <mach-o/nlist.h>
+#include <TargetConditionals.h>
+
+#include "MachOAnalyzer.h"
+#include "CodeSigningTypes.h"
+#include "Array.h"
+
+#include <stdio.h>
+
+
+#ifndef BIND_OPCODE_THREADED
+    #define BIND_OPCODE_THREADED    0xD0
+#endif
+
+#ifndef BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB
+    #define BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB    0x00
+#endif
+
+#ifndef BIND_SUBOPCODE_THREADED_APPLY
+    #define BIND_SUBOPCODE_THREADED_APPLY                               0x01
+#endif
+
+
+namespace dyld3 {
+
+
+const MachOAnalyzer* MachOAnalyzer::validMainExecutable(Diagnostics& diag, const mach_header* mh, const char* path, uint64_t sliceLength, const char* reqArchName, Platform reqPlatform)
+{
+    const MachOAnalyzer* result = (const MachOAnalyzer*)mh;
+    if ( !result->validMachOForArchAndPlatform(diag, (size_t)sliceLength, path, reqArchName, reqPlatform) )
+        return nullptr;
+    if ( !result->isDynamicExecutable() )
+        return nullptr;
+
+    return result;
+}
+
+
+closure::LoadedFileInfo MachOAnalyzer::load(Diagnostics& diag, const closure::FileSystem& fileSystem, const char* path, const char* reqArchName, Platform reqPlatform)
+{
+    closure::LoadedFileInfo info;
+    char realerPath[MAXPATHLEN];
+    if (!fileSystem.loadFile(path, info, realerPath, ^(const char *format, ...) {
+        va_list list;
+        va_start(list, format);
+        diag.error(format, list);
+        va_end(list);
+    })) {
+        return closure::LoadedFileInfo();
+    }
+
+    // if fat, remap just slice needed
+    bool fatButMissingSlice;
+    const FatFile*       fh = (FatFile*)info.fileContent;
+    uint64_t sliceOffset = info.sliceOffset;
+    uint64_t sliceLen = info.sliceLen;
+    if ( fh->isFatFileWithSlice(diag, info.fileContentLen, reqArchName, sliceOffset, sliceLen, fatButMissingSlice) ) {
+        if ( (sliceOffset & 0xFFF) != 0 ) {
+            // slice not page aligned
+            if ( strncmp((char*)info.fileContent + sliceOffset, "!<arch>", 7) == 0 )
+                diag.error("file is static library");
+            else
+                diag.error("slice is not page aligned");
+            fileSystem.unloadFile(info);
+            return closure::LoadedFileInfo();
+        }
+        else {
+            // unmap anything before slice
+            fileSystem.unloadPartialFile(info, sliceOffset, sliceLen);
+            // Update the info to keep track of the new slice offset.
+            info.sliceOffset = sliceOffset;
+            info.sliceLen = sliceLen;
+        }
+    }
+    else if ( fatButMissingSlice ) {
+        diag.error("missing required arch %s in %s", reqArchName, path);
+        fileSystem.unloadFile(info);
+        return closure::LoadedFileInfo();
+    }
+
+    const MachOAnalyzer* mh = (MachOAnalyzer*)info.fileContent;
+
+    // validate is mach-o of requested arch and platform
+    if ( !mh->validMachOForArchAndPlatform(diag, (size_t)info.sliceLen, path, reqArchName, reqPlatform) ) {
+        fileSystem.unloadFile(info);
+        return closure::LoadedFileInfo();
+    }
+
+    // if has zero-fill expansion, re-map
+    mh = mh->remapIfZeroFill(diag, fileSystem, info);
+
+    // on error, remove mappings and return nullptr
+    if ( diag.hasError() ) {
+        fileSystem.unloadFile(info);
+        return closure::LoadedFileInfo();
+    }
+
+    // now that LINKEDIT is at expected offset, finish validation
+    mh->validLinkedit(diag, path);
+
+    // on error, remove mappings and return nullptr
+    if ( diag.hasError() ) {
+        fileSystem.unloadFile(info);
+        return closure::LoadedFileInfo();
+    }
+
+    return info;
+}
+
+#if DEBUG
+// only used in debug builds of cache builder to verify segment moves are valid
+void MachOAnalyzer::validateDyldCacheDylib(Diagnostics& diag, const char* path) const
+{
+    validLinkedit(diag, path);
+    validSegments(diag, path, 0xffffffff);
+}
+#endif
+
+uint64_t MachOAnalyzer::mappedSize() const
+{
+    const uint32_t pageSize = uses16KPages() ? 0x4000 : 0x1000;
+    __block uint64_t textSegVmAddr   = 0;
+    __block uint64_t vmSpaceRequired = 0;
+    forEachSegment(^(const SegmentInfo& info, bool& stop) {
+        if ( strcmp(info.segName, "__TEXT") == 0 ) {
+            textSegVmAddr = info.vmAddr;
+        }
+        else if ( strcmp(info.segName, "__LINKEDIT") == 0 ) {
+            vmSpaceRequired = info.vmAddr + ((info.vmSize + (pageSize-1)) & (-pageSize)) - textSegVmAddr;
+            stop = true;
+        }
+    });
+
+    return vmSpaceRequired;
+}
+
+bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics& diag, size_t sliceLength, const char* path, const char* reqArchName, Platform reqPlatform) const
+{
+    // must start with mach-o magic value
+    if ( (this->magic != MH_MAGIC) && (this->magic != MH_MAGIC_64) ) {
+        diag.error("could not use '%s' because it is not a mach-o file, 0x%08X", path, this->magic);
+        return false;
+    }
+
+    // must match requested architecture, if specified
+    if ( reqArchName != nullptr ) {
+        if ( !this->isArch(reqArchName)) {
+            // except when looking for x86_64h, fallback to x86_64
+            if ( (strcmp(reqArchName, "x86_64h") != 0) || !this->isArch("x86_64") ) {
+#if SUPPORT_ARCH_arm64e
+                // except when looking for arm64e, fallback to arm64
+                if ( (strcmp(reqArchName, "arm64e") != 0) || !this->isArch("arm64") ) {
+#endif
+                    diag.error("could not use '%s' because it does not contain required architecture %s", path, reqArchName);
+                    return false;
+#if SUPPORT_ARCH_arm64e
+                }
+#endif
+            }
+        }
+    }
+
+    // must be a filetype dyld can load
+    switch ( this->filetype ) {
+        case MH_EXECUTE:
+        case MH_DYLIB:
+        case MH_BUNDLE:
+            break;
+        default:
+            diag.error("could not use '%s' because it is not a dylib, bundle, or executable", path);
+           return false;
+    }
+
+    // validate load commands structure
+    if ( !this->validLoadCommands(diag, path, sliceLength) ) {
+        return false;
+    }
+
+    // filter out static executables
+    if ( (this->filetype == MH_EXECUTE) && !isDynamicExecutable() ) {
+        diag.error("could not use '%s' because it is a static executable", path);
+        return false;
+    }
+
+    // must match requested platform (do this after load commands are validated)
+    if ( !this->supportsPlatform(reqPlatform) ) {
+        diag.error("could not use '%s' because it was built for a different platform", path);
+        return false;
+    }
+
+    // validate dylib loads
+    if ( !validEmbeddedPaths(diag, path) )
+        return false;
+
+    // validate segments
+    if ( !validSegments(diag, path, sliceLength) )
+        return false;
+
+    // validate entry
+    if ( this->filetype == MH_EXECUTE ) {
+        if ( !validMain(diag, path) )
+            return false;
+    }
+
+    // further validations done in validLinkedit()
+
+    return true;
+}
+
+bool MachOAnalyzer::validLinkedit(Diagnostics& diag, const char* path) const
+{
+    // validate LINKEDIT layout
+    if ( !validLinkeditLayout(diag, path) )
+        return false;
+
+    if ( hasChainedFixups() ) {
+        if ( !validChainedFixupsInfo(diag, path) )
+            return false;
+    }
+    else {
+        // validate rebasing info
+        if ( !validRebaseInfo(diag, path) )
+            return false;
+
+       // validate binding info
+        if ( !validBindInfo(diag, path) )
+            return false;
+    }
+
+    return true;
+}
+
+bool MachOAnalyzer::validLoadCommands(Diagnostics& diag, const char* path, size_t fileLen) const
+{
+    // check load command don't exceed file length
+    if ( this->sizeofcmds + sizeof(mach_header_64) > fileLen ) {
+        diag.error("in '%s' load commands exceed length of file", path);
+        return false;
+    }
+
+    // walk all load commands and sanity check them
+    Diagnostics walkDiag;
+    forEachLoadCommand(walkDiag, ^(const load_command* cmd, bool& stop) {});
+    if ( walkDiag.hasError() ) {
+#if BUILDING_CACHE_BUILDER
+        diag.error("in '%s' %s", path, walkDiag.errorMessage().c_str());
+#else
+        diag.error("in '%s' %s", path, walkDiag.errorMessage());
+#endif
+        return false;
+    }
+
+    // check load commands fit in TEXT segment
+    __block bool foundTEXT    = false;
+    forEachSegment(^(const SegmentInfo& info, bool& stop) {
+        if ( strcmp(info.segName, "__TEXT") == 0 ) {
+            foundTEXT = true;
+            if ( this->sizeofcmds + sizeof(mach_header_64) > info.fileSize ) {
+                diag.error("in '%s' load commands exceed length of __TEXT segment", path);
+            }
+            if ( info.fileOffset != 0 ) {
+                diag.error("in '%s' __TEXT segment not start of mach-o", path);
+            }
+            stop = true;
+        }
+    });
+    if ( !diag.noError() && !foundTEXT ) {
+        diag.error("in '%s' __TEXT segment not found", path);
+        return false;
+    }
+
+    return true;
+}
+
+const MachOAnalyzer* MachOAnalyzer::remapIfZeroFill(Diagnostics& diag, const closure::FileSystem& fileSystem, closure::LoadedFileInfo& info) const
+{
+    uint64_t vmSpaceRequired;
+    auto hasZeroFill = [this, &vmSpaceRequired]() {
+        __block bool     hasZeroFill     = false;
+        __block uint64_t textSegVmAddr   = 0;
+        forEachSegment(^(const SegmentInfo& segmentInfo, bool& stop) {
+            if ( strcmp(segmentInfo.segName, "__TEXT") == 0 ) {
+                textSegVmAddr = segmentInfo.vmAddr;
+            }
+            else if ( strcmp(segmentInfo.segName, "__LINKEDIT") == 0 ) {
+                uint64_t vmOffset = segmentInfo.vmAddr - textSegVmAddr;
+                // A zero fill page in the __DATA segment means the file offset of __LINKEDIT is less than its vm offset
+                if ( segmentInfo.fileOffset != vmOffset )
+                    hasZeroFill = true;
+                vmSpaceRequired = segmentInfo.vmAddr + segmentInfo.vmSize - textSegVmAddr;
+                stop = true;
+            }
+        });
+        return hasZeroFill;
+    };
+
+    if (hasZeroFill()) {
+        vm_address_t newMappedAddr;
+        if ( ::vm_allocate(mach_task_self(), &newMappedAddr, (size_t)vmSpaceRequired, VM_FLAGS_ANYWHERE) != 0 ) {
+            diag.error("vm_allocate failure");
+            return nullptr;
+        }
+        // mmap() each segment read-only with standard layout
+        __block uint64_t textSegVmAddr;
+        forEachSegment(^(const SegmentInfo& segmentInfo, bool& stop) {
+            if ( strcmp(segmentInfo.segName, "__TEXT") == 0 )
+                textSegVmAddr = segmentInfo.vmAddr;
+            if ( segmentInfo.fileSize != 0 ) {
+                kern_return_t r = vm_copy(mach_task_self(), (vm_address_t)((long)info.fileContent+segmentInfo.fileOffset), (vm_size_t)segmentInfo.fileSize, (vm_address_t)(newMappedAddr+segmentInfo.vmAddr-textSegVmAddr));
+                if ( r != KERN_SUCCESS ) {
+                    diag.error("vm_copy() failure");
+                    stop = true;
+                }
+            }
+        });
+        if ( diag.noError() ) {
+            // remove original mapping and return new mapping
+            fileSystem.unloadFile(info);
+
+            // Set vm_deallocate as the unload method.
+            info.unload = [](const closure::LoadedFileInfo& info) {
+                ::vm_deallocate(mach_task_self(), (vm_address_t)info.fileContent, (size_t)info.fileContentLen);
+            };
+
+            // And update the file content to the new location
+            info.fileContent = (const void*)newMappedAddr;
+            info.fileContentLen = vmSpaceRequired;
+            return (const MachOAnalyzer*)info.fileContent;
+        }
+        else {
+            // new mapping failed, return old mapping with an error in diag
+            ::vm_deallocate(mach_task_self(), newMappedAddr, (size_t)vmSpaceRequired);
+            return nullptr;
+        }
+    }
+
+    return this;
+}
+
+bool MachOAnalyzer::enforceFormat(Malformed kind) const
+{
+#if TARGET_OS_OSX
+    __block bool result = false;
+    forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+        if ( platform == Platform::macOS ) {
+            switch (kind) {
+            case Malformed::linkeditOrder:
+            case Malformed::linkeditAlignment:
+            case Malformed::dyldInfoAndlocalRelocs:
+                // enforce these checks on new binaries only
+                result = (sdk >= 0x000A0E00); // macOS 10.14
+            }
+        }
+    });
+    // if binary is so old, there is no platform info, don't enforce malformed errors
+    return result;
+#else
+    return true;
+#endif
+}
+
+bool MachOAnalyzer::validEmbeddedPaths(Diagnostics& diag, const char* path) const
+{
+    __block int  index = 1;
+    __block bool allGood = true;
+    __block bool foundInstallName = false;
+    __block int  dependentsCount = 0;
+    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.error("in '%s' load command #%d name offset (%u) outside its size (%u)", path, 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.error("in '%s' load command #%d string extends beyond end of load command", path, index);
+                        stop = true;
+                        allGood = false;
+                    }
+                }
+                if ( cmd->cmd  != LC_ID_DYLIB )
+                    ++dependentsCount;
+                break;
+            case LC_RPATH:
+                rpathCmd = (rpath_command*)cmd;
+                if ( rpathCmd->path.offset > cmd->cmdsize ) {
+                    diag.error("in '%s' load command #%d path offset (%u) outside its size (%u)", path, 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.error("in '%s' load command #%d string extends beyond end of load command", path, index);
+                        stop = true;
+                        allGood = false;
+                    }
+                }
+                break;
+        }
+        ++index;
+    });
+    if ( !allGood )
+        return false;
+
+    if ( this->filetype == MH_DYLIB ) {
+        if ( !foundInstallName ) {
+            diag.error("in '%s' MH_DYLIB is missing LC_ID_DYLIB", path);
+            return false;
+        }
+    }
+    else {
+        if ( foundInstallName ) {
+            diag.error("in '%s' LC_ID_DYLIB found in non-MH_DYLIB", path);
+            return false;
+        }
+    }
+
+    if ( (dependentsCount == 0) && (this->filetype == MH_EXECUTE)  ) {
+        diag.error("in '%s' missing LC_LOAD_DYLIB (must link with at least libSystem.dylib)", path);
+        return false;
+    }
+
+    return true;
+}
+
+bool MachOAnalyzer::validSegments(Diagnostics& diag, const char* path, size_t fileLen) const
+{
+    // 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.error("in '%s' load command size too small for LC_SEGMENT_64", path);
+               badSegmentLoadCommand = true;
+               stop = true;
+            }
+            else if ( (sectionsSpace % sizeof(section_64)) != 0 ) {
+               diag.error("in '%s' segment load command size 0x%X will not fit whole number of sections", path, cmd->cmdsize);
+               badSegmentLoadCommand = true;
+               stop = true;
+            }
+            else if ( sectionsSpace != (seg->nsects * sizeof(section_64)) ) {
+               diag.error("in '%s' load command size 0x%X does not match nsects %d", path, cmd->cmdsize, seg->nsects);
+               badSegmentLoadCommand = true;
+               stop = true;
+            }
+            else if ( greaterThanAddOrOverflow(seg->fileoff, seg->filesize, fileLen) ) {
+                diag.error("in '%s' segment load command content extends beyond end of file", path);
+                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.error("in '%s' segment filesize exceeds vmsize", path);
+                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.error("in '%s' load command size too small for LC_SEGMENT", path);
+               badSegmentLoadCommand = true;
+               stop = true;
+            }
+            else if ( (sectionsSpace % sizeof(section)) != 0 ) {
+               diag.error("in '%s' segment load command size 0x%X will not fit whole number of sections", path, cmd->cmdsize);
+               badSegmentLoadCommand = true;
+               stop = true;
+            }
+            else if ( sectionsSpace != (seg->nsects * sizeof(section)) ) {
+               diag.error("in '%s' load command size 0x%X does not match nsects %d", path, 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.error("in '%s' segment filesize exceeds vmsize", path);
+                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 SegmentInfo& info, bool& stop) {
+        if ( strcmp(info.segName, "__TEXT") == 0 ) {
+            if ( info.protections != (VM_PROT_READ|VM_PROT_EXECUTE) ) {
+                diag.error("in '%s' __TEXT segment permissions is not 'r-x'", path);
+                badPermissions = true;
+                stop = true;
+            }
+            hasTEXT = true;
+        }
+        else if ( strcmp(info.segName, "__LINKEDIT") == 0 ) {
+            if ( info.protections != VM_PROT_READ ) {
+                diag.error("in '%s' __LINKEDIT segment permissions is not 'r--'", path);
+                badPermissions = true;
+                stop = true;
+            }
+            hasLINKEDIT = true;
+        }
+        else if ( (info.protections & 0xFFFFFFF8) != 0 ) {
+            diag.error("in '%s' %s segment permissions has invalid bits set", path, info.segName);
+            badPermissions = true;
+            stop = true;
+        }
+        if ( greaterThanAddOrOverflow(info.fileOffset, info.fileSize, fileLen) ) {
+            diag.error("in '%s' %s segment content extends beyond end of file", path, info.segName);
+            badSize = true;
+            stop = true;
+        }
+        if ( is64() ) {
+            if ( info.vmAddr+info.vmSize < info.vmAddr ) {
+                diag.error("in '%s' %s segment vm range wraps", path, info.segName);
+                badSize = true;
+                stop = true;
+            }
+       }
+       else {
+            if ( (uint32_t)(info.vmAddr+info.vmSize) < (uint32_t)(info.vmAddr) ) {
+                diag.error("in '%s' %s segment vm range wraps", path, info.segName);
+                badSize = true;
+                stop = true;
+            }
+       }
+    });
+    if ( badPermissions || badSize )
+        return false;
+    if ( !hasTEXT ) {
+       diag.error("in '%s' missing __TEXT segment", path);
+       return false;
+    }
+    if ( !hasLINKEDIT ) {
+       diag.error("in '%s' missing __LINKEDIT segment", path);
+       return false;
+    }
+
+    // check for overlapping segments
+    __block bool badSegments = false;
+    forEachSegment(^(const SegmentInfo& info1, bool& stop1) {
+        uint64_t seg1vmEnd   = info1.vmAddr + info1.vmSize;
+        uint64_t seg1FileEnd = info1.fileOffset + info1.fileSize;
+        forEachSegment(^(const SegmentInfo& info2, bool& stop2) {
+            if ( info1.segIndex == info2.segIndex )
+                return;
+            uint64_t seg2vmEnd   = info2.vmAddr + info2.vmSize;
+            uint64_t seg2FileEnd = info2.fileOffset + info2.fileSize;
+            if ( ((info2.vmAddr <= info1.vmAddr) && (seg2vmEnd > info1.vmAddr) && (seg1vmEnd > info1.vmAddr )) || ((info2.vmAddr >= info1.vmAddr ) && (info2.vmAddr < seg1vmEnd) && (seg2vmEnd > info2.vmAddr)) ) {
+                diag.error("in '%s' segment %s vm range overlaps segment %s", path, info1.segName, info2.segName);
+                badSegments = true;
+                stop1 = true;
+                stop2 = true;
+            }
+             if ( ((info2.fileOffset  <= info1.fileOffset) && (seg2FileEnd > info1.fileOffset) && (seg1FileEnd > info1.fileOffset)) || ((info2.fileOffset  >= info1.fileOffset) && (info2.fileOffset  < seg1FileEnd) && (seg2FileEnd > info2.fileOffset )) ) {
+                diag.error("in '%s' segment %s file content overlaps segment %s", path, info1.segName, info2.segName);
+                badSegments = true;
+                stop1 = true;
+                stop2 = true;
+            }
+            if ( (info1.segIndex < info2.segIndex) && !stop1 ) {
+                if ( (info1.vmAddr > info2.vmAddr) || ((info1.fileOffset > info2.fileOffset ) && (info1.fileOffset != 0) && (info2.fileOffset  != 0)) ){
+                    if ( !inDyldCache() ) {
+                        // dyld cache __DATA_* segments are moved around
+                        diag.error("in '%s' segment load commands out of order with respect to layout for %s and %s", path, info1.segName, info2.segName);
+                        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.error("in '%s' section %s size too large 0x%llX", path, sect->sectname, sect->size);
+                    badSections = true;
+                }
+                else if ( sect->addr < seg->vmaddr ) {
+                    diag.error("in '%s' section %s start address 0x%llX is before containing segment's address 0x%0llX", path, sect->sectname, sect->addr, seg->vmaddr);
+                    badSections = true;
+                }
+                else if ( sect->addr+sect->size > seg->vmaddr+seg->vmsize ) {
+                    diag.error("in '%s' section %s end address 0x%llX is beyond containing segment's end address 0x%0llX", path, 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.error("in '%s' section %s size too large 0x%X", path, sect->sectname, sect->size);
+                    badSections = true;
+                }
+                else if ( sect->addr < seg->vmaddr ) {
+                    diag.error("in '%s' section %s start address 0x%X is before containing segment's address 0x%0X", path,  sect->sectname, sect->addr, seg->vmaddr);
+                    badSections = true;
+                }
+                else if ( sect->addr+sect->size > seg->vmaddr+seg->vmsize ) {
+                    diag.error("in '%s' section %s end address 0x%X is beyond containing segment's end address 0x%0X", path, sect->sectname, sect->addr+sect->size, seg->vmaddr+seg->vmsize);
+                    badSections = true;
+                }
+            }
+        }
+    });
+
+    return !badSections;
+}
+
+
+bool MachOAnalyzer::validMain(Diagnostics& diag, const char* path) const
+{
+    __block uint64_t textSegStartAddr = 0;
+    __block uint64_t textSegStartSize = 0;
+    forEachSegment(^(const SegmentInfo& info, bool& stop) {
+        if ( strcmp(info.segName, "__TEXT") == 0 ) {
+            textSegStartAddr = info.vmAddr;
+            textSegStartSize = info.vmSize;
+            stop = true;
+       }
+    });
+
+    __block int mainCount   = 0;
+    __block int threadCount = 0;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        entry_point_command* mainCmd;
+        uint64_t startAddress;
+        switch (cmd->cmd) {
+            case LC_MAIN:
+                ++mainCount;
+                mainCmd = (entry_point_command*)cmd;
+                if ( mainCmd->entryoff > textSegStartSize ) {
+                    diag.error("LC_MAIN points outside of __TEXT segment");
+                    stop = true;
+                }
+                break;
+            case LC_UNIXTHREAD:
+                ++threadCount;
+                startAddress = entryAddrFromThreadCmd((thread_command*)cmd);
+                if ( startAddress == 0 ) {
+                    diag.error("LC_UNIXTHREAD not valid for arch %s", archName());
+                    stop = true;
+                }
+                else if ( (startAddress < textSegStartAddr) || (startAddress > textSegStartAddr+textSegStartSize) ) {
+                    diag.error("LC_UNIXTHREAD entry not in __TEXT segment");
+                    stop = true;
+                }
+                break;
+        }
+    });
+    if ( diag.hasError() )
+        return false;
+    if ( diag.noError() && (mainCount+threadCount == 1) )
+        return true;
+
+    if ( mainCount + threadCount == 0 )
+        diag.error("missing LC_MAIN or LC_UNIXTHREAD");
+    else
+        diag.error("only one LC_MAIN or LC_UNIXTHREAD is allowed");
+    return false;
+}
+
+
+namespace {
+    struct LinkEditContentChunk
+    {
+        const char* name;
+        uint32_t    stdOrder;
+        uint32_t    fileOffsetStart;
+        uint32_t    size;
+
+        static int compareByFileOffset(const void* l, const void* r) {
+            if ( ((LinkEditContentChunk*)l)->fileOffsetStart < ((LinkEditContentChunk*)r)->fileOffsetStart )
+                return -1;
+            else
+                return 1;
+        }
+        static int compareByStandardOrder(const void* l, const void* r) {
+           if ( ((LinkEditContentChunk*)l)->stdOrder < ((LinkEditContentChunk*)r)->stdOrder )
+                return -1;
+            else
+                return 1;
+        }
+    };
+} // anonymous namespace
+
+
+
+bool MachOAnalyzer::validLinkeditLayout(Diagnostics& diag, const char* path) const
+{
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() )
+        return false;
+    const uint32_t ptrSize = pointerSize();
+
+    // build vector of all blobs in LINKEDIT
+    LinkEditContentChunk blobs[32];
+    LinkEditContentChunk* bp = blobs;
+    if ( leInfo.dyldInfo != nullptr ) {
+        if ( leInfo.dyldInfo->rebase_size != 0 )
+            *bp++ = {"rebase opcodes",         1, leInfo.dyldInfo->rebase_off, leInfo.dyldInfo->rebase_size};
+        if ( leInfo.dyldInfo->bind_size != 0 )
+            *bp++ = {"bind opcodes",           2, leInfo.dyldInfo->bind_off, leInfo.dyldInfo->bind_size};
+        if ( leInfo.dyldInfo->weak_bind_size != 0 )
+            *bp++ = {"weak bind opcodes",      3, leInfo.dyldInfo->weak_bind_off, leInfo.dyldInfo->weak_bind_size};
+        if ( leInfo.dyldInfo->lazy_bind_size != 0 )
+            *bp++ = {"lazy bind opcodes",      4, leInfo.dyldInfo->lazy_bind_off, leInfo.dyldInfo->lazy_bind_size};
+        if ( leInfo.dyldInfo->export_size!= 0 )
+            *bp++ = {"exports trie",           5, leInfo.dyldInfo->export_off, leInfo.dyldInfo->export_size};
+    }
+    if ( leInfo.dynSymTab != nullptr ) {
+        if ( leInfo.dynSymTab->nlocrel != 0 )
+            *bp++ = {"local relocations",      6, leInfo.dynSymTab->locreloff, static_cast<uint32_t>(leInfo.dynSymTab->nlocrel*sizeof(relocation_info))};
+        if ( leInfo.dynSymTab->nextrel != 0 )
+            *bp++ = {"external relocations",  11, leInfo.dynSymTab->extreloff, static_cast<uint32_t>(leInfo.dynSymTab->nextrel*sizeof(relocation_info))};
+        if ( leInfo.dynSymTab->nindirectsyms != 0 )
+            *bp++ = {"indirect symbol table", 12, leInfo.dynSymTab->indirectsymoff, leInfo.dynSymTab->nindirectsyms*4};
+    }
+    if ( leInfo.splitSegInfo != nullptr ) {
+        if ( leInfo.splitSegInfo->datasize != 0 )
+            *bp++ = {"shared cache info",      6, leInfo.splitSegInfo->dataoff, leInfo.splitSegInfo->datasize};
+    }
+    if ( leInfo.functionStarts != nullptr ) {
+        if ( leInfo.functionStarts->datasize != 0 )
+            *bp++ = {"function starts",        7, leInfo.functionStarts->dataoff, leInfo.functionStarts->datasize};
+    }
+    if ( leInfo.dataInCode != nullptr ) {
+        if ( leInfo.dataInCode->datasize != 0 )
+            *bp++ = {"data in code",           8, leInfo.dataInCode->dataoff, leInfo.dataInCode->datasize};
+    }
+    if ( leInfo.symTab != nullptr ) {
+        if ( leInfo.symTab->nsyms != 0 )
+            *bp++ = {"symbol table",         10, leInfo.symTab->symoff, static_cast<uint32_t>(leInfo.symTab->nsyms*(ptrSize == 8 ? sizeof(nlist_64) : sizeof(struct nlist)))};
+        if ( leInfo.symTab->strsize != 0 )
+            *bp++ = {"symbol table strings", 20, leInfo.symTab->stroff, leInfo.symTab->strsize};
+    }
+    if ( leInfo.codeSig != nullptr ) {
+        if ( leInfo.codeSig->datasize != 0 )
+            *bp++ = {"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) && enforceFormat(Malformed::dyldInfoAndlocalRelocs) ) {
+            diag.error("in '%s' malformed mach-o contains LC_DYLD_INFO_ONLY and local relocations", path);
+            return false;
+        }
+        if ( leInfo.dynSymTab->nextrel != 0 ) {
+            diag.error("in '%s' malformed mach-o contains LC_DYLD_INFO_ONLY and external relocations", path);
+            return false;
+        }
+    }
+    if ( (leInfo.dyldInfo == nullptr) && (leInfo.dynSymTab == nullptr) ) {
+        diag.error("in '%s' malformed mach-o misssing LC_DYLD_INFO and LC_DYSYMTAB", path);
+        return false;
+    }
+    const unsigned long blobCount = bp - blobs;
+    if ( blobCount == 0 ) {
+        diag.error("in '%s' malformed mach-o misssing LINKEDIT", path);
+        return false;
+    }
+
+    uint32_t linkeditFileEnd = leInfo.layout.linkeditFileOffset + leInfo.layout.linkeditFileSize;
+
+
+    // sort blobs by file-offset and error on overlaps
+    ::qsort(blobs, blobCount, sizeof(LinkEditContentChunk), &LinkEditContentChunk::compareByFileOffset);
+    uint32_t     prevEnd = leInfo.layout.linkeditFileOffset;
+    const char*  prevName = "start of LINKEDIT";
+    for (unsigned long i=0; i < blobCount; ++i) {
+        const LinkEditContentChunk& blob = blobs[i];
+        if ( blob.fileOffsetStart < prevEnd ) {
+            diag.error("in '%s' LINKEDIT overlap of %s and %s", path, prevName, blob.name);
+            return false;
+        }
+        if (greaterThanAddOrOverflow(blob.fileOffsetStart, blob.size, linkeditFileEnd)) {
+            diag.error("in '%s' LINKEDIT content '%s' extends beyond end of segment", path, blob.name);
+            return false;
+        }
+        prevEnd  = blob.fileOffsetStart + blob.size;
+        prevName = blob.name;
+    }
+
+    // sort vector by order and warn on non standard order or mis-alignment
+    ::qsort(blobs, blobCount, sizeof(LinkEditContentChunk), &LinkEditContentChunk::compareByStandardOrder);
+    prevEnd = leInfo.layout.linkeditFileOffset;
+    for (unsigned long i=0; i < blobCount; ++i) {
+        const LinkEditContentChunk& blob = blobs[i];
+        if ( ((blob.fileOffsetStart & (ptrSize-1)) != 0) && (blob.stdOrder != 20) && enforceFormat(Malformed::linkeditAlignment) )  // ok for "symbol table strings" to be mis-aligned
+            diag.error("in '%s' mis-aligned LINKEDIT content '%s'", path, blob.name);
+        if ( (blob.fileOffsetStart < prevEnd) && enforceFormat(Malformed::linkeditOrder) ) {
+            diag.error("in '%s' LINKEDIT out of order %s", path, blob.name);
+        }
+        prevEnd  = blob.fileOffsetStart;
+    }
+
+    // Check for invalid symbol table sizes
+    if ( leInfo.symTab != nullptr ) {
+        if ( leInfo.symTab->nsyms > 0x10000000 ) {
+            diag.error("in '%s' malformed mach-o image: symbol table too large", path);
+            return false;
+        }
+        if ( leInfo.dynSymTab != nullptr ) {
+            // validate indirect symbol table
+            if ( leInfo.dynSymTab->nindirectsyms != 0 ) {
+                if ( leInfo.dynSymTab->nindirectsyms > 0x10000000 ) {
+                    diag.error("in '%s' malformed mach-o image: indirect symbol table too large", path);
+                    return false;
+                }
+            }
+            if ( (leInfo.dynSymTab->nlocalsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->ilocalsym > leInfo.symTab->nsyms) ) {
+                diag.error("in '%s' malformed mach-o image: indirect symbol table local symbol count exceeds total symbols", path);
+                return false;
+            }
+            if ( leInfo.dynSymTab->ilocalsym + leInfo.dynSymTab->nlocalsym < leInfo.dynSymTab->ilocalsym  ) {
+                diag.error("in '%s' malformed mach-o image: indirect symbol table local symbol count wraps", path);
+                return false;
+            }
+            if ( (leInfo.dynSymTab->nextdefsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->iextdefsym > leInfo.symTab->nsyms) ) {
+                diag.error("in '%s' malformed mach-o image: indirect symbol table extern symbol count exceeds total symbols", path);
+                return false;
+            }
+            if ( leInfo.dynSymTab->iextdefsym + leInfo.dynSymTab->nextdefsym < leInfo.dynSymTab->iextdefsym  ) {
+                diag.error("in '%s' malformed mach-o image: indirect symbol table extern symbol count wraps", path);
+                return false;
+            }
+            if ( (leInfo.dynSymTab->nundefsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->iundefsym > leInfo.symTab->nsyms) ) {
+                diag.error("in '%s' malformed mach-o image: indirect symbol table undefined symbol count exceeds total symbols", path);
+                return false;
+            }
+            if ( leInfo.dynSymTab->iundefsym + leInfo.dynSymTab->nundefsym < leInfo.dynSymTab->iundefsym  ) {
+                diag.error("in '%s' malformed mach-o image: indirect symbol table undefined symbol count wraps", path);
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+
+
+bool MachOAnalyzer::invalidRebaseState(Diagnostics& diag, const char* opcodeName, const char* path, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+                                      bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type) const
+{
+    if ( !segIndexSet ) {
+        diag.error("in '%s' %s missing preceding REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", path, opcodeName);
+        return true;
+    }
+    if ( segmentIndex >= leInfo.layout.linkeditSegIndex )  {
+        diag.error("in '%s' %s segment index %d too large", path, opcodeName, segmentIndex);
+        return true;
+    }
+    if ( segmentOffset > (segments[segmentIndex].vmSize-ptrSize) ) {
+        diag.error("in '%s' %s current segment offset 0x%08llX beyond segment size (0x%08llX)", path, opcodeName, segmentOffset, segments[segmentIndex].vmSize);
+        return true;
+    }
+    switch ( type )  {
+        case REBASE_TYPE_POINTER:
+            if ( !segments[segmentIndex].writable() ) {
+                diag.error("in '%s' %s pointer rebase is in non-writable segment", path, opcodeName);
+                return true;
+            }
+            if ( segments[segmentIndex].executable() ) {
+                diag.error("in '%s' %s pointer rebase is in executable segment", path, opcodeName);
+                return true;
+            }
+            break;
+        case REBASE_TYPE_TEXT_ABSOLUTE32:
+        case REBASE_TYPE_TEXT_PCREL32:
+            if ( !segments[segmentIndex].textRelocs ) {
+                diag.error("in '%s' %s text rebase is in segment that does not support text relocations", path, opcodeName);
+                return true;
+            }
+            if ( segments[segmentIndex].writable() ) {
+                diag.error("in '%s' %s text rebase is in writable segment", path, opcodeName);
+                return true;
+            }
+            if ( !segments[segmentIndex].executable() ) {
+                diag.error("in '%s' %s pointer rebase is in non-executable segment", path, opcodeName);
+                return true;
+            }
+            break;
+        default:
+            diag.error("in '%s' %s unknown rebase type %d", path, opcodeName, type);
+            return true;
+    }
+    return false;
+}
+
+
+void MachOAnalyzer::getAllSegmentsInfos(Diagnostics& diag, SegmentInfo segments[]) const
+{
+    forEachSegment(^(const SegmentInfo& info, bool& stop) {
+        segments[info.segIndex] = info;
+    });
+}
+
+
+bool MachOAnalyzer::validRebaseInfo(Diagnostics& diag, const char* path) const
+{
+    forEachRebase(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+                          bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, bool& stop) {
+        if ( invalidRebaseState(diag, opcodeName, path, leInfo, segments, segIndexSet, ptrSize, segmentIndex, segmentOffset, type) )
+            stop = true;
+    });
+    return diag.noError();
+}
+
+
+void MachOAnalyzer::forEachTextRebase(Diagnostics& diag, void (^handler)(uint64_t runtimeOffset, bool& stop)) const
+{
+    __block bool     startVmAddrSet = false;
+    __block uint64_t startVmAddr    = 0;
+    forEachRebase(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+                          bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, bool& stop) {
+        if ( type != REBASE_TYPE_TEXT_ABSOLUTE32 )
+            return;
+        if ( !startVmAddrSet ) {
+            for (int i=0; i <= segmentIndex; ++i) {
+                if ( strcmp(segments[i].segName, "__TEXT") == 0 ) {
+                    startVmAddr = segments[i].vmAddr;
+                    startVmAddrSet = true;
+                    break;
+               }
+            }
+        }
+        uint64_t rebaseVmAddr  = segments[segmentIndex].vmAddr + segmentOffset;
+        uint64_t runtimeOffset = rebaseVmAddr - startVmAddr;
+        handler(runtimeOffset, stop);
+    });
+}
+
+
+void MachOAnalyzer::forEachRebase(Diagnostics& diag, bool ignoreLazyPointers, void (^handler)(uint64_t runtimeOffset, bool& stop)) const
+{
+    __block bool     startVmAddrSet = false;
+    __block uint64_t startVmAddr    = 0;
+    __block uint64_t lpVmAddr       = 0;
+    __block uint64_t lpEndVmAddr    = 0;
+    __block uint64_t shVmAddr       = 0;
+    __block uint64_t shEndVmAddr    = 0;
+    if ( ignoreLazyPointers ) {
+        forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool &stop) {
+            if ( (info.sectFlags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) {
+                lpVmAddr    = info.sectAddr;
+                lpEndVmAddr = info.sectAddr + info.sectSize;
+            }
+            else if ( (info.sectFlags & S_ATTR_PURE_INSTRUCTIONS) && (strcmp(info.sectName, "__stub_helper") == 0) ) {
+                shVmAddr    = info.sectAddr;
+                shEndVmAddr = info.sectAddr + info.sectSize;
+            }
+        });
+    }
+    forEachRebase(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+                          bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, bool& stop) {
+        if ( type != REBASE_TYPE_POINTER )
+            return;
+        if ( !startVmAddrSet ) {
+            for (int i=0; i < segmentIndex; ++i) {
+                if ( strcmp(segments[i].segName, "__TEXT") == 0 ) {
+                    startVmAddr = segments[i].vmAddr;
+                    startVmAddrSet = true;
+                    break;
+               }
+            }
+        }
+        uint64_t rebaseVmAddr  = segments[segmentIndex].vmAddr + segmentOffset;
+        bool skipRebase = false;
+        if ( (rebaseVmAddr >= lpVmAddr) && (rebaseVmAddr < lpEndVmAddr) ) {
+            // rebase is in lazy pointer section
+            uint64_t lpValue = 0;
+            if ( ptrSize == 8 )
+                lpValue = *((uint64_t*)(rebaseVmAddr-startVmAddr+(uint8_t*)this));
+            else
+                lpValue = *((uint32_t*)(rebaseVmAddr-startVmAddr+(uint8_t*)this));
+            if ( (lpValue >= shVmAddr) && (lpValue < shEndVmAddr) ) {
+                // content is into stub_helper section
+                uint64_t lpTargetImageOffset = lpValue - startVmAddr;
+                const uint8_t* helperContent = (uint8_t*)this + lpTargetImageOffset;
+                bool isLazyStub = contentIsRegularStub(helperContent);
+                // ignore rebases for normal lazy pointers, but leave rebase for resolver helper stub
+                if ( isLazyStub )
+                    skipRebase = true;
+            }
+            else {
+                // if lazy pointer does not point into stub_helper, then it points to weak-def symbol and we need rebase
+            }
+        }
+        if ( !skipRebase ) {
+            uint64_t runtimeOffset = rebaseVmAddr - startVmAddr;
+            handler(runtimeOffset, stop);
+        }
+    });
+}
+
+
+bool MachOAnalyzer::contentIsRegularStub(const uint8_t* helperContent) const
+{
+    switch (this->cputype) {
+        case CPU_TYPE_X86_64:
+            return ( (helperContent[0] == 0x68) && (helperContent[5] == 0xE9) ); // push $xxx / JMP pcRel
+        case CPU_TYPE_I386:
+            return ( (helperContent[0] == 0x68) && (helperContent[5] == 0xFF) && (helperContent[2] == 0x26) ); // push $xxx / JMP *pcRel
+        case CPU_TYPE_ARM:
+            return ( (helperContent[0] == 0x00) && (helperContent[1] == 0xC0) && (helperContent[2] == 0x9F) && (helperContent[3] == 0xE5) ); // ldr  ip, [pc, #0]
+        case CPU_TYPE_ARM64:
+            return ( (helperContent[0] == 0x50) && (helperContent[1] == 0x00) && (helperContent[2] == 0x00) && (helperContent[3] == 0x18) ); // ldr w16, L0
+
+    }
+    return false;
+}
+
+static int uint32Sorter(const void* l, const void* r) {
+    if ( *((uint32_t*)l) < *((uint32_t*)r) )
+        return -1;
+    else
+        return 1;
+}
+
+
+void MachOAnalyzer::forEachRebase(Diagnostics& diag,
+                                 void (^handler)(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+                                                 bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset,
+                                                 uint8_t type, bool& stop)) const
+{
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() )
+        return;
+
+    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.linkeditSegIndex+1);
+    getAllSegmentsInfos(diag, segmentsInfo);
+    if ( diag.hasError() )
+        return;
+
+    if ( leInfo.dyldInfo != nullptr ) {
+        const uint8_t* p    = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->rebase_off);
+        const uint8_t* end  = p + leInfo.dyldInfo->rebase_size;
+        const uint32_t ptrSize = pointerSize();
+        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*ptrSize;
+                    break;
+                case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
+                    for (int i=0; i < immediate; ++i) {
+                        handler("REBASE_OPCODE_DO_REBASE_IMM_TIMES", leInfo, segmentsInfo, segIndexSet, ptrSize, segIndex, segOffset, type, stop);
+                        segOffset += ptrSize;
+                        if ( stop )
+                            break;
+                    }
+                    break;
+                case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
+                    count = read_uleb128(diag, p, end);
+                    for (uint32_t i=0; i < count; ++i) {
+                        handler("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo, segmentsInfo, segIndexSet, ptrSize, segIndex, segOffset, type, stop);
+                        segOffset += ptrSize;
+                        if ( stop )
+                            break;
+                    }
+                    break;
+                case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
+                    handler("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo, segmentsInfo, segIndexSet, ptrSize, segIndex, segOffset, type, stop);
+                    segOffset += read_uleb128(diag, p, end) + ptrSize;
+                    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) {
+                        handler("REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB", leInfo, segmentsInfo, segIndexSet, ptrSize, segIndex, segOffset, type, stop);
+                        segOffset += skip + ptrSize;
+                        if ( stop )
+                            break;
+                    }
+                    break;
+                default:
+                    diag.error("unknown rebase opcode 0x%02X", opcode);
+            }
+        }
+    }
+    else {
+        // old binary, walk relocations
+        const uint64_t                  relocsStartAddress = relocBaseAddress(segmentsInfo, leInfo.layout.linkeditSegIndex);
+        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);
+        const uint8_t                   ptrSize   = pointerSize();
+        STACK_ALLOC_OVERFLOW_SAFE_ARRAY(uint32_t, relocAddrs, 2048);
+        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;
+            }
+            relocAddrs.push_back(reloc->r_address);
+        }
+        if ( !relocAddrs.empty() ) {
+            ::qsort(&relocAddrs[0], relocAddrs.count(), sizeof(uint32_t), &uint32Sorter);
+            for (uint32_t addrOff : relocAddrs) {
+                uint32_t segIndex  = 0;
+                uint64_t segOffset = 0;
+                if ( segIndexAndOffsetForAddress(relocsStartAddress+addrOff, segmentsInfo, leInfo.layout.linkeditSegIndex, segIndex, segOffset) ) {
+                    uint8_t type = REBASE_TYPE_POINTER;
+                    if ( this->cputype == CPU_TYPE_I386 ) {
+                        if ( segmentsInfo[segIndex].executable() )
+                            type = REBASE_TYPE_TEXT_ABSOLUTE32;
+                    }
+                    handler("local relocation", leInfo, segmentsInfo, true, ptrSize, segIndex, segOffset, type , stop);
+                }
+                else {
+                    diag.error("local relocation has out of range r_address");
+                    break;
+                }
+            }
+        }
+        // then process indirect symbols
+        forEachIndirectPointer(diag, ^(uint64_t address, bool bind, int bindLibOrdinal,
+                                       const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& indStop) {
+            if ( bind )
+               return;
+            uint32_t segIndex  = 0;
+            uint64_t segOffset = 0;
+            if ( segIndexAndOffsetForAddress(address, segmentsInfo, leInfo.layout.linkeditSegIndex, segIndex, segOffset) ) {
+                handler("local relocation", leInfo, segmentsInfo, true, ptrSize, segIndex, segOffset, REBASE_TYPE_POINTER, indStop);
+            }
+            else {
+                diag.error("local relocation has out of range r_address");
+                indStop = true;
+            }
+        });
+    }
+}
+
+bool MachOAnalyzer::segIndexAndOffsetForAddress(uint64_t addr, const SegmentInfo segmentsInfos[], uint32_t segCount, uint32_t& segIndex, uint64_t& segOffset) const
+{
+    for (uint32_t i=0; i < segCount; ++i) {
+        if ( (segmentsInfos[i].vmAddr <= addr) && (addr < segmentsInfos[i].vmAddr+segmentsInfos[i].vmSize) ) {
+            segIndex  = i;
+            segOffset = addr - segmentsInfos[i].vmAddr;
+            return true;
+        }
+    }
+    return false;
+}
+
+uint64_t MachOAnalyzer::relocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const
+{
+    if ( is64() ) {
+        // x86_64 reloc base address is first writable segment
+        for (uint32_t i=0; i < segCount; ++i) {
+            if ( segmentsInfos[i].writable() )
+                return segmentsInfos[i].vmAddr;
+        }
+    }
+    return segmentsInfos[0].vmAddr;
+}
+
+
+
+void MachOAnalyzer::forEachIndirectPointer(Diagnostics& diag, void (^handler)(uint64_t pointerAddress, 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          ptrSize                  = pointerSize();
+    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 dyld3::MachOAnalyzer::SectionInfo& sectInfo, bool malformedSectionRange, bool& sectionStop) {
+        uint8_t  sectionType  = (sectInfo.sectFlags & SECTION_TYPE);
+        bool selfModifyingStub = (sectionType == S_SYMBOL_STUBS) && (sectInfo.sectFlags & S_ATTR_SELF_MODIFYING_CODE) && (sectInfo.reserved2 == 5) && (this->cputype == CPU_TYPE_I386);
+        if ( (sectionType != S_LAZY_SYMBOL_POINTERS) && (sectionType != S_NON_LAZY_SYMBOL_POINTERS) && !selfModifyingStub )
+            return;
+        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 ? sectInfo.reserved2 : ptrSize;
+        uint32_t elementCount = (uint32_t)(sectInfo.sectSize/elementSize);
+        if ( greaterThanAddOrOverflow(sectInfo.reserved1, elementCount, indirectSymbolTableCount) ) {
+            diag.error("section %s overflows indirect symbol table", sectInfo.sectName);
+            sectionStop = true;
+            return;
+        }
+
+        for (uint32_t i=0; (i < elementCount) && !stop; ++i) {
+            uint32_t symNum = indirectSymbolTable[sectInfo.reserved1 + i];
+            if ( symNum == INDIRECT_SYMBOL_ABS )
+                continue;
+            if ( symNum == INDIRECT_SYMBOL_LOCAL ) {
+                handler(sectInfo.sectAddr+i*elementSize, false, 0, "", false, false, false, stop);
+                continue;
+            }
+            if ( symNum > symCount ) {
+                diag.error("indirect symbol[%d] = %d which is invalid symbol index", sectInfo.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", sectInfo.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(sectInfo.sectAddr+i*elementSize, true, libOrdinal, symbolName, weakImport, lazy, selfModifyingStub, stop);
+        }
+        sectionStop = stop;
+    });
+}
+
+int MachOAnalyzer::libOrdinalFromDesc(uint16_t n_desc) const
+{
+    // -flat_namespace is always flat lookup
+    if ( (this->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 MachOAnalyzer::validBindInfo(Diagnostics& diag, const char* path) const
+{
+    forEachBind(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+                         bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
+                         uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset,
+                         uint8_t type, const char* symbolName, bool weakImport, uint64_t addend, bool& stop) {
+        if ( invalidBindState(diag, opcodeName, path, leInfo, segments, segIndexSet, libraryOrdinalSet, dylibCount,
+                              libOrdinal, ptrSize, segmentIndex, segmentOffset, type, symbolName) ) {
+            stop = true;
+        }
+    }, ^(const char* symbolName) {
+    });
+    return diag.noError();
+}
+
+bool MachOAnalyzer::invalidBindState(Diagnostics& diag, const char* opcodeName, const char* path, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+                                    bool segIndexSet,  bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal, uint32_t ptrSize,
+                                    uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, const char* symbolName) const
+{
+    if ( !segIndexSet ) {
+        diag.error("in '%s' %s missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", path, opcodeName);
+        return true;
+    }
+    if ( segmentIndex >= leInfo.layout.linkeditSegIndex )  {
+        diag.error("in '%s' %s segment index %d too large", path, opcodeName, segmentIndex);
+        return true;
+    }
+    if ( segmentOffset > (segments[segmentIndex].vmSize-ptrSize) ) {
+        diag.error("in '%s' %s current segment offset 0x%08llX beyond segment size (0x%08llX)", path, opcodeName, segmentOffset, segments[segmentIndex].vmSize);
+        return true;
+    }
+    if ( symbolName == NULL ) {
+        diag.error("in '%s' %s missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM", path, opcodeName);
+        return true;
+    }
+    if ( !libraryOrdinalSet ) {
+        diag.error("in '%s' %s missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL", path, opcodeName);
+        return true;
+    }
+    if ( libOrdinal > (int)dylibCount ) {
+        diag.error("in '%s' %s has library ordinal too large (%d) max (%d)", path, opcodeName, libOrdinal, dylibCount);
+        return true;
+    }
+    if ( libOrdinal < BIND_SPECIAL_DYLIB_WEAK_DEF_COALESCE ) {
+        diag.error("in '%s' %s has unknown library special ordinal (%d)", path, opcodeName, libOrdinal);
+        return true;
+    }
+    switch ( type )  {
+        case BIND_TYPE_POINTER:
+            if ( !segments[segmentIndex].writable() ) {
+                diag.error("in '%s' %s pointer bind is in non-writable segment", path, opcodeName);
+                return true;
+            }
+            if ( segments[segmentIndex].executable() ) {
+                diag.error("in '%s' %s pointer bind is in executable segment", path, opcodeName);
+                return true;
+            }
+            break;
+        case BIND_TYPE_TEXT_ABSOLUTE32:
+        case BIND_TYPE_TEXT_PCREL32:
+            if ( !segments[segmentIndex].textRelocs ) {
+                diag.error("in '%s' %s text bind is in segment that does not support text relocations", path, opcodeName);
+                return true;
+            }
+            if ( segments[segmentIndex].writable() ) {
+                diag.error("in '%s' %s text bind is in writable segment", path, opcodeName);
+                return true;
+            }
+            if ( !segments[segmentIndex].executable() ) {
+                diag.error("in '%s' %s pointer bind is in non-executable segment", path, opcodeName);
+                return true;
+            }
+            break;
+        default:
+            diag.error("in '%s' %s unknown bind type %d", path, opcodeName, type);
+            return true;
+    }
+    return false;
+}
+
+void MachOAnalyzer::forEachBind(Diagnostics& diag, void (^handler)(uint64_t runtimeOffset, int libOrdinal, const char* symbolName,
+                                                                  bool weakImport, uint64_t addend, bool& stop),
+                                                  void (^strongHandler)(const char* symbolName)) const
+{
+    __block bool     startVmAddrSet = false;
+    __block uint64_t startVmAddr    = 0;
+    forEachBind(diag, ^(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+                        bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
+                        uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset,
+                        uint8_t type, const char* symbolName, bool weakImport, uint64_t addend, bool& stop) {
+       if ( !startVmAddrSet ) {
+            for (int i=0; i <= segmentIndex; ++i) {
+                if ( strcmp(segments[i].segName, "__TEXT") == 0 ) {
+                    startVmAddr = segments[i].vmAddr;
+                    startVmAddrSet = true;
+                    break;
+               }
+            }
+        }
+        uint64_t bindVmOffset  = segments[segmentIndex].vmAddr + segmentOffset;
+        uint64_t runtimeOffset = bindVmOffset - startVmAddr;
+        handler(runtimeOffset, libOrdinal, symbolName, weakImport, addend, stop);
+    }, ^(const char* symbolName) {
+        strongHandler(symbolName);
+    });
+}
+
+void MachOAnalyzer::forEachBind(Diagnostics& diag,
+                                 void (^handler)(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+                                                 bool segIndexSet,  bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
+                                                 uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset,
+                                                 uint8_t type, const char* symbolName, bool weakImport, uint64_t addend, bool& stop),
+                                 void (^strongHandler)(const char* symbolName)) const
+{
+    const uint32_t  ptrSize = this->pointerSize();
+    bool            stop    = false;
+
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() )
+        return;
+
+    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.linkeditSegIndex+1);
+    getAllSegmentsInfos(diag, segmentsInfo);
+    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;
+        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;
+        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:
+                    stop = 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:
+                    handler("BIND_OPCODE_DO_BIND", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
+                            ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, addend, stop);
+                    segmentOffset += ptrSize;
+                    break;
+                case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+                    handler("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
+                            ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, addend, stop);
+                    segmentOffset += read_uleb128(diag, p, end) + ptrSize;
+                    break;
+                case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+                    handler("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
+                            ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, addend, stop);
+                    segmentOffset += immediate*ptrSize + ptrSize;
+                    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) {
+                        handler("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
+                                ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, addend, stop);
+                        segmentOffset += skip + ptrSize;
+                        if ( stop )
+                            break;
+                    }
+                    break;
+                default:
+                    diag.error("bad bind opcode 0x%02X", *p);
+            }
+        }
+        if ( diag.hasError() )
+            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:
+                        handler("BIND_OPCODE_DO_BIND", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
+                                ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, addend, stop);
+                        segmentOffset += ptrSize;
+                        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;
+                }
+            }
+        }
+        if ( diag.hasError() )
+            return;
+
+        // process weak bind info
+        if ( leInfo.dyldInfo->weak_bind_size != 0 ) {
+            p               = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->weak_bind_off);
+            end             = p + leInfo.dyldInfo->weak_bind_size;
+            type            = BIND_TYPE_POINTER;
+            segmentOffset   = 0;
+            segmentIndex    = 0;
+            symbolName      = NULL;
+            libraryOrdinal  = BIND_SPECIAL_DYLIB_WEAK_DEF_COALESCE;
+            segIndexSet     = false;
+            libraryOrdinalSet= true;
+            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:
+                        stop = 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_bind");
+                        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;
+                        if ( immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION ) {
+                            strongHandler(symbolName);
+                        }
+                        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:
+                        handler("BIND_OPCODE_DO_BIND", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
+                                ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, addend, stop);
+                        segmentOffset += ptrSize;
+                        break;
+                    case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+                        handler("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
+                                ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, addend, stop);
+                        segmentOffset += read_uleb128(diag, p, end) + ptrSize;
+                        break;
+                    case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+                        handler("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
+                                ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, addend, stop);
+                        segmentOffset += immediate*ptrSize + ptrSize;
+                        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) {
+                            handler("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB", leInfo, segmentsInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal,
+                                    ptrSize, segmentIndex, segmentOffset, type, symbolName, weakImport, addend, stop);
+                            segmentOffset += skip + ptrSize;
+                            if ( stop )
+                                break;
+                        }
+                        break;
+                    default:
+                        diag.error("bad bind opcode 0x%02X", *p);
+                }
+            }
+        }
+    }
+    else {
+        // old binary, process external relocations
+        const uint64_t                  relocsStartAddress = relocBaseAddress(segmentsInfo, leInfo.layout.linkeditSegIndex);
+        const relocation_info* const    relocsStart = (relocation_info*)getLinkEditContent(leInfo.layout, leInfo.dynSymTab->extreloff);
+        const relocation_info* const    relocsEnd   = &relocsStart[leInfo.dynSymTab->nextrel];
+        bool                            is64Bit     = is64() ;
+        const uint8_t                   relocSize   = (is64Bit ? 3 : 2);
+        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;
+        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;
+            }
+            uint32_t segIndex  = 0;
+            uint64_t segOffset = 0;
+            if ( segIndexAndOffsetForAddress(relocsStartAddress+reloc->r_address, segmentsInfo, leInfo.layout.linkeditSegIndex, segIndex, segOffset) ) {
+                uint32_t symbolIndex = reloc->r_symbolnum;
+                if ( symbolIndex > symCount ) {
+                    diag.error("external relocation has out of range r_symbolnum");
+                    break;
+                }
+                else {
+                    uint32_t strOffset  = is64Bit ? symbols64[symbolIndex].n_un.n_strx : symbols32[symbolIndex].n_un.n_strx;
+                    uint16_t n_desc     = is64Bit ? symbols64[symbolIndex].n_desc : symbols32[symbolIndex].n_desc;
+                    uint32_t libOrdinal = libOrdinalFromDesc(n_desc);
+                    if ( strOffset >= poolSize ) {
+                        diag.error("external relocation has r_symbolnum=%d which has out of range n_strx", symbolIndex);
+                        break;
+                    }
+                    else {
+                        const char*     symbolName = stringPool + strOffset;
+                        bool            weakImport = (n_desc & N_WEAK_REF);
+                        const uint8_t*  content    = (uint8_t*)this + segmentsInfo[segIndex].vmAddr - leInfo.layout.textUnslidVMAddr + segOffset;
+                        uint64_t        addend     = is64Bit ? *((uint64_t*)content) : *((uint32_t*)content);
+                        handler("external relocation", leInfo, segmentsInfo, true, true, dylibCount, libOrdinal,
+                                ptrSize, segIndex, segOffset, BIND_TYPE_POINTER, symbolName, weakImport, addend, stop);
+                    }
+                }
+            }
+            else {
+                diag.error("local relocation has out of range r_address");
+                break;
+            }
+        }
+        // then process indirect symbols
+        forEachIndirectPointer(diag, ^(uint64_t address, bool bind, int bindLibOrdinal,
+                                       const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& indStop) {
+            if ( !bind )
+               return;
+            uint32_t segIndex  = 0;
+            uint64_t segOffset = 0;
+            if ( segIndexAndOffsetForAddress(address, segmentsInfo, leInfo.layout.linkeditSegIndex, segIndex, segOffset) ) {
+                handler("indirect symbol", leInfo, segmentsInfo, true, true, dylibCount, bindLibOrdinal,
+                         ptrSize, segIndex, segOffset, BIND_TYPE_POINTER, bindSymbolName, bindWeakImport, 0, indStop);
+            }
+            else {
+                diag.error("indirect symbol has out of range address");
+                indStop = true;
+            }
+        });
+    }
+
+}
+
+
+bool MachOAnalyzer::validChainedFixupsInfo(Diagnostics& diag, const char* path) const
+{
+    __block uint32_t maxTargetCount = 0;
+    __block uint32_t currentTargetCount = 0;
+    forEachChainedFixup(diag,
+        ^(uint32_t totalTargets, bool& stop) {
+            maxTargetCount = totalTargets;
+        },
+        ^(const LinkEditInfo& leInfo, const SegmentInfo segments[], bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal, uint8_t type, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
+           if ( symbolName == NULL ) {
+                diag.error("in '%s' missing BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM", path);
+            }
+            else if ( !libraryOrdinalSet ) {
+                diag.error("in '%s' missing BIND_OPCODE_SET_DYLIB_ORDINAL", path);
+            }
+            else if ( libOrdinal > (int)dylibCount ) {
+                diag.error("in '%s' has library ordinal too large (%d) max (%d)", path, libOrdinal, dylibCount);
+            }
+            else if ( libOrdinal < BIND_SPECIAL_DYLIB_WEAK_DEF_COALESCE ) {
+                diag.error("in '%s' has unknown library special ordinal (%d)", path, libOrdinal);
+            }
+            else if ( type != BIND_TYPE_POINTER ) {
+                diag.error("in '%s' unknown bind type %d", path, type);
+            }
+            else if ( currentTargetCount > maxTargetCount ) {
+                diag.error("in '%s' chained target counts exceeds BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB", path);
+            }
+            ++currentTargetCount;
+            if ( diag.hasError() )
+                stop = true;
+        },
+        ^(const LinkEditInfo& leInfo, const SegmentInfo segments[], uint8_t segmentIndex, bool segIndexSet, uint64_t segmentOffset, bool& stop) {
+           if ( !segIndexSet ) {
+                diag.error("in '%s' missing BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", path);
+            }
+            else if ( segmentIndex >= leInfo.layout.linkeditSegIndex )  {
+                diag.error("in '%s' segment index %d too large", path, segmentIndex);
+            }
+            else if ( segmentOffset > (segments[segmentIndex].vmSize-8) ) {
+                diag.error("in '%s' current segment offset 0x%08llX beyond segment size (0x%08llX)", path, segmentOffset, segments[segmentIndex].vmSize);
+            }
+            else if ( !segments[segmentIndex].writable() ) {
+                diag.error("in '%s' pointer bind is in non-writable segment", path);
+            }
+            else if ( segments[segmentIndex].executable() ) {
+                diag.error("in '%s' pointer bind is in executable segment", path);
+            }
+            if ( diag.hasError() )
+                stop = true;
+        }
+    );
+
+    return diag.noError();
+}
+
+
+void MachOAnalyzer::forEachChainedFixup(Diagnostics& diag, void (^targetCount)(uint32_t totalTargets, bool& stop),
+                                                           void (^addTarget)(const LinkEditInfo& leInfo, const SegmentInfo segments[], bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal, uint8_t type, const char* symbolName, uint64_t addend, bool weakImport, bool& stop),
+                                                           void (^addChainStart)(const LinkEditInfo& leInfo, const SegmentInfo segments[], uint8_t segmentIndex, bool segIndexSet, uint64_t segmentOffset, bool& stop)) const
+{
+    bool            stop    = false;
+
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() )
+        return;
+
+    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.linkeditSegIndex+1);
+    getAllSegmentsInfos(diag, segmentsInfo);
+    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;
+        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;
+        uint64_t        targetTableCount;
+        uint64_t        addend = 0;
+        bool            weakImport = 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:
+                    stop = 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_SEGMENT_AND_OFFSET_ULEB:
+                    segmentIndex = immediate;
+                    segmentOffset = read_uleb128(diag, p, end);
+                    segIndexSet = true;
+                    break;
+                case BIND_OPCODE_SET_ADDEND_SLEB:
+                    addend = read_sleb128(diag, p, end);
+                    break;
+                case BIND_OPCODE_DO_BIND:
+                    if ( addTarget )
+                        addTarget(leInfo, segmentsInfo, libraryOrdinalSet, dylibCount, libraryOrdinal, type, symbolName, addend, weakImport, stop);
+                    break;
+                case BIND_OPCODE_THREADED:
+                    switch (immediate) {
+                        case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB:
+                            targetTableCount = read_uleb128(diag, p, end);
+                            if ( targetTableCount > 65535 ) {
+                                diag.error("BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB size too large");
+                                stop = true;
+                            }
+                            else {
+                                if ( targetCount )
+                                    targetCount((uint32_t)targetTableCount, stop);
+                            }
+                            break;
+                        case BIND_SUBOPCODE_THREADED_APPLY:
+                            if ( addChainStart )
+                                addChainStart(leInfo, segmentsInfo, segmentIndex, segIndexSet, segmentOffset, stop);
+                            break;
+                        default:
+                            diag.error("bad BIND_OPCODE_THREADED sub-opcode 0x%02X", immediate);
+                    }
+                    break;
+                default:
+                    diag.error("bad bind opcode 0x%02X", immediate);
+            }
+        }
+        if ( diag.hasError() )
+            return;
+    }
+}
+
+void MachOAnalyzer::forEachChainedFixupStart(Diagnostics& diag, void (^callback)(uint64_t runtimeOffset, bool& stop)) const
+{
+    __block bool     startVmAddrSet = false;
+    __block uint64_t startVmAddr    = 0;
+    forEachChainedFixup(diag, nullptr, nullptr, ^(const LinkEditInfo& leInfo, const SegmentInfo segments[], uint8_t segmentIndex, bool segIndexSet, uint64_t segmentOffset, bool& stop) {
+       if ( !startVmAddrSet ) {
+            for (int i=0; i <= segmentIndex; ++i) {
+                if ( strcmp(segments[i].segName, "__TEXT") == 0 ) {
+                    startVmAddr = segments[i].vmAddr;
+                    startVmAddrSet = true;
+                    break;
+               }
+            }
+        }
+        uint64_t startVmOffset = segments[segmentIndex].vmAddr + segmentOffset;
+        uint64_t runtimeOffset = startVmOffset - startVmAddr;
+        callback((uint32_t)runtimeOffset, stop);
+    });
+}
+
+void MachOAnalyzer::forEachChainedFixupTarget(Diagnostics& diag, void (^callback)(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop)) const
+{
+    forEachChainedFixup(diag, nullptr, ^(const LinkEditInfo& leInfo, const SegmentInfo segments[], bool libraryOrdinalSet, uint32_t dylibCount,
+                                         int libOrdinal, uint8_t type, const char* symbolName, uint64_t addend, bool weakImport, bool& stop){
+        callback(libOrdinal, symbolName, addend, weakImport, stop);
+    }, nullptr);
+}
+
+uint32_t MachOAnalyzer::segmentCount() const
+{
+    __block uint32_t count   = 0;
+    forEachSegment(^(const SegmentInfo& info, bool& stop) {
+        ++count;
+    });
+    return count;
+}
+
+bool MachOAnalyzer::hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const
+{
+    fileOffset = 0;
+    size = 0;
+
+    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
+
+    // early exist if no LC_CODE_SIGNATURE
+    if ( fileOffset == 0 )
+        return false;
+
+    // <rdar://problem/13622786> ignore code signatures in macOS binaries built with pre-10.9 tools
+    __block bool goodSignature = true;
+    if ( (this->cputype == CPU_TYPE_X86_64) || (this->cputype == CPU_TYPE_I386) ) {
+        forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+            if ( (platform == Platform::macOS) && (sdk < 0x000A0900) )
+                goodSignature = false;
+        });
+    }
+
+    return goodSignature;
+}
+
+bool MachOAnalyzer::hasInitializer(Diagnostics& diag, bool contentRebased, const void* dyldCache) const
+{
+    __block bool result = false;
+    forEachInitializer(diag, contentRebased, ^(uint32_t offset) {
+        result = true;
+    }, dyldCache);
+    return result;
+}
+
+void MachOAnalyzer::forEachInitializer(Diagnostics& diag, bool contentRebased, void (^callback)(uint32_t offset), const void* dyldCache) const
+{
+    __block uint64_t prefTextSegAddrStart = 0;
+    __block uint64_t prefTextSegAddrEnd   = 0;
+
+    forEachSegment(^(const SegmentInfo& info, bool& stop) {
+        if ( strcmp(info.segName, "__TEXT") == 0 ) {
+            prefTextSegAddrStart = info.vmAddr;
+            prefTextSegAddrEnd   = info.vmAddr + info.vmSize;
+            stop = true;
+        }
+    });
+    if ( prefTextSegAddrStart == prefTextSegAddrEnd ) {
+        diag.error("no __TEXT segment");
+        return;
+    }
+    uint64_t slide = (long)this - prefTextSegAddrStart;
+
+    // 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 ( (prefTextSegAddrStart < dashInit) && (dashInit < prefTextSegAddrEnd) )
+                callback((uint32_t)(dashInit - prefTextSegAddrStart));
+            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 ( (prefTextSegAddrStart < dashInit) && (dashInit < prefTextSegAddrEnd) )
+                callback((uint32_t)(dashInit - prefTextSegAddrStart));
+            else
+                diag.error("-init does not point within __TEXT segment");
+        }
+    });
+
+    // next any function pointers in mod-init section
+    unsigned ptrSize = pointerSize();
+    forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
+        if ( (info.sectFlags & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS ) {
+            const uint8_t* content;
+            content = (uint8_t*)(info.sectAddr + slide);
+            if ( (info.sectSize % ptrSize) != 0 ) {
+                diag.error("initializer section %s/%s has bad size", info.segInfo.segName, info.sectName);
+                stop = true;
+                return;
+            }
+            if ( malformedSectionRange ) {
+                diag.error("initializer section %s/%s extends beyond its segment", info.segInfo.segName, info.sectName);
+                stop = true;
+                return;
+            }
+            if ( ((long)content % ptrSize) != 0 ) {
+                diag.error("initializer section %s/%s is not pointer aligned", info.segInfo.segName, info.sectName);
+                stop = true;
+                return;
+            }
+            if ( ptrSize == 8 ) {
+                const uint64_t* initsStart = (uint64_t*)content;
+                const uint64_t* initsEnd   = (uint64_t*)((uint8_t*)content + info.sectSize);
+                for (const uint64_t* p=initsStart; p < initsEnd; ++p) {
+                    uint64_t anInit = *p;
+                    if ( contentRebased )
+                        anInit -= slide;
+                    if ( hasChainedFixups() ) {
+                        ChainedFixupPointerOnDisk* aChainedInit = (ChainedFixupPointerOnDisk*)p;
+                        if ( aChainedInit->authBind.bind )
+                            diag.error("initializer uses bind");
+                        if ( aChainedInit->authRebase.auth ) {
+                            anInit = aChainedInit->authRebase.target;
+                        }
+                        else {
+                            anInit = aChainedInit->plainRebase.signExtendedTarget();
+                        }
+                    }
+                    if ( (anInit <= prefTextSegAddrStart) || (anInit > prefTextSegAddrEnd) ) {
+                         diag.error("initializer 0x%0llX does not point within __TEXT segment", anInit);
+                         stop = true;
+                         break;
+                    }
+                    callback((uint32_t)(anInit - prefTextSegAddrStart));
+                }
+            }
+            else {
+                const uint32_t* initsStart = (uint32_t*)content;
+                const uint32_t* initsEnd   = (uint32_t*)((uint8_t*)content + info.sectSize);
+                for (const uint32_t* p=initsStart; p < initsEnd; ++p) {
+                    uint32_t anInit = *p;
+                    if ( contentRebased )
+                        anInit -= slide;
+                    if ( (anInit <= prefTextSegAddrStart) || (anInit > prefTextSegAddrEnd) ) {
+                         diag.error("initializer 0x%0X does not point within __TEXT segment", anInit);
+                         stop = true;
+                         break;
+                    }
+                    callback(anInit - (uint32_t)prefTextSegAddrStart);
+                }
+            }
+        }
+    });
+}
+
+
+void MachOAnalyzer::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
+}
+
+
+bool MachOAnalyzer::hasObjC() const
+{
+    __block bool result = false;
+    forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
+        if ( (strcmp(info.sectName, "__objc_imageinfo") == 0) && (strncmp(info.segInfo.segName, "__DATA", 6) == 0) ) {
+            result = true;
+            stop = true;
+        }
+        if ( (this->cputype == CPU_TYPE_I386) && (strcmp(info.sectName, "__image_info") == 0) && (strcmp(info.segInfo.segName, "__OBJC") == 0) ) {
+            result = true;
+            stop = true;
+        }
+    });
+    return result;
+}
+
+bool MachOAnalyzer::hasPlusLoadMethod(Diagnostics& diag) const
+{
+    __block bool result = false;
+    if ( (this->cputype == CPU_TYPE_I386) && supportsPlatform(Platform::macOS) ) {
+        // old objc runtime has no special section for +load methods, scan for string
+        int64_t slide = getSlide();
+        forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
+            if ( ( (info.sectFlags & SECTION_TYPE) == S_CSTRING_LITERALS ) ) {
+                if ( malformedSectionRange ) {
+                    diag.error("cstring section %s/%s extends beyond the end of the segment", info.segInfo.segName, info.sectName);
+                    stop = true;
+                    return;
+                }
+                const uint8_t* content = (uint8_t*)(info.sectAddr + slide);
+                const char* s   = (char*)content;
+                const char* end = s + info.sectSize;
+                while ( s < end ) {
+                    if ( strcmp(s, "load") == 0 ) {
+                        result = true;
+                        stop = true;
+                        return;
+                    }
+                    while (*s != '\0' )
+                        ++s;
+                    ++s;
+                }
+            }
+        });
+    }
+    else {
+        // in new objc runtime compiler puts classes/categories with +load method in specical section
+        forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
+            if ( strncmp(info.segInfo.segName, "__DATA", 6) != 0 )
+                return;
+            if ( (strcmp(info.sectName, "__objc_nlclslist") == 0) || (strcmp(info.sectName, "__objc_nlcatlist") == 0)) {
+                result = true;
+                stop = true;
+            }
+        });
+    }
+    return result;
+}
+
+const void* MachOAnalyzer::getRebaseOpcodes(uint32_t& size) const
+{
+    Diagnostics diag;
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() || (leInfo.dyldInfo == nullptr) )
+        return nullptr;
+
+    size = leInfo.dyldInfo->rebase_size;
+    return getLinkEditContent(leInfo.layout, leInfo.dyldInfo->rebase_off);
+}
+
+const void* MachOAnalyzer::getBindOpcodes(uint32_t& size) const
+{
+    Diagnostics diag;
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() || (leInfo.dyldInfo == nullptr) )
+        return nullptr;
+
+    size = leInfo.dyldInfo->bind_size;
+    return getLinkEditContent(leInfo.layout, leInfo.dyldInfo->bind_off);
+}
+
+const void* MachOAnalyzer::getLazyBindOpcodes(uint32_t& size) const
+{
+    Diagnostics diag;
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() || (leInfo.dyldInfo == nullptr) )
+        return nullptr;
+
+    size = leInfo.dyldInfo->lazy_bind_size;
+    return getLinkEditContent(leInfo.layout, leInfo.dyldInfo->lazy_bind_off);
+}
+
+
+uint64_t MachOAnalyzer::segAndOffsetToRuntimeOffset(uint8_t targetSegIndex, uint64_t targetSegOffset) const
+{
+    __block uint64_t textVmAddr = 0;
+    __block uint64_t result     = 0;
+    forEachSegment(^(const SegmentInfo& info, bool& stop) {
+        if ( strcmp(info.segName, "__TEXT") == 0 )
+            textVmAddr = info.vmAddr;
+        if ( info.segIndex == targetSegIndex ) {
+            result = (info.vmAddr - textVmAddr) + targetSegOffset;
+        }
+    });
+    return result;
+}
+
+bool MachOAnalyzer::hasLazyPointers(uint32_t& runtimeOffset, uint32_t& size) const
+{
+    size = 0;
+    forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool &stop) {
+        if ( (info.sectFlags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) {
+            runtimeOffset = (uint32_t)(info.sectAddr - preferredLoadAddress());
+            size          = (uint32_t)info.sectSize;
+            stop = true;
+        }
+    });
+    return (size != 0);
+}
+
+uint64_t MachOAnalyzer::preferredLoadAddress() const
+{
+    __block uint64_t textVmAddr = 0;
+    forEachSegment(^(const SegmentInfo& info, bool& stop) {
+        if ( strcmp(info.segName, "__TEXT") == 0 ) {
+            textVmAddr = info.vmAddr;
+            stop = true;
+        }
+    });
+    return textVmAddr;
+}
+
+
+bool MachOAnalyzer::getEntry(uint32_t& offset, bool& usesCRT) const
+{
+    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;
+            uint64_t startAddress = entryAddrFromThreadCmd((thread_command*)cmd);
+            offset = (uint32_t)(startAddress - preferredLoadAddress());
+        }
+    });
+    return (offset != 0);
+}
+
+uint64_t MachOAnalyzer::entryAddrFromThreadCmd(const thread_command* cmd) const
+{
+    assert(cmd->cmd == LC_UNIXTHREAD);
+    const uint32_t* regs32 = (uint32_t*)(((char*)cmd) + 16);
+    const uint64_t* regs64 = (uint64_t*)(((char*)cmd) + 16);
+    uint64_t startAddress = 0;
+    switch ( this->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;
+    }
+    return startAddress;
+}
+
+
+void MachOAnalyzer::forEachInterposingSection(Diagnostics& diag, void (^handler)(uint64_t vmOffset, uint64_t vmSize, bool& stop)) const
+{
+    const unsigned ptrSize   = pointerSize();
+    const unsigned entrySize = 2 * ptrSize;
+    forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool &stop) {
+        if ( ((info.sectFlags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(info.sectName, "__interpose") == 0) && (strcmp(info.segInfo.segName, "__DATA") == 0)) ) {
+            if ( info.sectSize % entrySize != 0 ) {
+                diag.error("interposing section %s/%s has bad size", info.segInfo.segName, info.sectName);
+                stop = true;
+                return;
+            }
+            if ( malformedSectionRange ) {
+                diag.error("interposing section %s/%s extends beyond the end of the segment", info.segInfo.segName, info.sectName);
+                stop = true;
+                return;
+            }
+            if ( (info.sectAddr % ptrSize) != 0 ) {
+                diag.error("interposing section %s/%s is not pointer aligned", info.segInfo.segName, info.sectName);
+                stop = true;
+                return;
+            }
+            handler(info.sectAddr - preferredLoadAddress(), info.sectSize, stop);
+        }
+    });
+}
+
+void MachOAnalyzer::forEachDOFSection(Diagnostics& diag, void (^callback)(uint32_t offset)) const
+{
+    forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool &stop) {
+        if ( ( (info.sectFlags & SECTION_TYPE) == S_DTRACE_DOF ) && !malformedSectionRange ) {
+            callback((uint32_t)(info.sectAddr - info.segInfo.vmAddr));
+        }
+    });
+}
+
+bool MachOAnalyzer::getCDHash(uint8_t cdHash[20]) const
+{
+    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 MachOAnalyzer::isRestricted() const
+{
+    __block bool result = false;
+    forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool &stop) {
+        if ( (strcmp(info.segInfo.segName, "__RESTRICT") == 0) && (strcmp(info.sectName, "__restrict") == 0) ) {
+            result = true;
+            stop = true;
+        }
+    });
+    return result;
+}
+
+bool MachOAnalyzer::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 MachOAnalyzer::canHavePrecomputedDlopenClosure(const char* path, void (^failureReason)(const char*)) const
+{
+    __block bool retval = true;
+
+    // only dylibs can go in cache
+    if ( (this->filetype != MH_DYLIB) && (this->filetype != MH_BUNDLE) ) {
+        retval = false;
+        failureReason("not MH_DYLIB or MH_BUNDLE");
+    }
+
+    // flat namespace files cannot go in cache
+    if ( (this->flags & MH_TWOLEVEL) == 0 ) {
+        retval = false;
+        failureReason("not built with two level namespaces");
+    }
+
+    // can only depend on other dylibs with absolute paths
+    __block bool allDepPathsAreGood = true;
+    forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+        if ( loadPath[0] != '/' ) {
+            allDepPathsAreGood = false;
+            stop = true;
+        }
+    });
+    if ( !allDepPathsAreGood ) {
+        retval = false;
+        failureReason("depends on dylibs that are not absolute paths");
+    }
+
+    // dylibs with interposing info cannot have dlopen closure pre-computed
+    __block bool hasInterposing = false;
+    forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool &stop) {
+        if ( ((info.sectFlags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(info.sectName, "__interpose") == 0) && (strcmp(info.segInfo.segName, "__DATA") == 0)) )
+            hasInterposing = true;
+    });
+    if ( hasInterposing ) {
+        retval = false;
+        failureReason("has interposing tuples");
+    }
+
+    // images that use dynamic_lookup, bundle_loader, or have weak-defs cannot have dlopen closure pre-computed
+    Diagnostics diag;
+    auto checkBind = ^(int libOrdinal, bool& stop) {
+        switch (libOrdinal) {
+            case BIND_SPECIAL_DYLIB_WEAK_DEF_COALESCE:
+                failureReason("has weak externals");
+                retval = false;
+                stop = true;
+                break;
+            case BIND_SPECIAL_DYLIB_FLAT_LOOKUP:
+                failureReason("has dynamic_lookup binds");
+                retval = false;
+                stop = true;
+                break;
+            case BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE:
+                failureReason("has reference to main executable (bundle loader)");
+                retval = false;
+                stop = true;
+                break;
+        }
+    };
+
+    if (hasChainedFixups()) {
+        forEachChainedFixupTarget(diag, ^(int libOrdinal, const char *symbolName, uint64_t addend, bool weakImport, bool &stop) {
+            checkBind(libOrdinal, stop);
+        });
+    } else {
+        forEachBind(diag, ^(uint64_t runtimeOffset, int libOrdinal, const char* symbolName, bool weakImport, uint64_t addend, bool& stop) {
+            checkBind(libOrdinal, stop);
+        },
+        ^(const char* symbolName) {
+        });
+    }
+
+    // special system dylib overrides cannot have closure pre-computed
+    if ( strncmp(path, "/usr/lib/system/introspection/", 30) == 0 ) {
+        retval = false;
+        failureReason("override of OS dylib");
+    }
+
+    return retval;
+}
+
+bool MachOAnalyzer::canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const
+{
+    if (!MachOFile::canBePlacedInDyldCache(path, failureReason))
+        return false;
+    if ( !(isArch("x86_64") || isArch("x86_64h")) )
+        return true;
+
+    // Kick dylibs out of the x86_64 cache if they are using TBI.
+    __block bool rebasesOk = true;
+    Diagnostics diag;
+    uint64_t startVMAddr = preferredLoadAddress();
+    uint64_t endVMAddr = startVMAddr + mappedSize();
+    forEachRebase(diag, false, ^(uint64_t runtimeOffset, bool &stop) {
+        uint64_t value = *(uint64_t*)((uint8_t*)this + runtimeOffset);
+        if ( (value < startVMAddr) || (value >= endVMAddr) ) {
+            failureReason("rebase value out of range of dylib");
+            rebasesOk = false;
+            stop = true;
+        }
+    });
+    return rebasesOk;
+}
+
+} // dyld3
+
+
diff --git a/dyld3/MachOAnalyzer.h b/dyld3/MachOAnalyzer.h
new file mode 100644 (file)
index 0000000..85255f3
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * 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 MachOAnalyzer_h
+#define MachOAnalyzer_h
+
+
+#define BIND_SPECIAL_DYLIB_WEAK_DEF_COALESCE (-3)
+
+#include "MachOLoaded.h"
+#include "ClosureFileSystem.h"
+
+namespace dyld3 {
+
+// Extra functionality on loaded mach-o files only used during closure building
+struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
+{
+    static closure::LoadedFileInfo load(Diagnostics& diag, const closure::FileSystem& fileSystem, const char* logicalPath, const char* reqArchName, Platform reqPlatform);
+    static const MachOAnalyzer*  validMainExecutable(Diagnostics& diag, const mach_header* mh, const char* path, uint64_t sliceLength, const char* reqArchName, Platform reqPlatform);
+
+    bool                validMachOForArchAndPlatform(Diagnostics& diag, size_t mappedSize, const char* path, const char* reqArchName, Platform reqPlatform) const;
+    uint64_t            mappedSize() const;
+    bool                hasObjC() const;
+    bool                hasPlusLoadMethod(Diagnostics& diag) const;
+    uint64_t            preferredLoadAddress() 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;
+
+    bool                isEncrypted() const;
+    bool                getCDHash(uint8_t cdHash[20]) const;
+    bool                hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const;
+    bool                usesLibraryValidation() const;
+    bool                isRestricted() const;
+    bool                getEntry(uint32_t& offset, bool& usesCRT) const;
+    bool                isSlideable() const;
+    bool                hasInitializer(Diagnostics& diag, bool contentRebased, const void* dyldCache=nullptr) const;
+    void                forEachInitializer(Diagnostics& diag, bool contentRebased, void (^callback)(uint32_t offset), const void* dyldCache=nullptr) 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                forEachRebase(Diagnostics& diag, void (^callback)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, 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)(uint64_t pointerAddress, bool bind, int bindLibOrdinal,
+                                                                                  const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& stop)) const;
+    void                forEachInterposingSection(Diagnostics& diag, void (^handler)(uint64_t vmOffset, uint64_t vmSize, bool& stop)) const;
+    const void*         content(uint64_t vmOffset);
+    void                forEachLocalReloc(void (^handler)(uint64_t runtimeOffset, bool& stop)) const;
+    void                forEachExternalReloc(void (^handler)(uint64_t runtimeOffset, int libOrdinal, const char* symbolName, bool& stop)) const;
+
+    const void*         getRebaseOpcodes(uint32_t& size) const;
+    const void*         getBindOpcodes(uint32_t& size) const;
+    const void*         getLazyBindOpcodes(uint32_t& size) const;
+    uint64_t            segAndOffsetToRuntimeOffset(uint8_t segIndex, uint64_t segOffset) const;
+    bool                hasLazyPointers(uint32_t& runtimeOffset, uint32_t& size) const;
+    void                forEachRebase(Diagnostics& diag, bool ignoreLazyPointer, void (^callback)(uint64_t runtimeOffset, bool& stop)) const;
+    void                forEachTextRebase(Diagnostics& diag, void (^callback)(uint64_t runtimeOffset, bool& stop)) const;
+    void                forEachBind(Diagnostics& diag, void (^callback)(uint64_t runtimeOffset, int libOrdinal, const char* symbolName,
+                                                                        bool weakImport, uint64_t addend, bool& stop),
+                                                       void (^strongHandler)(const char* symbolName)) const;
+    void                forEachChainedFixupTarget(Diagnostics& diag, void (^callback)(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop)) const;
+    void                forEachChainedFixupStart(Diagnostics& diag, void (^callback)(uint64_t runtimeOffset, bool& stop)) const;
+    bool                canHavePrecomputedDlopenClosure(const char* path, void (^failureReason)(const char*)) const;
+    bool                canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const;
+#if DEBUG
+    void                validateDyldCacheDylib(Diagnostics& diag, const char* path) const;
+#endif
+
+    const MachOAnalyzer*    remapIfZeroFill(Diagnostics& diag, const closure::FileSystem& fileSystem, closure::LoadedFileInfo& info) const;
+
+    // protected members of subclass promoted to public here
+    using MachOLoaded::SegmentInfo;
+    using MachOLoaded::SectionInfo;
+    using MachOLoaded::forEachSegment;
+    using MachOLoaded::forEachSection;
+    using MachOLoaded::forEachDependentDylib;
+    using MachOLoaded::getDylibInstallName;
+    using MachOLoaded::FoundSymbol;
+    using MachOLoaded::findExportedSymbol;
+    
+private:
+
+    struct SegmentStuff
+    {
+        uint64_t    fileOffset;
+        uint64_t    fileSize;
+        uint64_t    writable          :  1,
+                    executable        :  1,
+                    textRelocsAllowed :  1,  // segment supports text relocs (i386 only)
+                    segSize           : 61;
+       };
+
+    enum class Malformed { linkeditOrder, linkeditAlignment, dyldInfoAndlocalRelocs };
+    bool                    enforceFormat(Malformed) const;
+
+    const uint8_t*          getContentForVMAddr(const LayoutInfo& info, uint64_t vmAddr) const;
+
+    bool                    validLoadCommands(Diagnostics& diag, const char* path, size_t fileLen) const;
+    bool                    validEmbeddedPaths(Diagnostics& diag, const char* path) const;
+    bool                    validSegments(Diagnostics& diag, const char* path, size_t fileLen) const;
+    bool                    validLinkedit(Diagnostics& diag, const char* path) const;
+    bool                    validLinkeditLayout(Diagnostics& diag, const char* path) const;
+    bool                    validRebaseInfo(Diagnostics& diag, const char* path) const;
+    bool                    validBindInfo(Diagnostics& diag, const char* path) const;
+    bool                    validMain(Diagnostics& diag, const char* path) const;
+    bool                    validChainedFixupsInfo(Diagnostics& diag, const char* path) const;
+
+    bool                    invalidRebaseState(Diagnostics& diag, const char* opcodeName, const char* path, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+                                              bool segIndexSet, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type) const;
+    bool                    invalidBindState(Diagnostics& diag, const char* opcodeName, const char* path, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+                                              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                    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;
+
+    void                    getAllSegmentsInfos(Diagnostics& diag, SegmentInfo segments[]) const;
+    bool                    segmentHasTextRelocs(uint32_t segIndex) const;
+    uint64_t                relocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const;
+    void                    forEachRebase(Diagnostics& diag, void (^handler)(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+                                                                             bool segIndexSet, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, bool& stop)) const;
+    bool                    segIndexAndOffsetForAddress(uint64_t addr, const SegmentInfo segmentsInfos[], uint32_t segCount, uint32_t& segIndex, uint64_t& segOffset) const;
+    void                    forEachBind(Diagnostics& diag, void (^handler)(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
+                                                                           bool segIndexSet,  bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
+                                                                           uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset,
+                                                                           uint8_t type, const char* symbolName, bool weakImport, uint64_t addend, bool& stop),
+                                                           void (^strongHandler)(const char* symbolName)) const;
+    void                    forEachChainedFixup(Diagnostics& diag, void (^targetCount)(uint32_t totalTargets, bool& stop),
+                                                                   void (^addTarget)(const LinkEditInfo& leInfo, const SegmentInfo segments[], bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal, uint8_t type, const char* symbolName, uint64_t addend, bool weakImport, bool& stop),
+                                                                   void (^addChainStart)(const LinkEditInfo& leInfo, const SegmentInfo segments[], uint8_t segmentIndex, bool segIndexSet, uint64_t segmentOffset, bool& stop)) const;
+    bool                    contentIsRegularStub(const uint8_t* helperContent) const;
+    uint64_t                entryAddrFromThreadCmd(const thread_command* cmd) const;
+
+};
+
+} // namespace dyld3
+
+#endif /* MachOAnalyzer_h */
diff --git a/dyld3/MachOFile.cpp b/dyld3/MachOFile.cpp
new file mode 100644 (file)
index 0000000..bf8724b
--- /dev/null
@@ -0,0 +1,984 @@
+/*
+ * 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 <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <TargetConditionals.h>
+#include <mach/host_info.h>
+#include <mach/mach.h>
+#include <mach/mach_host.h>
+
+#include "MachOFile.h"
+#include "SupportedArchs.h"
+
+#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 CPU_SUBTYPE_ARM64_32_V8
+    #define CPU_SUBTYPE_ARM64_32_V8    1
+#endif
+
+#ifndef CPU_TYPE_ARM64_32
+    #ifndef CPU_ARCH_ABI64_32
+        #define CPU_ARCH_ABI64_32            0x02000000
+    #endif
+    #define CPU_TYPE_ARM64_32            (CPU_TYPE_ARM | CPU_ARCH_ABI64_32)
+#endif
+
+namespace dyld3 {
+
+////////////////////////////  FatFile ////////////////////////////////////////
+
+const FatFile* FatFile::isFatFile(const void* fileStart)
+{
+    const FatFile* fileStartAsFat = (FatFile*)fileStart;
+    if ( (fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC)) || (fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC_64)) )
+        return fileStartAsFat;
+    else
+        return nullptr;
+}
+
+void FatFile::forEachSlice(Diagnostics& diag, uint64_t fileLen, void (^callback)(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop)) const
+{
+       if ( this->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
+        if ( OSSwapBigToHostInt32(nfat_arch) > ((4096 - sizeof(fat_header)) / sizeof(fat_arch)) ) {
+            diag.error("fat header too large: %u entries", OSSwapBigToHostInt32(nfat_arch));
+            return;
+        }
+        bool stop = false;
+        const fat_arch* const archs = (fat_arch*)(((char*)this)+sizeof(fat_header));
+        for (uint32_t i=0; i < OSSwapBigToHostInt32(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*)this+offset, len, stop);
+            if ( stop )
+                break;
+        }
+    }
+    else if ( this->magic == OSSwapBigToHostInt32(FAT_MAGIC_64) ) {
+        if ( OSSwapBigToHostInt32(nfat_arch) > ((4096 - sizeof(fat_header)) / sizeof(fat_arch)) ) {
+            diag.error("fat header too large: %u entries", OSSwapBigToHostInt32(nfat_arch));
+            return;
+        }
+        bool stop = false;
+        const fat_arch_64* const archs = (fat_arch_64*)(((char*)this)+sizeof(fat_header));
+        for (uint32_t i=0; i < OSSwapBigToHostInt32(nfat_arch); ++i) {
+            uint32_t cpuType    = OSSwapBigToHostInt32(archs[i].cputype);
+            uint32_t cpuSubType = OSSwapBigToHostInt32(archs[i].cpusubtype);
+            uint64_t offset     = OSSwapBigToHostInt64(archs[i].offset);
+            uint64_t len        = OSSwapBigToHostInt64(archs[i].size);
+            if ( greaterThanAddOrOverflow(offset, len, fileLen) ) {
+                diag.error("slice %d extends beyond end of file", i);
+                return;
+            }
+            callback(cpuType, cpuSubType, (uint8_t*)this+offset, len, stop);
+            if ( stop )
+                break;
+        }
+    }
+    else {
+        diag.error("not a fat file");
+    }
+}
+
+bool FatFile::isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const char* archName, uint64_t& sliceOffset, uint64_t& sliceLen, bool& missingSlice) const
+{
+    missingSlice = false;
+    if ( (this->magic != OSSwapBigToHostInt32(FAT_MAGIC)) && (this->magic != OSSwapBigToHostInt32(FAT_MAGIC_64)) )
+        return false;
+
+    __block bool found = false;
+    forEachSlice(diag, fileLen, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
+        const char* sliceArchName = MachOFile::archName(sliceCpuType, sliceCpuSubType);
+        if ( strcmp(sliceArchName, archName) == 0 ) {
+            sliceOffset = (char*)sliceStart - (char*)this;
+            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 && (strcmp(archName, "x86_64h") == 0) )
+        return isFatFileWithSlice(diag, fileLen, "x86_64", sliceOffset, sliceLen, missingSlice);
+
+    return found;
+}
+
+
+////////////////////////////  MachOFile ////////////////////////////////////////
+
+
+const MachOFile::ArchInfo MachOFile::_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   },
+#if SUPPORT_ARCH_arm64e
+    { "arm64e",   CPU_TYPE_ARM64,    CPU_SUBTYPE_ARM64_E     },
+#endif
+#if SUPPORT_ARCH_arm64_32
+    { "arm64_32", CPU_TYPE_ARM64_32, CPU_SUBTYPE_ARM64_32_V8 },
+#endif
+    { "armv7k",   CPU_TYPE_ARM,      CPU_SUBTYPE_ARM_V7K     },
+    { "armv7s",   CPU_TYPE_ARM,      CPU_SUBTYPE_ARM_V7S     },
+    { "armv7",    CPU_TYPE_ARM,      CPU_SUBTYPE_ARM_V7      }
+};
+
+const MachOFile::PlatformInfo MachOFile::_s_platformInfos[] = {
+    { "macOS",       Platform::macOS,             LC_VERSION_MIN_MACOSX   },
+    { "iOS",         Platform::iOS,               LC_VERSION_MIN_IPHONEOS },
+    { "tvOS",        Platform::tvOS,              LC_VERSION_MIN_TVOS     },
+    { "watchOS",     Platform::watchOS,           LC_VERSION_MIN_WATCHOS  },
+    { "bridgeOS",    Platform::bridgeOS,          LC_BUILD_VERSION        },
+    { "iOSMac",      Platform::iOSMac,            LC_BUILD_VERSION        },
+    { "iOS-sim",     Platform::iOS_simulator,     LC_BUILD_VERSION        },
+    { "tvOS-sim",    Platform::tvOS_simulator,    LC_BUILD_VERSION        },
+    { "watchOS-sim", Platform::watchOS_simulator, LC_BUILD_VERSION        },
+};
+
+
+bool MachOFile::is64() const
+{
+    return (this->magic == MH_MAGIC_64);
+}
+
+uint32_t MachOFile::pointerSize() const
+{
+    if (this->magic == MH_MAGIC_64)
+        return 8;
+    else
+        return 4;
+}
+
+bool MachOFile::uses16KPages() const
+{
+    switch (this->cputype) {
+        case CPU_TYPE_ARM64:
+        case CPU_TYPE_ARM:
+        case CPU_TYPE_ARM64_32:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool MachOFile::isArch(const char* aName) const
+{
+    return (strcmp(aName, archName(this->cputype, this->cpusubtype)) == 0);
+}
+
+const char* MachOFile::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 MachOFile::cpuTypeFromArchName(const char* archName)
+{
+    for (const ArchInfo& info : _s_archInfos) {
+        if ( strcmp(archName, info.name) == 0 ) {
+            return info.cputype;
+        }
+    }
+    return 0;
+}
+
+uint32_t MachOFile::cpuSubtypeFromArchName(const char* archName)
+{
+    for (const ArchInfo& info : _s_archInfos) {
+        if ( strcmp(archName, info.name) == 0 ) {
+            return info.cpusubtype;
+        }
+    }
+    return 0;
+}
+
+const char* MachOFile::archName() const
+{
+    return archName(this->cputype, this->cpusubtype);
+}
+
+static void appendDigit(char*& s, unsigned& num, unsigned place, bool& startedPrinting)
+{
+    if ( num >= place ) {
+        unsigned dig = (num/place);
+        *s++ = '0' + dig;
+        num -= (dig*place);
+        startedPrinting = true;
+    }
+    else if ( startedPrinting ) {
+        *s++ = '0';
+    }
+}
+
+static void appendNumber(char*& s, unsigned num)
+{
+    assert(num < 99999);
+    bool startedPrinting = false;
+    appendDigit(s, num, 10000, startedPrinting);
+    appendDigit(s, num,  1000, startedPrinting);
+    appendDigit(s, num,   100, startedPrinting);
+    appendDigit(s, num,    10, startedPrinting);
+    appendDigit(s, num,     1, startedPrinting);
+    if ( !startedPrinting )
+        *s++ = '0';
+}
+
+void MachOFile::packedVersionToString(uint32_t packedVersion, char versionString[32])
+{
+    // sprintf(versionString, "%d.%d.%d", (packedVersion >> 16), ((packedVersion >> 8) & 0xFF), (packedVersion & 0xFF));
+    char* s = versionString;
+    appendNumber(s, (packedVersion >> 16));
+    *s++ = '.';
+    appendNumber(s, (packedVersion >> 8) & 0xFF);
+    *s++ = '.';
+    appendNumber(s, (packedVersion & 0xFF));
+    *s++ = '\0';
+}
+
+bool MachOFile::supportsPlatform(Platform reqPlatform) const
+{
+    __block bool foundRequestedPlatform = false;
+    __block bool foundOtherPlatform = false;
+    forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+        if ( platform == reqPlatform )
+            foundRequestedPlatform = true;
+        else
+            foundOtherPlatform = true;
+    });
+    if ( foundRequestedPlatform )
+        return true;
+
+    // we did find some platform info, but not requested, so return false
+    if ( foundOtherPlatform )
+        return false;
+
+    // binary has no explict load command to mark platform
+    // could be an old macOS binary, look at arch
+    if  ( reqPlatform == Platform::macOS ) {
+        if ( this->cputype == CPU_TYPE_X86_64 )
+            return true;
+        if ( this->cputype == CPU_TYPE_I386 )
+            return true;
+    }
+
+    return false;
+}
+
+Platform MachOFile::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
+}
+
+#if __x86_64__
+static bool isHaswell()
+{
+    // FIXME: figure out a commpage way to check this
+    static bool sAlreadyDetermined = false;
+    static bool sHaswell = false;
+    if ( !sAlreadyDetermined ) {
+        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);
+        sHaswell = (result == KERN_SUCCESS) && (info.cpu_subtype == CPU_SUBTYPE_X86_64_H);
+        sAlreadyDetermined = true;
+    }
+    return sHaswell;
+}
+#endif
+
+const char* MachOFile::currentArchName()
+{
+#if __ARM_ARCH_7K__
+    return "armv7k";
+#elif __ARM_ARCH_7A__
+    return "armv7";
+#elif __ARM_ARCH_7S__
+    return "armv7s";
+#elif __arm64e__
+    return "arm64e";
+#elif __arm64__
+#if __LP64__
+    return "arm64";
+#else
+    return "arm64_32";
+#endif
+#elif __x86_64__
+    return isHaswell() ? "x86_64h" : "x86_64";
+#elif __i386__
+    return "i386";
+#else
+    #error unknown arch
+#endif
+}
+
+
+bool MachOFile::isDylib() const
+{
+    return (this->filetype == MH_DYLIB);
+}
+
+bool MachOFile::isBundle() const
+{
+    return (this->filetype == MH_BUNDLE);
+}
+
+bool MachOFile::isMainExecutable() const
+{
+    return (this->filetype == MH_EXECUTE);
+}
+
+bool MachOFile::isDynamicExecutable() const
+{
+    if ( this->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 MachOFile::isPIE() const
+{
+    return (this->flags & MH_PIE);
+}
+
+const char* MachOFile::platformName(Platform reqPlatform)
+{
+    for (const PlatformInfo& info : _s_platformInfos) {
+        if ( info.platform == reqPlatform )
+            return info.name;
+    }
+    return "unknown platform";
+}
+
+void MachOFile::forEachSupportedPlatform(void (^handler)(Platform platform, uint32_t minOS, uint32_t sdk)) const
+{
+    Diagnostics diag;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        const build_version_command* buildCmd = (build_version_command *)cmd;
+        const version_min_command*   versCmd  = (version_min_command*)cmd;
+        switch ( cmd->cmd ) {
+            case LC_BUILD_VERSION:
+                handler((Platform)(buildCmd->platform), buildCmd->minos, buildCmd->sdk);
+                break;
+            case LC_VERSION_MIN_MACOSX:
+                handler(Platform::macOS, versCmd->version, versCmd->sdk);
+                break;
+            case LC_VERSION_MIN_IPHONEOS:
+                if ( (this->cputype == CPU_TYPE_X86_64) || (this->cputype == CPU_TYPE_I386) )
+                    handler(Platform::iOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
+                else
+                    handler(Platform::iOS, versCmd->version, versCmd->sdk);
+                break;
+            case LC_VERSION_MIN_TVOS:
+                if ( this->cputype == CPU_TYPE_X86_64 )
+                    handler(Platform::tvOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
+                else
+                    handler(Platform::tvOS, versCmd->version, versCmd->sdk);
+                break;
+            case LC_VERSION_MIN_WATCHOS:
+                if ( (this->cputype == CPU_TYPE_X86_64) || (this->cputype == CPU_TYPE_I386) )
+                    handler(Platform::watchOS_simulator, versCmd->version, versCmd->sdk); // old sim binary
+                else
+                    handler(Platform::watchOS, versCmd->version, versCmd->sdk);
+                break;
+        }
+    });
+    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
+}
+
+
+bool MachOFile::isMachO(Diagnostics& diag, uint64_t fileSize) const
+{
+    if ( !hasMachOMagic() ) {
+        diag.error("file does not start with MH_MAGIC[_64]");
+        return false;
+    }
+    if ( this->sizeofcmds + sizeof(mach_header_64) > fileSize ) {
+        diag.error("load commands exceed length of first segment");
+        return false;
+    }
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { });
+    return diag.noError();
+}
+
+bool MachOFile::hasMachOMagic() const
+{
+    return ( (this->magic == MH_MAGIC) || (this->magic == MH_MAGIC_64) );
+}
+
+void MachOFile::forEachLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& stop)) const
+{
+    bool stop = false;
+    const load_command* startCmds = nullptr;
+    if ( this->magic == MH_MAGIC_64 )
+        startCmds = (load_command*)((char *)this + sizeof(mach_header_64));
+    else if ( this->magic == MH_MAGIC )
+        startCmds = (load_command*)((char *)this + sizeof(mach_header));
+    else {
+        diag.error("file does not start with MH_MAGIC[_64]");
+        return;  // not a mach-o file
+    }
+    const load_command* const cmdsEnd = (load_command*)((char*)startCmds + this->sizeofcmds);
+    const load_command* cmd = startCmds;
+    for (uint32_t i = 0; i < this->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;
+    }
+}
+
+const char* MachOFile::installName() const
+{
+    const char*  name;
+    uint32_t     compatVersion;
+    uint32_t     currentVersion;
+    if ( getDylibInstallName(&name, &compatVersion, &currentVersion) )
+        return name;
+    return nullptr;
+}
+
+bool MachOFile::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;
+}
+
+bool MachOFile::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;
+}
+
+void MachOFile::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;
+                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 MachOFile::forDyldEnv(void (^callback)(const char* envVar, bool& stop)) const
+{
+    Diagnostics diag;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+         if ( cmd->cmd == LC_DYLD_ENVIRONMENT ) {
+            const dylinker_command* envCmd = (dylinker_command*)cmd;
+            const char* keyEqualsValue = (char*)envCmd + envCmd->name.offset;
+            // only process variables that start with DYLD_ and end in _PATH
+            if ( (strncmp(keyEqualsValue, "DYLD_", 5) == 0) ) {
+                const char* equals = strchr(keyEqualsValue, '=');
+                if ( equals != NULL ) {
+                    if ( strncmp(&equals[-5], "_PATH", 5) == 0 ) {
+                        callback(keyEqualsValue, stop);
+                    }
+                }
+            }
+        }
+    });
+    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
+}
+
+bool MachOFile::enforceCompatVersion() const
+{
+    __block bool result = true;
+    forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+        switch ( platform ) {
+            case Platform::macOS:
+                if ( minOS >= 0x000A0E00 )  // macOS 10.14
+                    result = false;
+                break;
+            case Platform::iOS:
+            case Platform::tvOS:
+            case Platform::iOS_simulator:
+            case Platform::tvOS_simulator:
+                if ( minOS >= 0x000C0000 )  // iOS 12.0
+                    result = false;
+                break;
+            case Platform::watchOS:
+            case Platform::watchOS_simulator:
+                if ( minOS >= 0x00050000 )  // watchOS 5.0
+                    result = false;
+                break;
+            case Platform::bridgeOS:
+                if ( minOS >= 0x00030000 )  // bridgeOS 3.0
+                    result = false;
+                break;
+            case Platform::iOSMac:
+                result = false;
+                break;
+            case Platform::unknown:
+                break;
+        }
+    });
+    return result;
+}
+
+
+void MachOFile::forEachSegment(void (^callback)(const SegmentInfo& info, bool& stop)) const
+{
+    Diagnostics diag;
+    const bool intel32 = (this->cputype == CPU_TYPE_I386);
+    __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;
+            }
+            SegmentInfo info;
+            info.fileOffset        = segCmd->fileoff;
+            info.fileSize          = segCmd->filesize;
+            info.vmAddr            = segCmd->vmaddr;
+            info.vmSize            = segCmd->vmsize;
+            info.sizeOfSections    = sizeOfSections;
+            info.segName           = segCmd->segname;
+            info.protections       = segCmd->initprot;
+            info.textRelocs        = false;
+            info.p2align           = p2align;
+            info.segIndex          = segIndex;
+            callback(info, stop);
+            ++segIndex;
+        }
+        else if ( cmd->cmd == LC_SEGMENT ) {
+            const segment_command* segCmd = (segment_command*)cmd;
+            uint64_t sizeOfSections = segCmd->vmsize;
+            uint8_t p2align = 0;
+            bool  hasTextRelocs = false;
+            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;
+                if ( sect->flags & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC) )
+                    hasTextRelocs = true;
+           }
+            SegmentInfo info;
+            info.fileOffset        = segCmd->fileoff;
+            info.fileSize          = segCmd->filesize;
+            info.vmAddr            = segCmd->vmaddr;
+            info.vmSize            = segCmd->vmsize;
+            info.sizeOfSections    = sizeOfSections;
+            info.segName           = segCmd->segname;
+            info.protections       = segCmd->initprot;
+            info.textRelocs        = intel32 && !info.writable() && hasTextRelocs;
+            info.p2align           = p2align;
+            info.segIndex          = segIndex;
+            callback(info, stop);
+            ++segIndex;
+        }
+    });
+    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
+}
+
+void MachOFile::forEachSection(void (^callback)(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop)) const
+{
+    Diagnostics diag;
+    const bool intel32 = (this->cputype == CPU_TYPE_I386);
+    __block uint32_t segIndex = 0;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        SectionInfo sectInfo;
+        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;
+            }
+            sectInfo.segInfo.fileOffset        = segCmd->fileoff;
+            sectInfo.segInfo.fileSize          = segCmd->filesize;
+            sectInfo.segInfo.vmAddr            = segCmd->vmaddr;
+            sectInfo.segInfo.vmSize            = segCmd->vmsize;
+            sectInfo.segInfo.sizeOfSections    = sizeOfSections;
+            sectInfo.segInfo.segName           = segCmd->segname;
+            sectInfo.segInfo.protections       = segCmd->initprot;
+            sectInfo.segInfo.textRelocs        = false;
+            sectInfo.segInfo.p2align           = p2align;
+            sectInfo.segInfo.segIndex          = segIndex;
+            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 malformedSectionRange = (sect->addr < segCmd->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, segCmd->vmaddr + segCmd->filesize);
+                sectInfo.sectName       = sectName;
+                sectInfo.sectFileOffset = sect->offset;
+                sectInfo.sectFlags      = sect->flags;
+                sectInfo.sectAddr       = sect->addr;
+                sectInfo.sectSize       = sect->size;
+                sectInfo.sectAlignP2    = sect->align;
+                sectInfo.reserved1      = sect->reserved1;
+                sectInfo.reserved2      = sect->reserved2;
+                callback(sectInfo, malformedSectionRange, stop);
+            }
+            ++segIndex;
+        }
+        else if ( cmd->cmd == LC_SEGMENT ) {
+            const segment_command* segCmd = (segment_command*)cmd;
+            uint64_t sizeOfSections = segCmd->vmsize;
+            uint8_t p2align = 0;
+            bool  hasTextRelocs = false;
+            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;
+                if ( sect->flags & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC) )
+                    hasTextRelocs = true;
+            }
+            sectInfo.segInfo.fileOffset        = segCmd->fileoff;
+            sectInfo.segInfo.fileSize          = segCmd->filesize;
+            sectInfo.segInfo.vmAddr            = segCmd->vmaddr;
+            sectInfo.segInfo.vmSize            = segCmd->vmsize;
+            sectInfo.segInfo.sizeOfSections    = sizeOfSections;
+            sectInfo.segInfo.segName           = segCmd->segname;
+            sectInfo.segInfo.protections       = segCmd->initprot;
+            sectInfo.segInfo.textRelocs        = intel32 && !sectInfo.segInfo.writable() && hasTextRelocs;
+            sectInfo.segInfo.p2align           = p2align;
+            sectInfo.segInfo.segIndex          = segIndex;
+            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 malformedSectionRange = (sect->addr < segCmd->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, segCmd->vmaddr + segCmd->filesize);
+                sectInfo.sectName       = sectName;
+                sectInfo.sectFileOffset = sect->offset;
+                sectInfo.sectFlags      = sect->flags;
+                sectInfo.sectAddr       = sect->addr;
+                sectInfo.sectSize       = sect->size;
+                sectInfo.sectAlignP2    = sect->align;
+                sectInfo.reserved1      = sect->reserved1;
+                sectInfo.reserved2      = sect->reserved2;
+                callback(sectInfo, malformedSectionRange, stop);
+            }
+            ++segIndex;
+        }
+    });
+    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
+}
+
+bool MachOFile::hasWeakDefs() const
+{
+    return (this->flags & MH_WEAK_DEFINES);
+}
+
+bool MachOFile::hasThreadLocalVariables() const
+{
+    return (this->flags & MH_HAS_TLV_DESCRIPTORS);
+}
+
+static bool endsWith(const char* str, const char* suffix)
+{
+    size_t strLen    = strlen(str);
+    size_t suffixLen = strlen(suffix);
+    if ( strLen < suffixLen )
+        return false;
+    return (strcmp(&str[strLen-suffixLen], suffix) == 0);
+}
+
+bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const
+{
+    // only dylibs can go in cache
+    if ( this->filetype != MH_DYLIB ) {
+        failureReason("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
+    bool retval = true;
+    const char* dylibName = installName();
+    if ( dylibName[0] != '/' ) {
+        retval = false;
+        failureReason("install name not an absolute path");
+    }
+    else if ( (strncmp(dylibName, "/usr/lib/", 9) != 0) && (strncmp(dylibName, "/System/Library/", 16) != 0) ) {
+        retval = false;
+        failureReason("Not in '/usr/lib/' or '/System/Library/'");
+    }
+
+    // flat namespace files cannot go in cache
+    if ( (this->flags & MH_TWOLEVEL) == 0 ) {
+        retval = false;
+        failureReason("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;
+        failureReason("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;
+        failureReason("Missing split seg info");
+    }
+    if ( !hasDyldInfo ) {
+        retval = false;
+        failureReason("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;
+        failureReason("Depends on dylibs ineligable for dyld cache");
+    }
+
+    // dylibs with interposing info cannot be in cache
+    __block bool hasInterposing = false;
+    forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool &stop) {
+        if ( ((info.sectFlags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(info.sectName, "__interpose") == 0) && (strcmp(info.segInfo.segName, "__DATA") == 0)) )
+            hasInterposing = true;
+    });
+    if ( hasInterposing ) {
+        retval = false;
+        failureReason("Has interposing tuples");
+    }
+
+    return retval;
+}
+
+
+bool MachOFile::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const
+{
+    if ( const encryption_info_command* encCmd = findFairPlayEncryptionLoadCommand() ) {
+       if ( encCmd->cryptid == 1 ) {
+            // Note: cryptid is 0 in just-built apps.  The AppStore sets cryptid to 1
+            textOffset = encCmd->cryptoff;
+            size       = encCmd->cryptsize;
+            return true;
+        }
+    }
+    textOffset = 0;
+    size = 0;
+    return false;
+}
+
+bool MachOFile::canBeFairPlayEncrypted() const
+{
+    return (findFairPlayEncryptionLoadCommand() != nullptr);
+}
+
+const encryption_info_command* MachOFile::findFairPlayEncryptionLoadCommand() const
+{
+    __block const encryption_info_command* result = nullptr;
+    Diagnostics diag;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+         if ( (cmd->cmd == LC_ENCRYPTION_INFO) || (cmd->cmd == LC_ENCRYPTION_INFO_64) ) {
+            result = (encryption_info_command*)cmd;
+            stop = true;
+        }
+    });
+    if ( diag.noError() )
+        return result;
+    else
+        return nullptr;
+}
+
+
+bool MachOFile::hasChainedFixups() const
+{
+#if SUPPORT_ARCH_arm64e
+    // for now only arm64e uses chained fixups
+    return ( strcmp(archName(), "arm64e") == 0 );
+#else
+    return false;
+#endif
+}
+
+uint64_t MachOFile::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 MachOFile::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 |= (~0ULL) << bit;
+    return result;
+}
+
+
+} // namespace dyld3
+
+
+
+
+
diff --git a/dyld3/MachOFile.h b/dyld3/MachOFile.h
new file mode 100644 (file)
index 0000000..4680e44
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * 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 MachOFile_h
+#define MachOFile_h
+
+#include <mach-o/loader.h>
+#include <mach-o/fat.h>
+#include <uuid/uuid.h>
+
+#include "Diagnostics.h"
+
+#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
+    #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE            0x02
+#endif
+
+
+#ifndef PLATFORM_IOSSIMULATOR
+    #define PLATFORM_IOSSIMULATOR (7)
+#endif
+
+#ifndef PLATFORM_TVOSSIMULATOR
+    #define PLATFORM_TVOSSIMULATOR (8)
+#endif
+
+#ifndef PLATFORM_WATCHOSSIMULATOR
+    #define PLATFORM_WATCHOSSIMULATOR (9)
+#endif
+
+
+namespace dyld3 {
+
+
+
+/// Returns true if (addLHS + addRHS) > b, or if the add overflowed
+template<typename T>
+inline 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>
+inline bool greaterThanAddOrOverflow(uint64_t addLHS, uint64_t addRHS, T b) {
+    return (addLHS > b) || (addRHS > (b-addLHS));
+}
+
+
+// Note, this should match PLATFORM_* values in <mach-o/loader.h>
+enum class Platform {
+    unknown             = 0,
+    macOS               = 1,    // PLATFORM_MACOS
+    iOS                 = 2,    // PLATFORM_IOS
+    tvOS                = 3,    // PLATFORM_TVOS
+    watchOS             = 4,    // PLATFORM_WATCHOS
+    bridgeOS            = 5,    // PLATFORM_BRIDGEOS
+    iOSMac              = 6,    // PLATFORM_IOSMAC
+    iOS_simulator       = 7,    // PLATFORM_IOSSIMULATOR
+    tvOS_simulator      = 8,    // PLATFORM_TVOSSIMULATOR
+    watchOS_simulator   = 9     // PLATFORM_WATCHOSSIMULATOR
+};
+
+// A file read/mapped into memory
+struct VIS_HIDDEN FatFile : fat_header
+{
+    static const FatFile*  isFatFile(const void* fileContent);
+    void                   forEachSlice(Diagnostics& diag, uint64_t fileLen, void (^callback)(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop)) const;
+    bool                   isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const char* archName, uint64_t& sliceOffset, uint64_t& sliceLen, bool& missingSlice) const;
+};
+
+
+// A mach-o file read/mapped into memory
+// Only info from mach_header or load commands is accessible (no LINKEDIT info)
+struct VIS_HIDDEN MachOFile : mach_header
+{
+    static const char*      archName(uint32_t cputype, uint32_t cpusubtype);
+    static const char*      platformName(Platform platform);
+    static uint32_t         cpuTypeFromArchName(const char* archName);
+    static uint32_t         cpuSubtypeFromArchName(const char* archName);
+    static void             packedVersionToString(uint32_t packedVersion, char versionString[32]);
+    static const char*      currentArchName();
+    static Platform         currentPlatform();
+    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);
+
+
+    bool            hasMachOMagic() const;
+       bool            isMachO(Diagnostics& diag, uint64_t fileSize) const;
+    bool            isDylib() const;
+    bool            isBundle() const;
+    bool            isMainExecutable() const;
+    bool            isDynamicExecutable() const;
+    bool            isPIE() const;
+    bool            isArch(const char* archName) const;
+    const char*     archName() const;
+    bool            is64() const;
+    uint32_t        pointerSize() const;
+    bool            uses16KPages() const;
+    bool            supportsPlatform(Platform) const;
+    bool            isSimulatorBinary() const;
+    bool            getUuid(uuid_t uuid) const;
+    bool            hasWeakDefs() const;
+    bool            hasThreadLocalVariables() const;
+    bool            getDylibInstallName(const char** installName, uint32_t* compatVersion, uint32_t* currentVersion) const;
+    void            forEachSupportedPlatform(void (^callback)(Platform platform, uint32_t minOS, uint32_t sdk)) const;
+    const char*     installName() const;  // returns nullptr is no install name
+    void            forEachDependentDylib(void (^callback)(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop)) const;
+    bool            canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const;
+    bool            canBeFairPlayEncrypted() const;
+    bool            isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const;
+    bool            hasChainedFixups() const;
+    void            forDyldEnv(void (^callback)(const char* envVar, bool& stop)) const;
+    bool            enforceCompatVersion() const;
+
+    struct SegmentInfo
+    {
+        uint64_t    fileOffset;
+        uint64_t    fileSize;
+        uint64_t    vmAddr;
+        uint64_t    vmSize;
+        uint64_t    sizeOfSections;
+        const char* segName;
+        uint32_t    protections;
+        uint32_t    textRelocs    :  1,  // segment has text relocs (i386 only)
+                    segIndex      : 15,
+                    p2align       : 16;
+        bool        readable() const   { return protections & VM_PROT_READ; }
+        bool        writable() const   { return protections & VM_PROT_WRITE; }
+        bool        executable() const { return protections & VM_PROT_EXECUTE; }
+     };
+
+    struct SectionInfo
+    {
+        SegmentInfo segInfo;
+        uint64_t    sectAddr;
+        uint64_t    sectSize;
+        const char* sectName;
+        uint32_t    sectFileOffset;
+        uint32_t    sectFlags;
+        uint32_t    sectAlignP2;
+        uint32_t    reserved1;
+        uint32_t    reserved2;
+    };
+
+    void            forEachSegment(void (^callback)(const SegmentInfo& info, bool& stop)) const;
+    void            forEachSection(void (^callback)(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop)) const;
+
+protected:
+    void            forEachLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& stop)) const;
+
+    const encryption_info_command* findFairPlayEncryptionLoadCommand() const;
+
+    struct ArchInfo
+    {
+        const char* name;
+        uint32_t    cputype;
+        uint32_t    cpusubtype;
+    };
+    static const ArchInfo       _s_archInfos[];
+
+    struct PlatformInfo
+    {
+        const char* name;
+        Platform    platform;
+        uint32_t    loadCommand;
+    };
+    static const PlatformInfo   _s_platformInfos[];
+};
+
+
+} // namespace dyld3
+
+#endif /* MachOFile_h */
diff --git a/dyld3/MachOLoaded.cpp b/dyld3/MachOLoaded.cpp
new file mode 100644 (file)
index 0000000..4e54752
--- /dev/null
@@ -0,0 +1,977 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#include <mach/mach.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <mach-o/reloc.h>
+#include <mach-o/nlist.h>
+#include <CommonCrypto/CommonDigest.h>
+
+#include <stdio.h>
+
+#include "MachOLoaded.h"
+#include "MachOFile.h"
+#include "MachOFile.h"
+#include "CodeSigningTypes.h"
+
+
+#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 {
+
+
+void MachOLoaded::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 hasMinVersion = 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 ( hasMinVersion )
+                     diag.error("multiple LC_VERSION_MIN_* load commands");
+                hasMinVersion = 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 ( hasMinVersion )
+                    diag.error("LC_BUILD_VERSION cannot coexist LC_VERSION_MIN_* with load commands");
+                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 MachOLoaded::getLinkEditPointers(Diagnostics& diag, LinkEditInfo& result) const
+{
+    getLinkEditLoadCommands(diag, result);
+    if ( diag.noError() )
+        getLayoutInfo(result.layout);
+}
+
+void MachOLoaded::getLayoutInfo(LayoutInfo& result) const
+{
+    forEachSegment(^(const SegmentInfo& info, bool& stop) {
+        if ( strcmp(info.segName, "__TEXT") == 0 ) {
+            result.textUnslidVMAddr = (uintptr_t)info.vmAddr;
+            result.slide = (uintptr_t)(((uint64_t)this) - info.vmAddr);
+        }
+        else if ( strcmp(info.segName, "__LINKEDIT") == 0 ) {
+            result.linkeditUnslidVMAddr = (uintptr_t)info.vmAddr;
+            result.linkeditFileOffset   = (uint32_t)info.fileOffset;
+            result.linkeditFileSize     = (uint32_t)info.fileSize;
+            result.linkeditSegIndex     = info.segIndex;
+        }
+    });
+}
+
+bool MachOLoaded::hasExportTrie(uint32_t& runtimeOffset, uint32_t& size) const
+{
+    runtimeOffset = 0;
+    size = 0;
+    Diagnostics diag;
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
+    if ( diag.hasError() )
+        return false;
+    if ( leInfo.dyldInfo != nullptr ) {
+        uint32_t offsetInLinkEdit = leInfo.dyldInfo->export_off - leInfo.layout.linkeditFileOffset;
+        runtimeOffset = offsetInLinkEdit + (uint32_t)(leInfo.layout.linkeditUnslidVMAddr - leInfo.layout.textUnslidVMAddr);
+        size = leInfo.dyldInfo->export_size;
+        return true;
+    }
+    return false;
+}
+
+
+#if BUILDING_LIBDYLD
+// this is only used by dlsym() at runtime.  All other binding is done when the closure is built.
+bool MachOLoaded::hasExportedSymbol(const char* symbolName, DependentToMachOLoaded finder, void** result,
+                                    bool* resultPointsToInstructions) const
+{
+    typedef void* (*ResolverFunc)(void);
+    ResolverFunc resolver;
+    Diagnostics diag;
+    FoundSymbol foundInfo;
+    if ( findExportedSymbol(diag, symbolName, foundInfo, finder) ) {
+        switch ( foundInfo.kind ) {
+            case FoundSymbol::Kind::headerOffset: {
+                *result = (uint8_t*)foundInfo.foundInDylib + foundInfo.value;
+                *resultPointsToInstructions = false;
+                int64_t slide = foundInfo.foundInDylib->getSlide();
+                foundInfo.foundInDylib->forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+                    uint64_t sectStartAddr = sectInfo.sectAddr + slide;
+                    uint64_t sectEndAddr = sectStartAddr + sectInfo.sectSize;
+                    if ( ((uint64_t)*result >= sectStartAddr) && ((uint64_t)*result < sectEndAddr) ) {
+                        *resultPointsToInstructions = (sectInfo.sectFlags & S_ATTR_PURE_INSTRUCTIONS) || (sectInfo.sectFlags & S_ATTR_SOME_INSTRUCTIONS);
+                        stop = true;
+                    }
+                });
+                break;
+            }
+            case FoundSymbol::Kind::absolute:
+                *result = (void*)(long)foundInfo.value;
+                *resultPointsToInstructions = false;
+                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)();
+                // FIXME: Set this properly
+                *resultPointsToInstructions = true;
+                break;
+        }
+        return true;
+    }
+    return false;
+}
+#endif // BUILDING_LIBDYLD
+
+bool MachOLoaded::findExportedSymbol(Diagnostics& diag, const char* symbolName, FoundSymbol& foundInfo, DependentToMachOLoaded 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 ) {
+                    if ( const MachOLoaded* depMH = findDependent(this, depIndex) ) {
+                       if ( depMH->findExportedSymbol(diag, symbolName, foundInfo, findDependent) ) {
+                            stop = true;
+                            foundInReExportedDylib = true;
+                        }
+                    }
+                }
+                ++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;
+            if ( (ordinal == 0) || (ordinal > dependentDylibCount()) ) {
+                diag.error("re-export ordinal %lld out of range for %s", ordinal, symbolName);
+                return false;
+            }
+            uint32_t depIndex = (uint32_t)(ordinal-1);
+            if ( const MachOLoaded* depMH = findDependent(this, depIndex) ) {
+                return depMH->findExportedSymbol(diag, importedName, 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.isWeakDef          = false;
+        foundInfo.foundInDylib       = this;
+        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;
+                }
+                if ( flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION )
+                    foundInfo.isWeakDef = true;
+                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;
+        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       = this;
+                foundInfo.value              = n_value - leInfo.layout.textUnslidVMAddr;
+                foundInfo.resolverFuncOffset = 0;
+                foundInfo.foundSymbolName    = symbolName;
+                stop = true;
+            }
+        });
+        if ( foundInfo.foundInDylib == nullptr ) {
+            // symbol not exported from this image. Search any re-exported dylibs
+            __block unsigned depIndex = 0;
+            forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+                if ( isReExport && findDependent ) {
+                    if ( const MachOLoaded* depMH = findDependent(this, depIndex) ) {
+                        if ( depMH->findExportedSymbol(diag, symbolName, foundInfo, findDependent) ) {
+                            stop = true;
+                        }
+                    }
+                }
+                ++depIndex;
+            });
+        }
+        return (foundInfo.foundInDylib != nullptr);
+    }
+}
+
+intptr_t MachOLoaded::getSlide() const
+{
+    Diagnostics diag;
+    __block intptr_t slide = 0;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == LC_SEGMENT_64 ) {
+            const segment_command_64* seg = (segment_command_64*)cmd;
+            if ( strcmp(seg->segname, "__TEXT") == 0 ) {
+                slide = (uintptr_t)(((uint64_t)this) - seg->vmaddr);
+                stop = true;
+            }
+        }
+        else if ( cmd->cmd == LC_SEGMENT ) {
+            const segment_command* seg = (segment_command*)cmd;
+            if ( strcmp(seg->segname, "__TEXT") == 0 ) {
+                slide = (uintptr_t)(((uint64_t)this) - seg->vmaddr);
+                stop = true;
+            }
+        }
+    });
+    diag.assertNoError();   // any malformations in the file should have been caught by earlier validate() call
+    return slide;
+}
+
+const uint8_t* MachOLoaded::getLinkEditContent(const LayoutInfo& info, uint32_t fileOffset) const
+{
+    uint32_t offsetInLinkedit   = fileOffset - info.linkeditFileOffset;
+    uintptr_t linkeditStartAddr = info.linkeditUnslidVMAddr + info.slide;
+    return (uint8_t*)(linkeditStartAddr + offsetInLinkedit);
+}
+
+
+void MachOLoaded::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*)symbols;
+        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 MachOLoaded::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);
+            }
+        }
+    }
+}
+
+uint32_t MachOLoaded::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* MachOLoaded::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;
+}
+
+const char* MachOLoaded::segmentName(uint32_t targetSegIndex) const
+{
+    __block const char* result = nullptr;
+       forEachSegment(^(const SegmentInfo& info, bool& stop) {
+        if ( targetSegIndex == info.segIndex ) {
+            result = info.segName;
+            stop = true;
+        }
+    });
+    return result;
+}
+
+bool MachOLoaded::findClosestFunctionStart(uint64_t address, uint64_t* functionStartAddress) const
+{
+    Diagnostics diag;
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() )
+        return false;
+    if ( leInfo.functionStarts == nullptr )
+        return false;
+
+    const uint8_t* starts    = getLinkEditContent(leInfo.layout, leInfo.functionStarts->dataoff);
+    const uint8_t* startsEnd = starts + leInfo.functionStarts->datasize;
+
+    uint64_t lastAddr    = (uint64_t)(long)this;
+    uint64_t runningAddr = lastAddr;
+    while (diag.noError()) {
+        uint64_t value = read_uleb128(diag, starts, startsEnd);
+        if ( value == 0 )
+            break;
+        lastAddr = runningAddr;
+        runningAddr += value;
+        //fprintf(stderr, "  addr=0x%08llX\n", runningAddr);
+        if ( runningAddr > address ) {
+            *functionStartAddress = lastAddr;
+            return true;
+        }
+    };
+
+    return false;
+}
+
+bool MachOLoaded::findClosestSymbol(uint64_t address, const char** symbolName, uint64_t* symbolAddr) const
+{
+    Diagnostics diag;
+    LinkEditInfo leInfo;
+    getLinkEditPointers(diag, leInfo);
+    if ( diag.hasError() )
+        return false;
+    if ( (leInfo.symTab == nullptr) || (leInfo.dynSymTab == nullptr) )
+        return false;
+    uint64_t targetUnslidAddress = address - leInfo.layout.slide;
+
+    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));
+    if ( is64() ) {
+        const struct nlist_64* symbols64  = (struct nlist_64*)symbols;
+        const struct nlist_64* bestSymbol = nullptr;
+        // first walk all global symbols
+        const struct nlist_64* const globalsStart = &symbols64[leInfo.dynSymTab->iextdefsym];
+        const struct nlist_64* const globalsEnd   = &globalsStart[leInfo.dynSymTab->nextdefsym];
+        for (const struct nlist_64* s = globalsStart; s < globalsEnd; ++s) {
+            if ( (s->n_type & N_TYPE) == N_SECT ) {
+                if ( bestSymbol == nullptr ) {
+                    if ( s->n_value <= targetUnslidAddress )
+                        bestSymbol = s;
+                }
+                else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) ) {
+                    bestSymbol = s;
+                }
+            }
+        }
+        // next walk all local symbols
+        const struct nlist_64* const localsStart = &symbols64[leInfo.dynSymTab->ilocalsym];
+        const struct nlist_64* const localsEnd   = &localsStart[leInfo.dynSymTab->nlocalsym];
+        for (const struct nlist_64* s = localsStart; s < localsEnd; ++s) {
+             if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) {
+                if ( bestSymbol == nullptr ) {
+                    if ( s->n_value <= targetUnslidAddress )
+                        bestSymbol = s;
+                }
+                else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) ) {
+                    bestSymbol = s;
+                }
+            }
+        }
+        if ( bestSymbol != NULL ) {
+            *symbolAddr = bestSymbol->n_value + leInfo.layout.slide;
+            if ( bestSymbol->n_un.n_strx < maxStringOffset )
+                *symbolName = &stringPool[bestSymbol->n_un.n_strx];
+            return true;
+        }
+    }
+    else {
+       const struct nlist* bestSymbol = nullptr;
+        // first walk all global symbols
+        const struct nlist* const globalsStart = &symbols[leInfo.dynSymTab->iextdefsym];
+        const struct nlist* const globalsEnd   = &globalsStart[leInfo.dynSymTab->nextdefsym];
+        for (const struct nlist* s = globalsStart; s < globalsEnd; ++s) {
+            if ( (s->n_type & N_TYPE) == N_SECT ) {
+                if ( bestSymbol == nullptr ) {
+                    if ( s->n_value <= targetUnslidAddress )
+                        bestSymbol = s;
+                }
+                else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) ) {
+                    bestSymbol = s;
+                }
+            }
+        }
+        // next walk all local symbols
+        const struct nlist* const localsStart = &symbols[leInfo.dynSymTab->ilocalsym];
+        const struct nlist* const localsEnd   = &localsStart[leInfo.dynSymTab->nlocalsym];
+        for (const struct nlist* s = localsStart; s < localsEnd; ++s) {
+             if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) {
+                if ( bestSymbol == nullptr ) {
+                    if ( s->n_value <= targetUnslidAddress )
+                        bestSymbol = s;
+                }
+                else if ( (s->n_value <= targetUnslidAddress) && (bestSymbol->n_value < s->n_value) ) {
+                    bestSymbol = s;
+                }
+            }
+        }
+        if ( bestSymbol != nullptr ) {
+#if __arm__
+            if ( bestSymbol->n_desc & N_ARM_THUMB_DEF )
+                *symbolAddr = (bestSymbol->n_value | 1) + leInfo.layout.slide;
+            else
+                *symbolAddr = bestSymbol->n_value + leInfo.layout.slide;
+#else
+            *symbolAddr = bestSymbol->n_value + leInfo.layout.slide;
+#endif
+            if ( bestSymbol->n_un.n_strx < maxStringOffset )
+                *symbolName = &stringPool[bestSymbol->n_un.n_strx];
+            return true;
+        }
+    }
+
+    return false;
+}
+
+const void* MachOLoaded::findSectionContent(const char* segName, const char* sectName, uint64_t& size) const
+{
+    __block const void* result = nullptr;
+    forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+        if ( (strcmp(sectInfo.sectName, sectName) == 0) && (strcmp(sectInfo.segInfo.segName, segName) == 0) ) {
+            size = sectInfo.sectSize;
+            result = (void*)(sectInfo.sectAddr + getSlide());
+        }
+    });
+    return result;
+}
+
+
+bool MachOLoaded::intersectsRange(uintptr_t start, uintptr_t length) const
+{
+    __block bool result = false;
+    uintptr_t slide = getSlide();
+    forEachSegment(^(const SegmentInfo& info, bool& stop) {
+        if ( (info.vmAddr+info.vmSize+slide >= start) && (info.vmAddr+slide < start+length) )
+            result = true;
+    });
+    return result;
+}
+
+const uint8_t* MachOLoaded::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 > (uint64_t)(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;
+}
+
+bool MachOLoaded::cdHashOfCodeSignature(const void* codeSigStart, size_t codeSignLen, uint8_t cdHash[20]) const
+{
+    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_SHA384 ) {
+        uint8_t digest[CC_SHA384_DIGEST_LENGTH];
+        CC_SHA384(cd, cdLength, digest);
+        // cd-hash of sigs that use SHA384 is the first 20 bytes of the SHA384 of the code digest
+        memcpy(cdHash, digest, 20);
+        return true;
+    }
+    else if ( (cd->hashType == CS_HASHTYPE_SHA256) || (cd->hashType == CS_HASHTYPE_SHA256_TRUNCATED) ) {
+        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;
+}
+
+
+// Note, this has to match the kernel
+static const uint32_t hashPriorities[] = {
+    CS_HASHTYPE_SHA1,
+    CS_HASHTYPE_SHA256_TRUNCATED,
+    CS_HASHTYPE_SHA256,
+    CS_HASHTYPE_SHA384,
+};
+
+static unsigned int hash_rank(const CS_CodeDirectory *cd)
+{
+    uint32_t type = cd->hashType;
+    for (uint32_t n = 0; n < sizeof(hashPriorities) / sizeof(hashPriorities[0]); ++n) {
+        if (hashPriorities[n] == type)
+            return n + 1;
+    }
+
+    /* not supported */
+    return 0;
+}
+
+
+// Note, this has to match the kernel
+static const uint32_t hashPriorities_watchOS[] = {
+    CS_HASHTYPE_SHA1
+};
+
+static unsigned int hash_rank_watchOS(const CS_CodeDirectory *cd)
+{
+    uint32_t type = cd->hashType;
+    for (uint32_t n = 0; n < sizeof(hashPriorities_watchOS) / sizeof(hashPriorities_watchOS[0]); ++n) {
+        if (hashPriorities_watchOS[n] == type)
+            return n + 1;
+    }
+
+    /* not supported */
+    return 0;
+}
+
+const void* MachOLoaded::findCodeDirectoryBlob(const void* codeSigStart, size_t codeSignLen) const
+{
+    // 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;
+
+    // Note: The kernel currently always uses sha1 for watchOS, even if other hashes are available.
+    const bool isWatchOS = this->supportsPlatform(Platform::watchOS);
+    auto hashRankFn = isWatchOS ? &hash_rank_watchOS : &hash_rank;
+
+    // walk each sub blob, looking at ones with type CSSLOT_CODEDIRECTORY
+    const CS_CodeDirectory* bestCd = nullptr;
+    for (uint32_t i=0; i < subBlobCount; ++i) {
+        if ( codeSuperBlob->index[i].type == htonl(CSSLOT_CODEDIRECTORY) ) {
+            // Ok, this is the regular code directory
+        } else if ( codeSuperBlob->index[i].type >= htonl(CSSLOT_ALTERNATE_CODEDIRECTORIES) && codeSuperBlob->index[i].type <= htonl(CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT)) {
+            // Ok, this is the alternative code directory
+        } else {
+            continue;
+        }
+        uint32_t cdOffset = htonl(codeSuperBlob->index[i].offset);
+        // verify offset is not out of range
+        if ( cdOffset > (codeSignLen - sizeof(CS_CodeDirectory)) )
+            continue;
+        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) )
+            continue;
+        if ( cd->magic == htonl(CSMAGIC_CODEDIRECTORY) ) {
+            if ( !bestCd || (hashRankFn(cd) > hashRankFn(bestCd)) )
+                bestCd = cd;
+        }
+    }
+    return bestCd;
+}
+
+
+// Regular pointer which needs to fit in 51-bits of value.
+// C++ RTTI uses the top bit, so we'll allow the whole top-byte
+// and the signed-extended bottom 43-bits to be fit in to 51-bits.
+uint64_t MachOLoaded::ChainedFixupPointerOnDisk::signExtend51(uint64_t value51)
+{
+    uint64_t top8Bits     = value51 & 0x007F80000000000ULL;
+    uint64_t bottom43Bits = value51 & 0x000007FFFFFFFFFFULL;
+    uint64_t newValue     = (top8Bits << 13) | (((intptr_t)(bottom43Bits << 21) >> 21) & 0x00FFFFFFFFFFFFFF);
+    return newValue;
+}
+
+uint64_t MachOLoaded::ChainedFixupPointerOnDisk::PlainRebase::signExtendedTarget() const
+{
+    return signExtend51(this->target);
+}
+
+uint64_t MachOLoaded::ChainedFixupPointerOnDisk::PlainBind::signExtendedAddend() const
+{
+    uint64_t addend19     = this->addend;
+    if ( addend19 & 0x40000 )
+        return addend19 | 0xFFFFFFFFFFFC0000ULL;
+    else
+        return addend19;
+}
+
+const char* MachOLoaded::ChainedFixupPointerOnDisk::keyName(uint8_t keyBits)
+{
+    static const char* names[] = {
+        "IA", "IB", "DA", "DB"
+    };
+    assert(keyBits < 4);
+    return names[keyBits];
+}
+
+const char* MachOLoaded::ChainedFixupPointerOnDisk::AuthRebase::keyName() const
+{
+    return ChainedFixupPointerOnDisk::keyName(this->key);
+}
+
+const char* MachOLoaded::ChainedFixupPointerOnDisk::AuthBind::keyName() const
+{
+    return ChainedFixupPointerOnDisk::keyName(this->key);
+}
+
+
+uint64_t MachOLoaded::ChainedFixupPointerOnDisk::signPointer(void* loc, uint64_t target) const
+{
+#if __has_feature(ptrauth_calls)
+    uint64_t discriminator = authBind.diversity;
+    if ( authBind.addrDiv )
+        discriminator = __builtin_ptrauth_blend_discriminator(loc, discriminator);
+    switch ( authBind.key ) {
+        case 0: // IA
+            return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target, 0, discriminator);
+        case 1: // IB
+            return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target, 1, discriminator);
+        case 2: // DA
+            return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target, 2, discriminator);
+        case 3: // DB
+            return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target, 3, discriminator);
+    }
+#endif
+    return target;
+}
+
+
+
+} // namespace dyld3
+
diff --git a/dyld3/MachOLoaded.h b/dyld3/MachOLoaded.h
new file mode 100644 (file)
index 0000000..3895a70
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * 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 MachOLoaded_h
+#define MachOLoaded_h
+
+#include <stdint.h>
+
+#include "MachOFile.h"
+
+
+class CacheBuilder;
+
+namespace dyld3 {
+
+
+// A mach-o mapped into memory with zero-fill expansion
+// Can be used in dyld at runtime or during closure building
+struct VIS_HIDDEN MachOLoaded : public MachOFile
+{
+       typedef const MachOLoaded* (^DependentToMachOLoaded)(const MachOLoaded* image, uint32_t depIndex);
+
+    // for dlsym()
+       bool                hasExportedSymbol(const char* symbolName, DependentToMachOLoaded finder, void** result,
+                                          bool* resultPointsToInstructions) const;
+
+    // for DYLD_PRINT_SEGMENTS
+    const char*         segmentName(uint32_t segIndex) const;
+
+    // used to see if main executable overlaps shared region
+    bool                intersectsRange(uintptr_t start, uintptr_t length) const;
+
+    // for _dyld_get_image_slide()
+    intptr_t            getSlide() const;
+
+    // quick check if image has been incorporated into the dyld cache
+    bool                   inDyldCache() const { return (this->flags & 0x80000000); }
+
+    // for dladdr()
+    bool                findClosestSymbol(uint64_t unSlidAddr, const char** symbolName, uint64_t* symbolUnslidAddr) const;
+
+    // for _dyld_find_unwind_sections()
+    const void*         findSectionContent(const char* segName, const char* sectName, uint64_t& size) const;
+
+    // used at runtime to validate loaded image matches closure
+    bool                cdHashOfCodeSignature(const void* codeSigStart, size_t codeSignLen, uint8_t cdHash[20]) const;
+
+    // used by DyldSharedCache to find closure
+    static const uint8_t*   trieWalk(Diagnostics& diag, const uint8_t* start, const uint8_t* end, const char* symbol);
+
+    // used by cache builder during error handling in chain bind processing
+    const char*             dependentDylibLoadPath(uint32_t depIndex) const;
+
+    // used by closure builder to find the offset and size of the trie.
+    bool                    hasExportTrie(uint32_t& runtimeOffset, uint32_t& size) const;
+
+
+    // For use with new rebase/bind scheme were each fixup location on disk contains info on what
+    // fix up it needs plus the offset to the next fixup.
+    union ChainedFixupPointerOnDisk
+    {
+        struct PlainRebase
+        {
+            uint64_t    target   : 51,
+                        next     : 11,
+                        bind     :  1,    // 0
+                        auth     :  1;    // 0
+            uint64_t    signExtendedTarget() const;
+        };
+        struct PlainBind
+        {
+            uint64_t    ordinal   : 16,
+                        zero      : 16,
+                        addend    : 19,
+                        next      : 11,
+                        bind      :  1,    // 1
+                        auth      :  1;    // 0
+            uint64_t    signExtendedAddend() const;
+        };
+        struct AuthRebase
+        {
+            uint64_t    target    : 32,
+                        diversity : 16,
+                        addrDiv   :  1,
+                        key       :  2,
+                        next      : 11,
+                        bind      :  1,    // 0
+                        auth      :  1;    // 1
+            const char* keyName() const;
+        };
+        struct AuthBind
+        {
+            uint64_t    ordinal   : 16,
+                        zero      : 16,
+                        diversity : 16,
+                        addrDiv   :  1,
+                        key       :  2,
+                        next      : 11,
+                        bind      :  1,    // 1
+                        auth      :  1;    // 1
+            const char* keyName() const;
+        };
+
+        uint64_t        raw;
+        AuthRebase      authRebase;
+        AuthBind        authBind;
+        PlainRebase     plainRebase;
+        PlainBind       plainBind;
+
+        static const char*  keyName(uint8_t keyBits);
+        static uint64_t     signExtend51(uint64_t);
+        uint64_t            signPointer(void* loc, uint64_t target) const;
+     };
+
+protected:
+    friend CacheBuilder;
+
+    struct FoundSymbol {
+        enum class Kind { headerOffset, absolute, resolverOffset };
+        Kind                kind;
+        bool                isThreadLocal;
+        bool                isWeakDef;
+        const MachOLoaded*  foundInDylib;
+        uint64_t            value;
+        uint32_t            resolverFuncOffset;
+        const char*         foundSymbolName;
+    };
+
+     struct LayoutInfo {
+        uintptr_t    slide;
+        uintptr_t    textUnslidVMAddr;
+        uintptr_t    linkeditUnslidVMAddr;
+        uint32_t     linkeditFileOffset;
+        uint32_t     linkeditFileSize;
+        uint32_t     linkeditSegIndex;
+    };
+
+    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;
+    };
+
+    bool                    findExportedSymbol(Diagnostics& diag, const char* symbolName, FoundSymbol& foundInfo, DependentToMachOLoaded finder) const;
+    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;
+    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;
+    uint32_t                dependentDylibCount() const;
+    bool                    findClosestFunctionStart(uint64_t address, uint64_t* functionStartAddress) const;
+
+    const void*             findCodeDirectoryBlob(const void* codeSigStart, size_t codeSignLen) const;
+
+};
+
+} // namespace dyld3
+
+#endif /* MachOLoaded_h */
diff --git a/dyld3/MachOParser.cpp b/dyld3/MachOParser.cpp
deleted file mode 100644 (file)
index 92366f0..0000000
+++ /dev/null
@@ -1,3507 +0,0 @@
-/*
- * 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;
-    }
-    // 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
deleted file mode 100644 (file)
index 73b5ead..0000000
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * 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
index 2516175dae77b2d715c43bbf5e9b9641b08018ae..7e3b1f8d0fc0964ae4bd661d0008833afe7ee363 100644 (file)
@@ -30,6 +30,7 @@
 #include <mach/mach.h>
 #include <sys/stat.h> 
 #include <fcntl.h>
+#include <limits.h>
 #include <sys/errno.h>
 #include <unistd.h>
 
@@ -38,6 +39,7 @@
 
 
 namespace dyld3 {
+namespace closure {
 
 #if BUILDING_LIBDYLD
 PathOverrides   gPathOverrides;
@@ -55,79 +57,39 @@ static const char* strrstr(const char* str, const char* sub)
     return NULL;
 }
 
+    
+void PathOverrides::setFallbackPathHandling(FallbackPathMode mode)
+{
+    _fallbackPathMode = mode;
+}
 
-#if DYLD_IN_PROCESS
-void PathOverrides::setEnvVars(const char* envp[])
+void PathOverrides::setEnvVars(const char* envp[], const MachOFile* mainExe, const char* mainExePath)
 {
     for (const char** p = envp; *p != NULL; p++) {
         addEnvVar(*p);
     }
+    if ( mainExe != nullptr )
+        setMainExecutable(mainExe, mainExePath);
 }
 
-#else
-PathOverrides::PathOverrides(const std::vector<std::string>& env)
+void PathOverrides::setMainExecutable(const dyld3::MachOFile* mainExe, const char* mainExePath)
 {
-    for (const std::string& envVar : env) {
-        addEnvVar(envVar.c_str());
-    }
+    assert(mainExe != nullptr);
+    assert(mainExe->isMainExecutable());
+    // process any LC_DYLD_ENVIRONMENT load commands in main executable
+       mainExe->forDyldEnv(^(const char* envVar, bool& stop) {
+        addEnvVar(envVar);
+       });
 }
-#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;
@@ -150,112 +112,150 @@ uint32_t PathOverrides::envVarCount() const
 
 void PathOverrides::forEachInsertedDylib(void (^handler)(const char* dylibPath)) const
 {
-    if ( _insertedDylibs == nullptr )
+    if ( _insertedDylibs != nullptr ) {
+        forEachInColonList(_insertedDylibs, ^(const char* path, bool &stop) {
+            handler(path);
+        });
+    }
+}
+
+void PathOverrides::handleEnvVar(const char* key, const char* value, void (^handler)(const char* envVar)) const
+{
+    if ( value == nullptr )
         return;
-    for (const char** lp=_insertedDylibs; *lp != nullptr; ++lp)
-        handler(*lp);
+    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::forEachEnvVar(void (^handler)(const char* envVar)) const
+{
+    handleEnvVar("DYLD_LIBRARY_PATH",            _dylibPathOverrides,      handler);
+    handleEnvVar("DYLD_FRAMEWORK_PATH",          _frameworkPathOverrides,  handler);
+    handleEnvVar("DYLD_FALLBACK_FRAMEWORK_PATH", _frameworkPathFallbacks,  handler);
+    handleEnvVar("DYLD_FALLBACK_LIBRARY_PATH",   _dylibPathFallbacks,      handler);
+    handleEnvVar("DYLD_INSERT_LIBRARIES",        _insertedDylibs,          handler);
+    handleEnvVar("DYLD_IMAGE_SUFFIX",            _imageSuffix,             handler);
+    handleEnvVar("DYLD_ROOT_PATH",               _rootPath,                handler);
+}
+
+const char* PathOverrides::addString(const char* str)
+{
+    if ( _pathPool == nullptr )
+        _pathPool = PathPool::allocate();
+    return _pathPool->add(str);
+}
+
+void PathOverrides::setString(const char*& var, const char* value)
+{
+    if ( var == nullptr ) {
+        var = addString(value);
+        return;
+    }
+    // string already in use, build new appended string
+    char tmp[strlen(var)+strlen(value)+2];
+    strcpy(tmp, var);
+    strcat(tmp, ":");
+    strcat(tmp, value);
+    var = addString(tmp);
 }
 
 void PathOverrides::addEnvVar(const char* keyEqualsValue)
 {
+    // We have to make a copy of the env vars because the dyld
+    // semantics is that the env vars are only looked at once
+    // at launch (using setenv() at runtime does not change dyld behavior).
     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);
+        if ( strncmp(keyEqualsValue, "DYLD_LIBRARY_PATH", 17) == 0 ) {
+            setString(_dylibPathOverrides, &keyEqualsValue[18]);
         }
-        else if ( strcmp(key, "DYLD_FRAMEWORK_PATH") == 0 ) {
-            _frameworkPathOverrides = parseColonListIntoArray(value);
+        else if ( strncmp(keyEqualsValue, "DYLD_FRAMEWORK_PATH", 19) == 0 ) {
+            setString(_frameworkPathOverrides, &keyEqualsValue[20]);
         }
-        else if ( strcmp(key, "DYLD_FALLBACK_FRAMEWORK_PATH") == 0 ) {
-            _frameworkPathFallbacks = parseColonListIntoArray(value);
+        else if ( strncmp(keyEqualsValue, "DYLD_FALLBACK_FRAMEWORK_PATH", 28) == 0 ) {
+            setString(_frameworkPathFallbacks, &keyEqualsValue[29]);
         }
-        else if ( strcmp(key, "DYLD_FALLBACK_LIBRARY_PATH") == 0 ) {
-            _dylibPathFallbacks = parseColonListIntoArray(value);
+        else if ( strncmp(keyEqualsValue, "DYLD_FALLBACK_LIBRARY_PATH", 26) == 0 ) {
+            setString(_dylibPathFallbacks, &keyEqualsValue[27]);
         }
-        else if ( strcmp(key, "DYLD_INSERT_LIBRARIES") == 0 ) {
-            _insertedDylibs = parseColonListIntoArray(value);
+        else if ( strncmp(keyEqualsValue, "DYLD_INSERT_LIBRARIES", 21) == 0 ) {
+            setString(_insertedDylibs, &keyEqualsValue[22]);
         }
-        else if ( strcmp(key, "DYLD_IMAGE_SUFFIX") == 0 ) {
-            _imageSuffix = value;
+        else if ( strncmp(keyEqualsValue, "DYLD_IMAGE_SUFFIX", 17) == 0 ) {
+            setString(_imageSuffix, &keyEqualsValue[18]);
         }
-        else if ( strcmp(key, "DYLD_ROOT_PATH") == 0 ) {
-            _rootPath = value;
+        else if ( strncmp(keyEqualsValue, "DYLD_ROOT_PATH", 14) == 0 ) {
+            setString(_rootPath, &keyEqualsValue[15]);
         }
     }
 }
 
-void PathOverrides::forEachInColonList(const char* list, void (^handler)(const char* path))
+void PathOverrides::forEachInColonList(const char* list, void (^handler)(const char* path, bool& stop))
 {
     char buffer[strlen(list)+1];
     const char* t = list;
+    bool stop = false;
     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);
+        handler(buffer, stop);
+        if ( stop )
+            return;
         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);
+    handler(t, stop);
 }
 
 void PathOverrides::forEachDylibFallback(Platform platform, void (^handler)(const char* fallbackDir, bool& stop)) const
 {
-    bool stop = false;
+    __block bool stop = false;
     if ( _dylibPathFallbacks != nullptr ) {
-        for (const char** fp=_dylibPathFallbacks; *fp != nullptr; ++fp) {
-            handler(*fp, stop);
-            if ( stop )
-                return;
-        }
+        forEachInColonList(_dylibPathFallbacks, ^(const char* pth, bool& innerStop) {
+            handler(pth, innerStop);
+            if ( innerStop )
+                stop = true;
+        });
     }
     else {
         switch ( platform ) {
             case Platform::macOS:
-                // "$HOME/lib"
-                handler("/usr/local/lib", stop);  // FIXME: not for restricted processes
-                if ( !stop )
-                    handler("/usr/lib", stop);
+                switch ( _fallbackPathMode ) {
+                    case FallbackPathMode::classic:
+                        // "$HOME/lib"
+                        handler("/usr/local/lib", stop);
+                        if ( stop )
+                            break;
+                        // fall thru
+                    case FallbackPathMode::restricted:
+                        handler("/usr/lib", stop);
+                        break;
+                    case FallbackPathMode::none:
+                        break;
+                }
                 break;
             case Platform::iOS:
             case Platform::watchOS:
             case Platform::tvOS:
             case Platform::bridgeOS:
             case Platform::unknown:
-                handler("/usr/local/lib", stop);
-                if ( !stop )
+                if ( _fallbackPathMode != FallbackPathMode::none ) {
+                    handler("/usr/local/lib", stop);
+                    if ( stop )
+                        break;
+                }
+                // fall into /usr/lib case
+            case Platform::iOSMac:
+            case Platform::iOS_simulator:
+            case Platform::watchOS_simulator:
+            case Platform::tvOS_simulator:
+                if ( _fallbackPathMode != FallbackPathMode::none )
                     handler("/usr/lib", stop);
                 break;
         }
@@ -264,43 +264,100 @@ void PathOverrides::forEachDylibFallback(Platform platform, void (^handler)(cons
 
 void PathOverrides::forEachFrameworkFallback(Platform platform, void (^handler)(const char* fallbackDir, bool& stop)) const
 {
-    bool stop = false;
+    __block bool stop = false;
     if ( _frameworkPathFallbacks != nullptr ) {
-        for (const char** fp=_frameworkPathFallbacks; *fp != nullptr; ++fp) {
-            handler(*fp, stop);
-            if ( stop )
-                return;
-        }
+        forEachInColonList(_frameworkPathFallbacks, ^(const char* pth, bool& innerStop) {
+            handler(pth, innerStop);
+            if ( innerStop )
+                stop = true;
+        });
     }
     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);
+                switch ( _fallbackPathMode ) {
+                    case FallbackPathMode::classic:
+                        // "$HOME/Library/Frameworks"
+                        handler("/Library/Frameworks", stop);
+                        if ( stop )
+                            break;
+                        // "/Network/Library/Frameworks"
+                        // fall thru
+                    case FallbackPathMode::restricted:
+                        handler("/System/Library/Frameworks", stop);
+                        break;
+                    case FallbackPathMode::none:
+                        break;
+                }
                 break;
             case Platform::iOS:
             case Platform::watchOS:
             case Platform::tvOS:
             case Platform::bridgeOS:
+            case Platform::iOSMac:
+            case Platform::iOS_simulator:
+            case Platform::watchOS_simulator:
+            case Platform::tvOS_simulator:
             case Platform::unknown:
-                handler("/System/Library/Frameworks", stop);
+                if ( _fallbackPathMode != FallbackPathMode::none )
+                    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
+
+//
+// copy path and add suffix to result
+//
+//  /path/foo.dylib      _debug   =>   /path/foo_debug.dylib
+//  foo.dylib            _debug   =>   foo_debug.dylib
+//  foo                  _debug   =>   foo_debug
+//  /path/bar            _debug   =>   /path/bar_debug
+//  /path/bar.A.dylib    _debug   =>   /path/bar.A_debug.dylib
+//
+void PathOverrides::addSuffix(const char* path, const char* suffix, char* result) const
+{
+    strcpy(result, path);
+
+    // find last slash
+    char* start = strrchr(result, '/');
+    if ( start != NULL )
+        start++;
+    else
+        start = result;
+
+    // find last dot after last slash
+    char* dot = strrchr(start, '.');
+    if ( dot != NULL ) {
+        strcpy(dot, suffix);
+        strcat(&dot[strlen(suffix)], &path[dot-result]);
+    }
+    else {
+        strcat(result, suffix);
+    }
+}
+
+void PathOverrides::forEachImageSuffix(const char* path, bool isFallbackPath, bool& stop, void (^handler)(const char* possiblePath, bool isFallbackPath, bool& stop)) const
+{
+    if ( _imageSuffix == nullptr ) {
+        handler(path, isFallbackPath, stop);
+    }
+    else {
+        forEachInColonList(_imageSuffix, ^(const char* suffix, bool& innerStop) {
+            char npath[strlen(path)+strlen(suffix)+8];
+            addSuffix(path, suffix, npath);
+            handler(npath, isFallbackPath, innerStop);
+            if ( innerStop )
+                stop = true;
+        });
+        if ( !stop )
+            handler(path, isFallbackPath, stop);
+    }
+}
+
+void PathOverrides::forEachPathVariant(const char* initialPath, void (^handler)(const char* possiblePath, bool isFallbackPath, bool& stop), Platform platform) const
 {
-#if DYLD_IN_PROCESS
-    Platform platform = MachOParser::currentPlatform();
-#endif
     __block bool stop = false;
 
     // check for overrides
@@ -309,15 +366,15 @@ void PathOverrides::forEachPathVariant(const char* initialPath,
         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);
+            forEachInColonList(_frameworkPathOverrides, ^(const char* frDir, bool &innerStop) {
+                char npath[strlen(frDir)+frameworkPartialPathLen+8];
+                strcpy(npath, frDir);
                 strcat(npath, "/");
                 strcat(npath, frameworkPartialPath);
-                handler(npath, stop);
-                if ( stop )
-                    return;
-            }
+                forEachImageSuffix(npath, false, innerStop, handler);
+                if ( innerStop )
+                    stop = true;
+            });
         }
     }
     else {
@@ -325,20 +382,22 @@ void PathOverrides::forEachPathVariant(const char* 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;
-            }
+            forEachInColonList(_dylibPathOverrides, ^(const char* libDir, bool &innerStop) {
+                char npath[strlen(libDir)+libraryLeafNameLen+8];
+                strcpy(npath, libDir);
+                strcat(npath, "/");
+                strcat(npath, libraryLeafName);
+                forEachImageSuffix(npath, false, innerStop, handler);
+                if ( innerStop )
+                    stop = true;
+            });
         }
     }
+    if ( stop )
+        return;
 
     // try original path
-    handler(initialPath, stop);
+    forEachImageSuffix(initialPath, false, stop, handler);
     if ( stop )
         return;
 
@@ -346,14 +405,15 @@ void PathOverrides::forEachPathVariant(const char* initialPath,
     if ( frameworkPartialPath != nullptr ) {
         const size_t frameworkPartialPathLen = strlen(frameworkPartialPath);
         // look at each DYLD_FALLBACK_FRAMEWORK_PATH directory
+        bool usesDefaultFallbackPaths = (_frameworkPathFallbacks == nullptr);
         forEachFrameworkFallback(platform, ^(const char* dir, bool& innerStop) {
             char npath[strlen(dir)+frameworkPartialPathLen+8];
             strcpy(npath, dir);
             strcat(npath, "/");
             strcat(npath, frameworkPartialPath);
-            handler(npath, innerStop);
+            forEachImageSuffix(npath, usesDefaultFallbackPaths, innerStop, handler);
             if ( innerStop )
-                stop = innerStop;
+                stop = true;
         });
 
     }
@@ -361,14 +421,15 @@ void PathOverrides::forEachPathVariant(const char* initialPath,
         const char* libraryLeafName = getLibraryLeafName(initialPath);
         const size_t libraryLeafNameLen = strlen(libraryLeafName);
         // look at each DYLD_FALLBACK_LIBRARY_PATH directory
+        bool usesDefaultFallbackPaths = (_dylibPathFallbacks == nullptr);
         forEachDylibFallback(platform, ^(const char* dir, bool& innerStop) {
             char libpath[strlen(dir)+libraryLeafNameLen+8];
             strcpy(libpath, dir);
             strcat(libpath, "/");
             strcat(libpath, libraryLeafName);
-            handler(libpath, innerStop);
+            forEachImageSuffix(libpath, usesDefaultFallbackPaths, innerStop, handler);
             if ( innerStop )
-                stop = innerStop;
+                stop = true;
         });
     }
 }
@@ -428,6 +489,59 @@ const char* PathOverrides::getLibraryLeafName(const char* path)
         return path;
 }
 
+
+
+////////////////////////////  PathPool ////////////////////////////////////////
+
+
+PathPool* PathPool::allocate()
+{
+    vm_address_t addr;
+    ::vm_allocate(mach_task_self(), &addr, kAllocationSize, VM_FLAGS_ANYWHERE);
+    PathPool* p = (PathPool*)addr;
+    p->_next      = nullptr;
+    p->_current   = &(p->_buffer[0]);
+    p->_bytesFree = kAllocationSize - sizeof(PathPool);
+    return p;
+}
+
+void PathPool::deallocate(PathPool* pool) {
+    do {
+        PathPool* next = pool->_next;
+        ::vm_deallocate(mach_task_self(), (vm_address_t)pool, kAllocationSize);
+        pool = next;
+    } while (pool);
+}
+
+const char* PathPool::add(const char* path)
+{
+    size_t len = strlen(path) + 1;
+    if ( len < _bytesFree ) {
+        char* result = _current;
+        strcpy(_current, path);
+        _current += len;
+        _bytesFree -= len;
+        return result;
+    }
+    if ( _next == nullptr )
+        _next = allocate();
+    return _next->add(path);
+}
+
+void PathPool::forEachPath(void (^handler)(const char* path))
+{
+    for (const char* s = _buffer; s < _current; ++s) {
+        handler(s);
+        s += strlen(s);
+    }
+
+    if ( _next != nullptr )
+        _next->forEachPath(handler);
+}
+
+
+
+} // namespace closure
 } // namespace dyld3
 
 
index 4ae740726097ee7fa98823ead6722cbadc7103d0..8b0fbff4ab2386f8858a5de0407a5af942e0bb3d 100644 (file)
 
 #include <stdint.h>
 
-#if !DYLD_IN_PROCESS
-#include <vector>
-#include <string>
-#endif
-
 #include "Logging.h"
-#include "MachOParser.h"
+#include "MachOFile.h"
 
 
 namespace dyld3 {
+namespace closure {
+
+
+class VIS_HIDDEN PathPool
+{
+public:
+    static PathPool*    allocate();
+    static void         deallocate(PathPool* pool);
+    const char*         add(const char* path);
+    void                forEachPath(void (^handler)(const char* path));
+
+private:
+    enum { kAllocationSize = 32*1024 };
+
+    PathPool*       _next;
+    char*           _current;
+    size_t          _bytesFree;
+    char            _buffer[];
+};
+
 
 class VIS_HIDDEN PathOverrides
 {
@@ -46,23 +61,22 @@ public:
     // libdyld is never unloaded
                                     ~PathOverrides();
 #endif
+    enum class FallbackPathMode { classic, restricted, none };
 
-#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
+    void                            setFallbackPathHandling(FallbackPathMode mode);
+    void                            setEnvVars(const char* envp[], const dyld3::MachOFile* mainExe, const char* mainExePath);
+    void                            setMainExecutable(const dyld3::MachOFile* mainExe, const char* mainExePath);
+    void                            forEachPathVariant(const char* requestedPath, void (^handler)(const char* possiblePath, bool isFallbackPath, bool& stop),
+                                                       Platform plat=MachOFile::currentPlatform()) const;
 
     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                            setString(const char*& var, const char* value);
+    const char*                     addString(const char* str);
+    static void                     forEachInColonList(const char* list, void (^callback)(const char* path, bool& stop));
     void                            addEnvVar(const char* keyEqualsValue);
     const char*                     getFrameworkPartialPath(const char* path) const;
     static const char*              getLibraryLeafName(const char* path);
@@ -70,14 +84,18 @@ private:
     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;
+    void                            forEachImageSuffix(const char* path, bool isFallbackPath, bool& stop, void (^handler)(const char* possiblePath, bool isFallbackPath, bool& stop)) const;
+    void                            addSuffix(const char* path, const char* suffix, char* result) const;
+
+    PathPool*                       _pathPool                   = nullptr;
+    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
+    FallbackPathMode                _fallbackPathMode           = FallbackPathMode::classic;
 };
 
 #if BUILDING_LIBDYLD
@@ -85,6 +103,7 @@ extern PathOverrides   gPathOverrides;
 #endif
 
 
+} // namespace closure
 } // namespace dyld3
 
 #endif // __DYLD_PATH_OVERRIDES_H__
index 993ed54cbde78b831ea7f73f6565bb6aa0281b2b..c54c07fe0852b7ddcb823d6e5fc3dbda23ffab3d 100644 (file)
@@ -45,8 +45,6 @@
 
 #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
@@ -102,13 +100,18 @@ struct CacheInfo
     #define ARCH_NAME            "arm64e"
     #define ARCH_CACHE_MAGIC     "dyld_v1  arm64e"
 #elif __arm64__
-    #define ARCH_NAME            "arm64"
-    #define ARCH_CACHE_MAGIC     "dyld_v1   arm64"
+    #if __LP64__
+        #define ARCH_NAME            "arm64"
+        #define ARCH_CACHE_MAGIC     "dyld_v1   arm64"
+    #else
+        #define ARCH_NAME            "arm64_32"
+        #define ARCH_CACHE_MAGIC     "dyld_v1arm64_32"
+    #endif
 #endif
 
 
 
-static void rebaseChain(uint8_t* pageContent, uint16_t startOffset, uintptr_t slideAmount, const dyld_cache_slide_info2* slideInfo)
+static void rebaseChainV2(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;
@@ -132,6 +135,38 @@ static void rebaseChain(uint8_t* pageContent, uint16_t startOffset, uintptr_t sl
     }
 }
 
+#if !__LP64__
+static void rebaseChainV4(uint8_t* pageContent, uint16_t startOffset, uintptr_t slideAmount, const dyld_cache_slide_info4* 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 & 0xFFFF8000) == 0 ) {
+           // small positive non-pointer, use as-is
+        }
+        else if ( (value & 0x3FFF8000) == 0x3FFF8000 ) {
+           // small negative non-pointer
+           value |= 0xC0000000;
+        }
+        else {
+            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;
+    }
+}
+#endif
 
 static void getCachePath(const SharedCacheOptions& options, size_t pathBufferSize, char pathBuffer[])
 {
@@ -168,10 +203,11 @@ static void getCachePath(const SharedCacheOptions& options, size_t pathBufferSiz
     struct stat enableStatBuf;
     struct stat devCacheStatBuf;
     struct stat optCacheStatBuf;
+    bool developmentDevice = dyld3::internalInstall();
     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 )
+    if ( developmentDevice && ((enableFileExists && (enableStatBuf.st_size < ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE) && devCacheExists) || !optCacheExists) )
         strlcat(pathBuffer, DYLD_SHARED_CACHE_DEVELOPMENT_EXT, pathBufferSize);
 #endif
 
@@ -205,7 +241,7 @@ static bool validPlatform(const SharedCacheOptions& options, const DyldSharedCac
     if ( cache->header.mappingOffset < 0xE0 )
         return true;
 
-    if ( cache->header.platform != (uint32_t)MachOParser::currentPlatform() )
+    if ( cache->header.platform != (uint32_t)MachOFile::currentPlatform() )
         return false;
 
 #if TARGET_IPHONE_SIMULATOR
@@ -240,6 +276,7 @@ static bool preflightCacheFile(const SharedCacheOptions& options, SharedCacheLoa
         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";
@@ -266,7 +303,7 @@ static bool preflightCacheFile(const SharedCacheOptions& options, SharedCacheLoa
         ::close(fd);
         return false;
     }
-    if ( (cache->header.mappingCount != 3) || (cache->header.mappingOffset > 0x120) ) {
+    if ( (cache->header.mappingCount != 3) || (cache->header.mappingOffset > 0x138) ) {
         results->errorMessage = "shared cache file mappings are invalid";
         ::close(fd);
         return false;
@@ -327,7 +364,7 @@ static bool preflightCacheFile(const SharedCacheOptions& options, SharedCacheLoa
         return false;
     }
     if ( memcmp(mappedData, firstPage, sizeof(firstPage)) != 0 ) {
-        results->errorMessage = "first page of shared cache not mmap()able";
+        results->errorMessage = "first page of mmap()ed shared cache not valid";
         ::close(fd);
         return false;
     }
@@ -363,7 +400,120 @@ static bool preflightCacheFile(const SharedCacheOptions& options, SharedCacheLoa
     return true;
 }
 
+
 #if !TARGET_IPHONE_SIMULATOR
+
+// update all __DATA pages with slide info
+static bool rebaseDataPages(bool isVerbose, CacheInfo& info, SharedCacheLoadInfo* results)
+{
+    uint64_t dataPagesStart = info.mappings[1].sfm_address;
+    const dyld_cache_slide_info* slideInfo = nullptr;
+    if ( info.slideInfoSize != 0 ) {
+        slideInfo = (dyld_cache_slide_info*)(info.slideInfoAddressUnslid + results->slide);
+    }
+    const dyld_cache_slide_info* slideInfoHeader = (dyld_cache_slide_info*)slideInfo;
+    if ( slideInfoHeader != nullptr ) {
+        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);
+            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);
+                        rebaseChainV2(page, pageStartOffset, results->slide, slideHeader);
+                        done = (pInfo & DYLD_CACHE_SLIDE_PAGE_ATTR_END);
+                        ++chainIndex;
+                    }
+                }
+                else {
+                    uint32_t pageOffset = pageEntry * 4;
+                    //dyld::log("     start pageOffset=0x%03X\n", pageOffset);
+                    rebaseChainV2(page, pageOffset, results->slide, slideHeader);
+                }
+            }
+        }
+#if __LP64__
+        else if ( slideInfoHeader->version == 3 ) {
+             const dyld_cache_slide_info3* slideHeader = (dyld_cache_slide_info3*)slideInfo;
+             const uint32_t                pageSize    = slideHeader->page_size;
+             for (int i=0; i < slideHeader->page_starts_count; ++i) {
+                 uint8_t* page = (uint8_t*)(dataPagesStart + (pageSize*i));
+                 uint64_t delta = slideHeader->page_starts[i];
+                 if ( delta == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE )
+                     continue;
+                 delta = delta/sizeof(uint64_t); // initial offset is byte based
+                 dyld_cache_slide_pointer3* loc = (dyld_cache_slide_pointer3*)page;
+                 do {
+                     loc += delta;
+                     delta = loc->plain.offsetToNextPointer;
+                     if ( loc->auth.authenticated ) {
+#if __has_feature(ptrauth_calls)
+                        uint64_t target = info.sharedRegionStart + loc->auth.offsetFromSharedCacheBase + results->slide;
+                        MachOLoaded::ChainedFixupPointerOnDisk ptr;
+                        ptr.raw = *((uint64_t*)loc);
+                        loc->raw = ptr.signPointer(loc, target);
+#else
+                        results->errorMessage = "invalid pointer kind in cache file";
+                        return false;
+#endif
+                     }
+                     else {
+                         loc->raw = MachOLoaded::ChainedFixupPointerOnDisk::signExtend51(loc->plain.pointerValue) + results->slide;
+                     }
+                } while (delta != 0);
+            }
+        }
+#else
+        else if ( slideInfoHeader->version == 4 ) {
+            const dyld_cache_slide_info4* slideHeader = (dyld_cache_slide_info4*)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);
+            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_SLIDE4_PAGE_NO_REBASE )
+                    continue;
+                if ( pageEntry & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA ) {
+                    uint16_t chainIndex = (pageEntry & DYLD_CACHE_SLIDE4_PAGE_INDEX);
+                    bool done = false;
+                    while ( !done ) {
+                        uint16_t pInfo = page_extras[chainIndex];
+                        uint16_t pageStartOffset = (pInfo & DYLD_CACHE_SLIDE4_PAGE_INDEX)*4;
+                        //dyld::log("     chain[%d] pageOffset=0x%03X\n", chainIndex, pageStartOffset);
+                        rebaseChainV4(page, pageStartOffset, results->slide, slideHeader);
+                        done = (pInfo & DYLD_CACHE_SLIDE4_PAGE_EXTRA_END);
+                        ++chainIndex;
+                    }
+                }
+                else {
+                    uint32_t pageOffset = pageEntry * 4;
+                    //dyld::log("     start pageOffset=0x%03X\n", pageOffset);
+                    rebaseChainV4(page, pageOffset, results->slide, slideHeader);
+                }
+            }
+        }
+#endif // LP64
+        else {
+            results->errorMessage = "invalid slide info in cache file";
+            return false;
+        }
+    }
+    return true;
+}
+
 static bool reuseExistingCache(const SharedCacheOptions& options, SharedCacheLoadInfo* results)
 {
     uint64_t cacheBaseAddress;
@@ -377,10 +527,6 @@ static bool reuseExistingCache(const SharedCacheOptions& options, SharedCacheLoa
             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 ) {
@@ -413,7 +559,7 @@ static long pickCacheASLR(CacheInfo& info)
 #endif
 
     // <rdar://problem/32031197> respect -disable_aslr boot-arg
-    if ( dyld3::loader::bootArgsContains("-disable_aslr") )
+    if ( dyld3::bootArgsContains("-disable_aslr") )
         slide = 0;
 
     // update mappings
@@ -435,10 +581,6 @@ static bool mapCacheSystemWide(const SharedCacheOptions& options, SharedCacheLoa
         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);
@@ -450,7 +592,8 @@ static bool mapCacheSystemWide(const SharedCacheOptions& options, SharedCacheLoa
         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";
+        if ( results->errorMessage == nullptr )
+            results->errorMessage = "syscall to map cache into shared region failed";
         return false;
     }
 
@@ -460,7 +603,7 @@ static bool mapCacheSystemWide(const SharedCacheOptions& options, SharedCacheLoa
     }
     return true;
 }
-#endif
+#endif // TARGET_IPHONE_SIMULATOR
 
 static bool mapCachePrivate(const SharedCacheOptions& options, SharedCacheLoadInfo* results)
 {
@@ -471,18 +614,12 @@ static bool mapCachePrivate(const SharedCacheOptions& options, SharedCacheLoadIn
 
     // 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);
@@ -506,55 +643,22 @@ static bool mapCachePrivate(const SharedCacheOptions& options, SharedCacheLoadIn
             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 TARGET_IPHONE_SIMULATOR // simulator caches do not support sliding
+    return true;
+#else
+    bool success = rebaseDataPages(options.verbose, info, results);
 
     if ( options.verbose ) {
         dyld::log("mapped dyld cache file private to process (%s):\n", results->path);
         verboseSharedCacheMappings(info.mappings);
     }
-    return true;
+    return success;
+#endif
 }
 
 
@@ -563,7 +667,6 @@ bool loadDyldCache(const SharedCacheOptions& options, SharedCacheLoadInfo* resul
 {
     results->loadAddress        = 0;
     results->slide              = 0;
-    results->cachedDylibsGroup  = nullptr;
     results->errorMessage       = nullptr;
 
 #if TARGET_IPHONE_SIMULATOR
@@ -576,11 +679,14 @@ bool loadDyldCache(const SharedCacheOptions& options, SharedCacheLoadInfo* resul
     }
     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);
+        bool hasError = false;
+        if ( reuseExistingCache(options, results) ) {
+            hasError = (results->errorMessage != nullptr);
+        } else {
+            // slow path: this is first process to load cache
+            hasError = mapCacheSystemWide(options, results);
+        }
+        return hasError;
     }
 #endif
 }
@@ -591,56 +697,84 @@ bool findInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dyl
     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) ) {
+    if ( loadInfo.loadAddress->header.formatVersion != dyld3::closure::kFormatVersion ) {
+        // support for older cache with a different Image* format
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+        uint64_t hash = 0;
+        for (const char* s=dylibPathToFind; *s != '\0'; ++s)
+                hash += hash*4 + *s;
+#endif
         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) {
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+            // on iOS, inode is used to hold hash of path
+            if ( (p->modTime == 0) && (p->inode != hash) )
+                continue;
+#endif
             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;
+                results->image        = 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);
+    const dyld3::closure::ImageArray* images = loadInfo.loadAddress->cachedDylibsImageArray();
+    results->image = nullptr;
+    uint32_t imageIndex;
+    if ( loadInfo.loadAddress->hasImagePath(dylibPathToFind, imageIndex) ) {
+        results->image = images->imageForNum(imageIndex+1);
+    }
+ #if __MAC_OS_X_VERSION_MIN_REQUIRED
+    else {
+        // <rdar://problem/32740215> handle symlink to cached dylib
+        if ( loadInfo.loadAddress->header.dylibsExpectedOnDisk ) {
+            struct stat statBuf;
+            if ( dyld::my_stat(dylibPathToFind, &statBuf) == 0 ) {
+                // on macOS we store the inode and mtime of each dylib in the cache in the dyld_cache_image_info array
+                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) {
+                    if ( (p->inode == statBuf.st_ino) && (p->modTime == statBuf.st_mtime) ) {
+                        imageIndex = (uint32_t)(p - start);
+                        results->image = images->imageForNum(imageIndex+1);
+                        break;
+                    }
+                }
+            }
+        }
+        else {
+            char resolvedPath[PATH_MAX];
+            if ( realpath(dylibPathToFind, resolvedPath) != nullptr ) {
+                if ( loadInfo.loadAddress->hasImagePath(resolvedPath, imageIndex) ) {
+                    results->image = images->imageForNum(imageIndex+1);
+                }
+            }
+        }
     }
 #endif
-    if ( imageData == nullptr )
+    if ( results->image == nullptr )
         return false;
 
-    launch_cache::Image image(imageData);
-    results->mhInCache    = (const mach_header*)((uintptr_t)loadInfo.loadAddress + image.cacheOffset());
-    results->pathInCache  = image.path();
+    results->mhInCache    = (const mach_header*)((uintptr_t)loadInfo.loadAddress + results->image->cacheOffset());
+    results->pathInCache  = results->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) )
+    if ( (loadInfo.loadAddress == nullptr) || (loadInfo.loadAddress->header.formatVersion != closure::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);
+    uint32_t imageIndex;
+    return loadInfo.loadAddress->hasImagePath(dylibPathToFind, imageIndex);
 }
 
 
index dbd459577cee8cc04e050a053a3db92d41fa1754..402ce5417eb50609cfbbf721770da7a4cb349f48 100644 (file)
@@ -41,23 +41,22 @@ struct SharedCacheOptions {
 };
 
 struct SharedCacheLoadInfo {
-    const DyldSharedCache*                          loadAddress;
-    long                                            slide;
-    const launch_cache::binary_format::ImageGroup*  cachedDylibsGroup;
-    const char*                                     errorMessage;
-    char                                            path[256];
+    const DyldSharedCache*       loadAddress;
+    long                         slide;
+    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;
+    const mach_header*          mhInCache;
+    const char*                 pathInCache;
+    long                        slideInCache;
+    const closure::Image*       image;
 };
 
-
 bool findInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dylibPathToFind, SharedCacheFindDylibResults* results);
 
 bool pathIsInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dylibPathToFind);
diff --git a/dyld3/StartGlue.h b/dyld3/StartGlue.h
new file mode 100644 (file)
index 0000000..6ef4a27
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2013 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef __START_GLUE_H__
+#define __START_GLUE_H__
+
+// Implemented in start_glue.s
+// Declare 'start' as a character, so that we can index into it.
+// Avoid arithmetic on function pointers.
+extern "C" char start;
+
+
+// <rdar://problem/12792039> need 'start' to be one atom, but entry is in interior
+
+#if __x86_64__ || __i386__ 
+       #define address_of_start (void*)((uintptr_t)&start + 1)
+#elif __arm64__
+       #define address_of_start (void*)((uintptr_t)&start + 4)
+#elif __arm__
+       #define address_of_start (void*)((uintptr_t)&start + 2)
+#endif
+
+
+
+#endif // __START_GLUE_H__
diff --git a/dyld3/SupportedArchs.h b/dyld3/SupportedArchs.h
new file mode 100644 (file)
index 0000000..425c23f
--- /dev/null
@@ -0,0 +1,30 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2015 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef _DYLD_SUPPORTED_ARCHS_H_
+#define _DYLD_SUPPORTED_ARCHS_H_
+
+
+
+#endif // _DYLD_SUPPORTED_ARCHS_H_
index ff153114865e0110dcad0eb157e17c3ee26a8571..430e3aaadc9db1e7e021c9661c482acabba297fc 100644 (file)
@@ -69,38 +69,71 @@ void kdebug_trace_dyld_image(const uint32_t code,
 #endif /* __LP64__ */
 }
 
+// FIXME
+// We get distinct copies of this in libdyld and dyld. Eventually we can fix it,
+// for now we will just offset the values.
+
+#if BUILDING_DYLD
+static std::atomic<uint64_t> trace_pair_id(0);
+#else
+static std::atomic<uint64_t> trace_pair_id(1LL<<63);
+#endif
+
 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);
-    }
+bool kdebug_trace_dyld_enabled(uint32_t code) {
+    return kdebug_is_enabled(code);
 }
 
-static std::atomic<uint64_t> trace_pair_id(0);
+VIS_HIDDEN
+void kdebug_trace_dyld_marker(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3, kt_arg data4) {
+    if (kdebug_is_enabled(code)) {
+        data1.prepare(code);
+        data2.prepare(code);
+        data3.prepare(code);
+        data4.prepare(code);
+        kdebug_trace(code, data1.value(), data2.value(), data3.value(), data4.value());
+        data4.destroy(code);
+        data3.destroy(code);
+        data2.destroy(code);
+        data1.destroy(code);
+    }
+}
 
 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();
+uint64_t kdebug_trace_dyld_duration_start(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3) {
+    uint64_t result = 0;
+    if (kdebug_is_enabled(code)) {
+        result = ++trace_pair_id;
+        data1.prepare(code);
+        data2.prepare(code);
+        data3.prepare(code);
+        kdebug_trace(code | DBG_FUNC_START, result, data1.value(), data2.value(), data3.value());
+        data3.destroy(code);
+        data2.destroy(code);
+        data1.destroy(code);
     }
+    return result;
 }
 
-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);
+VIS_HIDDEN
+void kdebug_trace_dyld_duration_end(uint64_t trace_id, uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3) {
+    if (trace_id != 0 && kdebug_is_enabled(code)) {
+        data1.prepare(code);
+        data2.prepare(code);
+        data3.prepare(code);
+        kdebug_trace(code | DBG_FUNC_END, trace_id, data1.value(), data2.value(), data3.value());
+        data3.destroy(code);
+        data2.destroy(code);
+        data1.destroy(code);
     }
 }
+
+void ScopedTimer::startTimer() {
+    current_trace_id = kdebug_trace_dyld_duration_start(code, data1, data2, data3);
+}
+
+void ScopedTimer::endTimer() {
+    kdebug_trace_dyld_duration_end(current_trace_id, code, data4, data5, data6);
+}
+
 };
index b127aa99d99813bbc15bc63b1e490137f0530950..cf315ca5771df41929d35bf10e50b474766dd946 100644 (file)
 #include <mach-o/loader.h>
 #include <System/sys/kdebug.h>
 
-#ifndef DBG_DYLD_SIGNPOST
-    #define DBG_DYLD_SIGNPOST (6)
-#endif
+#define DBG_DYLD_INTERNAL_SUBCLASS              (7)
+#define DBG_DYLD_API_SUBCLASS                   (8)
+#define DBG_DYLD_DEBUGGING_SUBCLASS             (9)
+
+#define DBG_DYLD_TIMING_STATIC_INITIALIZER      (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 0))
+#define DBG_DYLD_TIMING_LAUNCH_EXECUTABLE       (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 1))
+#define DBG_DYLD_TIMING_MAP_IMAGE               (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 2))
+#define DBG_DYLD_TIMING_APPLY_FIXUPS            (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 3))
+#define DBG_DYLD_TIMING_ATTACH_CODESIGNATURE    (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 4))
+#define DBG_DYLD_TIMING_BUILD_CLOSURE           (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 5))
+#define DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE      (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 6))
+#define DBG_DYLD_TIMING_FUNC_FOR_REMOVE_IMAGE   (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 7))
+#define DBG_DYLD_TIMING_OBJC_INIT               (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 8))
+#define DBG_DYLD_TIMING_OBJC_MAP                (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 9))
+#define DBG_DYLD_TIMING_APPLY_INTERPOSING       (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 10))
+#define DBG_DYLD_GDB_IMAGE_NOTIFIER             (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 11))
+#define DBG_DYLD_REMOTE_IMAGE_NOTIFIER          (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 12))
+
+#define DBG_DYLD_TIMING_DLOPEN                  (KDBG_CODE(DBG_DYLD, DBG_DYLD_API_SUBCLASS, 0))
+#define DBG_DYLD_TIMING_DLOPEN_PREFLIGHT        (KDBG_CODE(DBG_DYLD, DBG_DYLD_API_SUBCLASS, 1))
+#define DBG_DYLD_TIMING_DLCLOSE                 (KDBG_CODE(DBG_DYLD, DBG_DYLD_API_SUBCLASS, 2))
+#define DBG_DYLD_TIMING_DLSYM                   (KDBG_CODE(DBG_DYLD, DBG_DYLD_API_SUBCLASS, 3))
+#define DBG_DYLD_TIMING_DLADDR                  (KDBG_CODE(DBG_DYLD, DBG_DYLD_API_SUBCLASS, 4))
+
+#define DBG_DYLD_DEBUGGING_VM_REMAP             (KDBG_CODE(DBG_DYLD, DBG_DYLD_DEBUGGING_SUBCLASS, 0))
+#define DBG_DYLD_DEBUGGING_VM_UNMAP             (KDBG_CODE(DBG_DYLD, DBG_DYLD_DEBUGGING_SUBCLASS, 1))
+#define DBG_DYLD_DEBUGGING_MAP_LOOP             (KDBG_CODE(DBG_DYLD, DBG_DYLD_DEBUGGING_SUBCLASS, 2))
+#define DBG_DYLD_DEBUGGING_MARK                 (KDBG_CODE(DBG_DYLD, DBG_DYLD_DEBUGGING_SUBCLASS, 3))
 
-#ifndef DBG_DYLD_TIMING
-    #define DBG_DYLD_TIMING (7)
-#endif
 
-#ifndef DBG_DYLD_PRINT
-    #define DBG_DYLD_PRINT (8)
-#endif
+#define VIS_HIDDEN __attribute__((visibility("hidden")))
 
-#ifndef DBG_DYLD_SIGNPOST_START_DYLD
-    #define DBG_DYLD_SIGNPOST_START_DYLD (0)
-#endif
+namespace dyld3 {
 
-#ifndef DBG_DYLD_SIGNPOST_START_MAIN
-    #define DBG_DYLD_SIGNPOST_START_MAIN (1)
+struct VIS_HIDDEN kt_arg {
+    kt_arg(int value) : _value(value), _str(nullptr) {}
+    kt_arg(uint64_t value) : _value(value), _str(nullptr) {}
+    kt_arg(const char *value) : _value(0), _str(value) {}
+    kt_arg(void *value) : _value((uint64_t)value), _str(nullptr) {}
+    uint64_t value() const { return _value; }
+private:
+    void prepare(uint32_t code) {
+        if (_str) {
+            _value = kdebug_trace_string(code, 0, _str);
+            if (_value == (uint64_t)-1) _value = 0;
+        }
+    }
+    void destroy(uint32_t code) {
+        if (_str && _value) {
+            kdebug_trace_string(code, _value, nullptr);
+        }
+    }
+    friend class ScopedTimer;
+    friend uint64_t kdebug_trace_dyld_duration_start(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3);
+    friend void kdebug_trace_dyld_duration_end(uint64_t pair_id, uint32_t code, kt_arg data4, kt_arg data5, kt_arg data6);
+    friend void kdebug_trace_dyld_marker(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3, kt_arg data4);
+    uint64_t _value;
+    const char* _str;
+};
+
+class VIS_HIDDEN ScopedTimer {
+public:
+    ScopedTimer(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3)
+        : code(code), data1(data1), data2(data2), data3(data3), data4(0), data5(0), data6(0) {
+#if BUILDING_LIBDYLD || BUILDING_DYLD
+        startTimer();
 #endif
+    }
 
-#ifndef DBG_DYLD_SIGNPOST_START_MAIN_DYLD2
-    #define DBG_DYLD_SIGNPOST_START_MAIN_DYLD2 (2)
+    ~ScopedTimer() {
+#if BUILDING_LIBDYLD || BUILDING_DYLD
+        endTimer();
 #endif
-
-#ifndef DBG_DYLD_TIMING_STATIC_INITIALIZER
-    #define DBG_DYLD_TIMING_STATIC_INITIALIZER (0)
+    }
+
+    void setData4(kt_arg data) { data4 = data; }
+    void setData5(kt_arg data) { data5 = data; }
+    void setData6(kt_arg data) { data6 = data; }
+private:
+#if BUILDING_LIBDYLD || BUILDING_DYLD
+    void startTimer();
+    void endTimer();
 #endif
 
-#ifndef DBG_DYLD_PRINT_GENERIC
-    #define DBG_DYLD_PRINT_GENERIC (0)
-#endif
-
-
-#define VIS_HIDDEN __attribute__((visibility("hidden")))
-
-namespace dyld3 {
+    uint32_t code;
+    kt_arg data1;
+    kt_arg data2;
+    kt_arg data3;
+    kt_arg data4;
+    kt_arg data5;
+    kt_arg data6;
+    uint64_t current_trace_id = 0;
+};
 
 VIS_HIDDEN
 void kdebug_trace_dyld_image(const uint32_t code,
@@ -79,13 +136,16 @@ void kdebug_trace_dyld_image(const uint32_t code,
                        const mach_header* load_addr);
 
 VIS_HIDDEN
-void kdebug_trace_dyld_signpost(const uint32_t code, uint64_t data1, uint64_t data2);
+bool kdebug_trace_dyld_enabled(uint32_t code);
+
+VIS_HIDDEN
+void kdebug_trace_dyld_marker(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3, kt_arg data4);
 
 VIS_HIDDEN
-void kdebug_trace_dyld_duration(const uint32_t code, uint64_t data1, uint64_t data2, void (^block)());
+uint64_t kdebug_trace_dyld_duration_start(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3);
 
 VIS_HIDDEN
-void kdebug_trace_print(const uint32_t code, const char *string);
-}
+void kdebug_trace_dyld_duration_end(uint64_t trace_id, uint32_t code, kt_arg data4, kt_arg data5, kt_arg data6);
 
+};
 #endif /* Tracing_h */
diff --git a/dyld3/closured/closured.cpp b/dyld3/closured/closured.cpp
deleted file mode 100644 (file)
index 1e239e8..0000000
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * 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
deleted file mode 100644 (file)
index 565c8bc..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-#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
deleted file mode 100644 (file)
index 5a352d6..0000000
+++ /dev/null
@@ -1,12 +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>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
deleted file mode 100644 (file)
index 6acc727..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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
deleted file mode 100644 (file)
index e19d134..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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
deleted file mode 100644 (file)
index bd89f9c..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-;;; 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
deleted file mode 100644 (file)
index 998a298..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-/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
deleted file mode 100644 (file)
index f7e654c..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-
-namespace dyld3 {
-
-struct ClosureBuffer { };
-
-ClosureBuffer closured_CreateImageGroup(const ClosureBuffer& input)
-{
-    return ClosureBuffer();
-}
-
-
-} // namespace dyld3
index 42cedaa1eea36caa18f83c523401afd087318024..630d01c30c46d5f44e07b3af1740611f679dd8ff 100644 (file)
 #include "dyld_priv.h"
 #include "libdyldEntryVector.h"
 #include "AllImages.h"
+#include "Array.h"
+#include "Loading.h"
 #include "Logging.h"
 #include "PathOverrides.h"
-#include "LaunchCacheFormat.h"
-#include "start_glue.h"
-
-extern "C" void start();
+#include "StartGlue.h"
+#include "dyld_process_info_internal.h"
 
+extern "C" char start;
 
 VIS_HIDDEN const char** appleParams;
 
@@ -90,32 +91,38 @@ 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)
+static void entry_setNotifyMonitoringDyldMain(void (*notifyMonitoringDyldMain)()) {
+    setNotifyMonitoringDyldMain(notifyMonitoringDyldMain);
+}
+
+static void entry_setNotifyMonitoringDyld(void (*notifyMonitoringDyld)(bool unloading,unsigned imageCount,
+                                                                               const struct mach_header* loadAddresses[],
+                                                                               const char* imagePaths[])) {
+    setNotifyMonitoringDyld(notifyMonitoringDyld);
+}
+
+static void entry_setInitialImageList(const closure::LaunchClosure* closure,
+                                const DyldSharedCache* dyldCacheLoadAddress, const char* dyldCachePath,
+                                const Array<LoadedImage>& initialImages, const LoadedImage& libSystem)
 {
     gAllImages.init(closure, dyldCacheLoadAddress, dyldCachePath, initialImages);
-    gAllImages.applyInterposingToDyldCache(closure, initialImages);
+    gAllImages.applyInterposingToDyldCache(closure);
 
     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);
+    gAllImages.runLibSystemInitializer(libSystem);
 
     // now that malloc is available, parse DYLD_ env vars
-    gPathOverrides.setEnvVars((const char**)environ);
+    closure::gPathOverrides.setEnvVars((const char**)environ, gAllImages.mainExecutable(), gAllImages.mainExecutableImage()->path());
 }
 
 static void entry_runInitialzersBottomUp(const mach_header* mainExecutableImageLoadAddress)
 {
-    gAllImages.runInitialzersBottomUp(mainExecutableImageLoadAddress);
+    gAllImages.runStartupInitialzers();
     gAllImages.notifyMonitorMain();
 }
 
@@ -124,19 +131,25 @@ static void entry_setChildForkFunction(void (*func)() )
     sChildForkFunction = func;
 }
 
-typedef void (*StartFunc)();
+static void entry_setRestrictions(bool allowAtPaths, bool allowEnvPaths)
+{
+    gAllImages.setRestrictions(allowAtPaths, allowEnvPaths);
+}
 
 const LibDyldEntryVector entryVectorForDyld = {
     LibDyldEntryVector::kCurrentVectorVersion,
-    launch_cache::binary_format::kFormatVersion,
+    closure::kFormatVersion,
     &entry_setVars,
     &entry_setHaltFunction,
     &entry_setOldAllImageInfo,
     &entry_setInitialImageList,
     &entry_runInitialzersBottomUp,
-    (StartFunc)address_of_start,
+    (__typeof(LibDyldEntryVector::startFunc))address_of_start,
     &entry_setChildForkFunction,
     &entry_setLogFunction,
+    &entry_setRestrictions,
+    &entry_setNotifyMonitoringDyldMain,
+    &entry_setNotifyMonitoringDyld
 };
 
 VIS_HIDDEN void _dyld_fork_child()
index 3c61df215af2e173fa797d30d0522c1a107e5ccf..d7b832dfd6f06e5439a1c06bd5d4bb7e219323c2 100644 (file)
 
 #include <mach-o/loader.h>
 
-#include "LaunchCache.h"
 #include "Loading.h"
 
 struct dyld_all_image_infos;
+class DyldSharedCache;
 
 namespace dyld3 {
 
+
 struct LibDyldEntryVector
 {
-    enum { kCurrentVectorVersion = 4 };
+    enum { kCurrentVectorVersion = 6 };
 
     uint32_t    vectorVersion;              // should be kCurrentVectorVersion
-    uint32_t    binaryFormatVersion;        // should be launch_cache::binary_format::kFormatVersion
+    uint32_t    binaryFormatVersion;        // should be dyld3::closure::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        (*setInitialImageList)(const closure::LaunchClosure* closure,
+                                        const DyldSharedCache* dyldCacheLoadAddress, const char* dyldCachePath,
+                                        const Array<LoadedImage>& initialImages, const LoadedImage& libSystem);
     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));
+    // added in version 5
+    void        (*setRestrictions)(bool allowAtPaths, bool allowEnvVars);
+    // added in version 6
+    void        (*setNotifyMonitoringDyldMain)(void (*notifyMonitoringDyldMain)());
+    void        (*setNotifyMonitoringDyld)(void (*notifyMonitoringDyldMain)(bool unloading, unsigned imageCount,
+                                                                            const struct mach_header* loadAddresses[],
+                                                                            const char* imagePaths[]));
 };
 
 extern const LibDyldEntryVector entryVectorForDyld;
index 9ab3bb86daa58bf9e8f82209cecc96ef95bba8c7..f26d9ae99e4969ee954479cd16d2efaa0e5cf978 100644 (file)
@@ -41,6 +41,8 @@
 #include "DyldSharedCache.h"
 #include "Trie.hpp"
 #include "MachOFileAbstraction.hpp"
+#include "MachOLoaded.h"
+#include "MachOAnalyzer.h"
 
 
 #ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
@@ -53,20 +55,22 @@ 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);
-
+    void            adjustImageForNewSegmentLocations(CacheBuilder::ASLR_Tracker& aslrTracker,
+                                                      CacheBuilder::LOH_Tracker& lohTracker);
 private:
-    void            adjustReferencesUsingInfoV2(std::vector<void*>& pointersForASLR);
+    void            adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTracker, CacheBuilder::LOH_Tracker& lohTracker);
     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);
+                                    uint64_t imageStartAddress, uint64_t imageEndAddress,
+                                    CacheBuilder::ASLR_Tracker& aslrTracker, CacheBuilder::LOH_Tracker* lohTracker,
+                                    uint32_t*& lastMappedAddr32, uint32_t& lastKind, uint64_t& lastToNewAddress);
+    void            adjustDataPointers(CacheBuilder::ASLR_Tracker& aslrTracker);
+    void            slidePointer(int segIndex, uint64_t segOffset, uint8_t type, CacheBuilder::ASLR_Tracker& aslrTracker);
     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            adjustInstruction(uint8_t kind, uint8_t* textLoc, uint64_t codeToDataDelta);
     void            rebuildLinkEditAndLoadCommands();
     uint64_t        slideForOrigAddress(uint64_t addr);
 
@@ -77,7 +81,6 @@ private:
     macho_header<P>*                                        _mh;
     Diagnostics&                                            _diagnostics;
     const uint8_t*                                          _linkeditBias       = nullptr;
-    int64_t                                                 _linkeditAdjust     = 0;
     unsigned                                                _linkeditSegIndex   = 0;
     bool                                                    _maskPointers       = false;
     bool                                                    _splitSegInfoV2     = false;
@@ -132,10 +135,9 @@ Adjustor<P>::Adjustor(DyldSharedCache* cacheBuffer, macho_header<P>* mh, const s
                 segCmd = (macho_segment_command<P>*)cmd;
                 _segCmds.push_back(segCmd);
                 _segOrigStartAddresses.push_back(segCmd->vmaddr());
-                _segSlides.push_back(_mappingInfo[segIndex].dstCacheAddress - segCmd->vmaddr());
+                _segSlides.push_back(_mappingInfo[segIndex].dstCacheUnslidAddress - segCmd->vmaddr());
                 if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
-                    _linkeditAdjust = _mappingInfo[segIndex].dstCacheOffset - segCmd->fileoff();
-                    _linkeditBias = (uint8_t*)cacheBuffer + _linkeditAdjust;
+                    _linkeditBias = (uint8_t*)_mappingInfo[segIndex].dstSegment - segCmd->fileoff();
                     _linkeditSegIndex = segIndex;
                 }
                 ++segIndex;
@@ -143,7 +145,7 @@ Adjustor<P>::Adjustor(DyldSharedCache* cacheBuffer, macho_header<P>* mh, const s
         }
         cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
     }
-    _maskPointers = (P::E::get32(mh->cputype()) == CPU_TYPE_ARM64);
+    _maskPointers = (P::E::get32(mh->cputype()) == CPU_TYPE_ARM64) || (P::E::get32(mh->cputype()) == CPU_TYPE_ARM64_32);
     if ( _splitSegInfoCmd != NULL ) {
         const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()];
         _splitSegInfoV2 = (*infoStart == DYLD_CACHE_ADJ_V2_FORMAT);
@@ -154,15 +156,16 @@ Adjustor<P>::Adjustor(DyldSharedCache* cacheBuffer, macho_header<P>* mh, const s
 }
 
 template <typename P>
-void Adjustor<P>::adjustImageForNewSegmentLocations(std::vector<void*>& pointersForASLR)
+void Adjustor<P>::adjustImageForNewSegmentLocations(CacheBuilder::ASLR_Tracker& aslrTracker,
+                                                    CacheBuilder::LOH_Tracker& lohTracker)
 {
     if ( _diagnostics.hasError() )
         return;
     if ( _splitSegInfoV2 ) {
-        adjustReferencesUsingInfoV2(pointersForASLR);
+        adjustReferencesUsingInfoV2(aslrTracker, lohTracker);
     }
     else {
-        adjustDataPointers(pointersForASLR);
+        adjustDataPointers(aslrTracker);
         adjustCode();
     }
     if ( _diagnostics.hasError() )
@@ -171,6 +174,14 @@ void Adjustor<P>::adjustImageForNewSegmentLocations(std::vector<void*>& pointers
     if ( _diagnostics.hasError() )
         return;
     rebuildLinkEditAndLoadCommands();
+
+#if DEBUG
+    Diagnostics  diag;
+    ((dyld3::MachOAnalyzer*)_mh)->validateDyldCacheDylib(diag, _installName);
+    if ( diag.hasError() ) {
+        fprintf(stderr, "%s\n", diag.errorMessage().c_str());
+    }
+#endif
 }
 
 template <typename P>
@@ -198,11 +209,11 @@ void Adjustor<P>::rebuildLinkEditAndLoadCommands()
     // 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 weakBindOffset      = bindOffset + bindSize;
     uint32_t weakBindSize        = _dyldInfo->weak_bind_size();
-    uint32_t exportOffset        = weakBindOffset + weakBindSize;
+    uint32_t lazyBindOffset      = weakBindOffset + weakBindSize;
+    uint32_t lazyBindSize        = _dyldInfo->lazy_bind_size();
+    uint32_t exportOffset        = lazyBindOffset + lazyBindSize;
     uint32_t exportSize          = (uint32_t)newTrieBytes.size();
     uint32_t splitSegInfoOffset  = exportOffset + exportSize;
     uint32_t splitSegInfosSize   = (_splitSegInfoCmd ? _splitSegInfoCmd->datasize() : 0);
@@ -224,7 +235,6 @@ void Adjustor<P>::rebuildLinkEditAndLoadCommands()
         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);
@@ -247,9 +257,10 @@ void Adjustor<P>::rebuildLinkEditAndLoadCommands()
     if ( symbolStringsSize )
         memcpy(&newLinkeditBufer[symbolStringsOffset], &_linkeditBias[_symTabCmd->stroff()], symbolStringsSize);
 
-    memcpy((uint8_t*)_cacheBuffer+linkeditStartOffset, newLinkeditBufer, newLinkEditSize);
-    ::bzero((uint8_t*)_cacheBuffer+linkeditStartOffset+newLinkEditSize, linkeditBufferSize-newLinkEditSize);
+    memcpy(_mappingInfo[_linkeditSegIndex].dstSegment, newLinkeditBufer, newLinkEditSize);
+    ::bzero(((uint8_t*)_mappingInfo[_linkeditSegIndex].dstSegment)+newLinkEditSize, linkeditBufferSize-newLinkEditSize);
     ::free(newLinkeditBufer);
+    uint32_t linkeditStartOffset = (uint32_t)_mappingInfo[_linkeditSegIndex].dstCacheFileOffset;
 
     // 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>));
@@ -299,7 +310,7 @@ void Adjustor<P>::rebuildLinkEditAndLoadCommands()
                 dyldInfo->set_lazy_bind_size(lazyBindSize);
                 dyldInfo->set_export_off(exportSize ? linkeditStartOffset+exportOffset : 0);
                 dyldInfo->set_export_size(exportSize);
-               break;
+                break;
             case LC_FUNCTION_STARTS:
                 functionStartsCmd = (macho_linkedit_data_command<P>*)cmd;
                 functionStartsCmd->set_dataoff(linkeditStartOffset+funcStartsOffset);
@@ -314,10 +325,10 @@ void Adjustor<P>::rebuildLinkEditAndLoadCommands()
                 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);
+                segFileOffsetDelta = (int32_t)(_mappingInfo[segIndex].dstCacheFileOffset - segCmd->fileoff());
+                segCmd->set_vmaddr(_mappingInfo[segIndex].dstCacheUnslidAddress);
                 segCmd->set_vmsize(_mappingInfo[segIndex].dstCacheSegmentSize);
-                segCmd->set_fileoff(_mappingInfo[segIndex].dstCacheOffset);
+                segCmd->set_fileoff(_mappingInfo[segIndex].dstCacheFileOffset);
                 segCmd->set_filesize(_mappingInfo[segIndex].copySegmentSize);
                 if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 )
                     segCmd->set_vmsize(linkeditBufferSize);
@@ -387,9 +398,9 @@ void Adjustor<P>::adjustSymbolTable()
 }
 
 template <typename P>
-void Adjustor<P>::slidePointer(int segIndex, uint64_t segOffset, uint8_t type, std::vector<void*>& pointersForASLR)
+void Adjustor<P>::slidePointer(int segIndex, uint64_t segOffset, uint8_t type, CacheBuilder::ASLR_Tracker& aslrTracker)
 {
-    pint_t*   mappedAddrP = (pint_t*)((uint8_t*)_cacheBuffer + _mappingInfo[segIndex].dstCacheOffset + segOffset);
+    pint_t*   mappedAddrP  = (pint_t*)((uint8_t*)_mappingInfo[segIndex].dstSegment + segOffset);
     uint32_t* mappedAddr32 = (uint32_t*)mappedAddrP;
     pint_t    valueP;
     uint32_t  value32;
@@ -397,7 +408,7 @@ void Adjustor<P>::slidePointer(int segIndex, uint64_t segOffset, uint8_t type, s
         case REBASE_TYPE_POINTER:
             valueP = (pint_t)P::getP(*mappedAddrP);
             P::setP(*mappedAddrP, valueP + slideForOrigAddress(valueP));
-            pointersForASLR.push_back(mappedAddrP);
+            aslrTracker.add(mappedAddrP);
             break;
         
         case REBASE_TYPE_TEXT_ABSOLUTE32:
@@ -466,13 +477,15 @@ static uint32_t setArmWord(uint32_t instruction, uint16_t word) {
 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)
+                                  CacheBuilder::ASLR_Tracker& aslrTracker, CacheBuilder::LOH_Tracker* lohTracker,
+                                  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;
+    dyld3::MachOLoaded::ChainedFixupPointerOnDisk chainPtr;
     int64_t offsetAdjust;
     int64_t delta;
     switch ( kind ) {
@@ -489,12 +502,12 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
             break;
         case DYLD_CACHE_ADJ_V2_POINTER_32:
             mappedAddr32 = (uint32_t*)mappedAddr;
-            if ( toNewAddress != (E::get32(*mappedAddr32) + targetSlide) ) {
+            if ( toNewAddress != (uint64_t)(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);
+            aslrTracker.add(mappedAddr32);
             break;
         case DYLD_CACHE_ADJ_V2_POINTER_64:
             mappedAddr64 = (uint64_t*)mappedAddr;
@@ -503,9 +516,26 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
                 return;
             }
             E::set64(*mappedAddr64, toNewAddress);
-            pointersForASLR.push_back(mappedAddr);
+            aslrTracker.add(mappedAddr64);
             break;
-        case DYLD_CACHE_ADJ_V2_DELTA_64:
+        case DYLD_CACHE_ADJ_V2_THREADED_POINTER_64:
+            mappedAddr64 = (uint64_t*)mappedAddr;
+            chainPtr.raw = E::get64(*mappedAddr64);
+            // ignore binds, fix up rebases to have new targets
+            if ( chainPtr.authRebase.bind == 0 ) {
+                if ( chainPtr.authRebase.auth ) {
+                    // auth pointer target is offset in dyld cache
+                    chainPtr.authRebase.target += (((dyld3::MachOAnalyzer*)_mh)->preferredLoadAddress() + targetSlide - _cacheBuffer->header.sharedRegionStart);
+                }
+                else {
+                    // plain pointer target is unslid address of target
+                    chainPtr.plainRebase.target += targetSlide;
+                }
+                // Note, the pointer remains a chain with just the target of the rebase adjusted to the new target location
+                E::set64(*mappedAddr64, chainPtr.raw);
+            }
+            break;
+       case DYLD_CACHE_ADJ_V2_DELTA_64:
             mappedAddr64 = (uint64_t*)mappedAddr;
             value64 = P::E::get64(*mappedAddr64);
             E::set64(*mappedAddr64, value64 + adjust);
@@ -524,6 +554,8 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
             break;
         case DYLD_CACHE_ADJ_V2_ARM64_ADRP:
             mappedAddr32 = (uint32_t*)mappedAddr;
+            if (lohTracker)
+                (*lohTracker)[toNewAddress].insert(mappedAddr);
             instruction = P::E::get32(*mappedAddr32);
             if ( (instruction & 0x9F000000) == 0x90000000 ) {
                 int64_t pageDistance = ((toNewAddress & ~0xFFF) - (fromNewAddress & ~0xFFF));
@@ -541,6 +573,8 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
             break;
         case DYLD_CACHE_ADJ_V2_ARM64_OFF12:
             mappedAddr32 = (uint32_t*)mappedAddr;
+            if (lohTracker)
+                (*lohTracker)[toNewAddress].insert(mappedAddr);
             instruction = P::E::get32(*mappedAddr32);
             offsetAdjust = (adjust & 0xFFF);
             if ( offsetAdjust == 0 )
@@ -713,7 +747,8 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
 }
 
 template <typename P>
-void Adjustor<P>::adjustReferencesUsingInfoV2(std::vector<void*>& pointersForASLR)
+void Adjustor<P>::adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTracker,
+                                              CacheBuilder::LOH_Tracker& lohTracker)
 {
     static const bool log = false;
 
@@ -731,24 +766,27 @@ void Adjustor<P>::adjustReferencesUsingInfoV2(std::vector<void*>& pointersForASL
     sectionNewAddress.reserve(16);
     sectionMappedAddress.reserve(16);
     // section index 0 refers to mach_header
-    sectionMappedAddress.push_back((uint8_t*)_cacheBuffer + _mappingInfo[0].dstCacheOffset);
+    sectionMappedAddress.push_back((uint8_t*)_mappingInfo[0].dstSegment);
     sectionSlides.push_back(_segSlides[0]);
-    sectionNewAddress.push_back(_mappingInfo[0].dstCacheAddress);
+    sectionNewAddress.push_back(_mappingInfo[0].dstCacheUnslidAddress);
     // section 1 and later refer to real sections
     unsigned sectionIndex = 0;
+    unsigned objcSelRefsSectionIndex = ~0U;
     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());
+            sectionMappedAddress.push_back((uint8_t*)_mappingInfo[segmentIndex].dstSegment + sect->addr() - segCmd->vmaddr());
             sectionSlides.push_back(_segSlides[segmentIndex]);
-            sectionNewAddress.push_back(_mappingInfo[segmentIndex].dstCacheAddress + sect->addr() - segCmd->vmaddr());
+            sectionNewAddress.push_back(_mappingInfo[segmentIndex].dstCacheUnslidAddress + sect->addr() - segCmd->vmaddr());
              if (log) {
                 fprintf(stderr, " %s/%s, sectIndex=%d, mapped at=%p\n",
                         sect->segname(), sect->sectname(), sectionIndex, sectionMappedAddress.back());
             }
             ++sectionIndex;
+            if (!strcmp(sect->segname(), "__DATA") && !strcmp(sect->sectname(), "__objc_selrefs"))
+                objcSelRefsSectionIndex = sectionIndex;
         }
     }
 
@@ -770,6 +808,7 @@ void Adjustor<P>::adjustReferencesUsingInfoV2(std::vector<void*>& pointersForASL
         uint8_t* fromSectionMappedAddress = sectionMappedAddress[fromSectionIndex];
         uint64_t toSectionSlide = sectionSlides[toSectionIndex];
         uint64_t toSectionNewAddress = sectionNewAddress[toSectionIndex];
+        CacheBuilder::LOH_Tracker* lohTrackerPtr = (toSectionIndex == objcSelRefsSectionIndex) ? &lohTracker : nullptr;
         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) {
@@ -778,8 +817,8 @@ void Adjustor<P>::adjustReferencesUsingInfoV2(std::vector<void*>& pointersForASL
             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);
+                if ( kind > 13 ) {
+                    _diagnostics.error("unknown split seg info v2 kind value (%llu) in %s", kind, _installName);
                     return;
                 }
                 uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd);
@@ -794,8 +833,9 @@ void Adjustor<P>::adjustReferencesUsingInfoV2(std::vector<void*>& pointersForASL
                     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 ( toSectionIndex != 255 ) {
+                        adjustReference((uint32_t)kind, fromMappedAddr, fromNewAddress, toNewAddress, deltaAdjust, toSectionSlide, imageStartAddress, imageEndAddress, aslrTracker, lohTrackerPtr, lastMappedAddr32, lastKind, lastToNewAddress);
+                    }
                     if ( _diagnostics.hasError() )
                         return;
                 }
@@ -806,7 +846,7 @@ void Adjustor<P>::adjustReferencesUsingInfoV2(std::vector<void*>& pointersForASL
 }
 
 template <typename P>
-void Adjustor<P>::adjustDataPointers(std::vector<void*>& pointersForASLR)
+void Adjustor<P>::adjustDataPointers(CacheBuilder::ASLR_Tracker& aslrTracker)
 {
     const uint8_t* p = &_linkeditBias[_dyldInfo->rebase_off()];
     const uint8_t* end = &p[_dyldInfo->rebase_size()];
@@ -840,26 +880,26 @@ void Adjustor<P>::adjustDataPointers(std::vector<void*>& pointersForASLR)
                 break;
             case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
                 for (int i=0; i < immediate; ++i) {
-                    slidePointer(segIndex, segOffset, type, pointersForASLR);
+                    slidePointer(segIndex, segOffset, type, aslrTracker);
                     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);
+                    slidePointer(segIndex, segOffset, type, aslrTracker);
                     segOffset += sizeof(pint_t);
                 }
                 break;
             case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
-                slidePointer(segIndex, segOffset, type, pointersForASLR);
+                slidePointer(segIndex, segOffset, type, aslrTracker);
                 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);
+                    slidePointer(segIndex, segOffset, type, aslrTracker);
                     segOffset += skip + sizeof(pint_t);
                 }
                 break;
@@ -873,11 +913,10 @@ void Adjustor<P>::adjustDataPointers(std::vector<void*>& pointersForASLR)
 
 
 template <typename P>
-void Adjustor<P>::adjustInstruction(uint8_t kind, uint64_t cacheOffset, uint64_t codeToDataDelta)
+void Adjustor<P>::adjustInstruction(uint8_t kind, uint8_t* textLoc, uint64_t codeToDataDelta)
 {
-    uint8_t* fixupLoc = (uint8_t*)_cacheBuffer + cacheOffset;
-    uint32_t* fixupLoc32 = (uint32_t*)fixupLoc;
-    uint64_t* fixupLoc64 = (uint64_t*)fixupLoc;
+    uint32_t* fixupLoc32 = (uint32_t*)textLoc;
+    uint64_t* fixupLoc64 = (uint64_t*)textLoc;
     uint32_t instruction;
     uint32_t value32;
     uint64_t value64;
@@ -1010,10 +1049,10 @@ void Adjustor<P>::adjustCode()
     // 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;
+        uint8_t* textLoc = (uint8_t*)_mappingInfo[0].dstSegment;
         while (uint64_t delta = read_uleb128(p, infoEnd)) {
-            cacheOffset += delta;
-            adjustInstruction(kind, cacheOffset, codeToDataDelta);
+            textLoc += delta;
+            adjustInstruction(kind, textLoc, codeToDataDelta);
         }
     }
 }
@@ -1065,16 +1104,16 @@ void Adjustor<P>::adjustExportsTrie(std::vector<uint8_t>& newTrieBytes)
 
 } // anonymous namespace
 
-
-void adjustDylibSegments(DyldSharedCache* cache, bool is64, mach_header* mhInCache, const std::vector<CacheBuilder::SegmentMappingInfo>& mappingInfo, std::vector<void*>& pointersForASLR, Diagnostics& diag)
+void CacheBuilder::adjustDylibSegments(const DylibInfo& dylib, Diagnostics& diag) const
 {
-    if ( is64 ) {
-        Adjustor<Pointer64<LittleEndian>> adjustor64(cache, (macho_header<Pointer64<LittleEndian>>*)mhInCache, mappingInfo, diag);
-        adjustor64.adjustImageForNewSegmentLocations(pointersForASLR);
+    DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    if ( _archLayout->is64 ) {
+        Adjustor<Pointer64<LittleEndian>> adjustor64(cache, (macho_header<Pointer64<LittleEndian>>*)dylib.cacheLocation[0].dstSegment, dylib.cacheLocation, diag);
+        adjustor64.adjustImageForNewSegmentLocations(_aslrTracker, _lohTracker);
     }
     else {
-        Adjustor<Pointer32<LittleEndian>> adjustor32(cache, (macho_header<Pointer32<LittleEndian>>*)mhInCache, mappingInfo, diag);
-        adjustor32.adjustImageForNewSegmentLocations(pointersForASLR);
+        Adjustor<Pointer32<LittleEndian>> adjustor32(cache, (macho_header<Pointer32<LittleEndian>>*)dylib.cacheLocation[0].dstSegment, dylib.cacheLocation, diag);
+        adjustor32.adjustImageForNewSegmentLocations(_aslrTracker, _lohTracker);
     }
 }
 
@@ -1082,3 +1121,4 @@ void adjustDylibSegments(DyldSharedCache* cache, bool is64, mach_header* mhInCac
 
 
 
+
index 713d0bda7e2a62956ffecce8a75bbee5a844d378..dc2c5e303ba67e819667f7d8f0229f2b2c4b21c5 100644 (file)
@@ -27,6 +27,7 @@
 
 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);
+bool build(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& masterDstRoot, bool dedupe, bool verbose, bool skipWrites, bool agileChooseSHA256CdHash,
+           bool emitDevCaches, bool isLocallyBuiltCache);
 
 #endif /* BuilderUtils_h */
index 7704301d7a53a47aeb797ab35b2dddb4badca73e..212673d37b1df486f1553f83b8da5c2af79a1ffe 100644 (file)
@@ -23,6 +23,7 @@
 
 
 #include <set>
+#include <array>
 #include <string>
 #include <sstream>
 #include <iomanip> // std::setfill, std::setw
@@ -140,7 +141,7 @@ 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)
+           bool skipWrites, bool agileChooseSHA256CdHash, bool emitDevCaches, bool isLocallyBuiltCache)
 {
     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);
@@ -207,7 +208,8 @@ bool build(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& mas
             }
         }
         
-        manifest.configuration(*cacheSet.begin()).forEachArchitecture([&masterDstRoot, &dedupe, &fileName, &setName, &manifest, &buildQueue, &cacheSet, verbose](const std::string& arch) {
+        manifest.configuration(*cacheSet.begin()).forEachArchitecture([&masterDstRoot, &dedupe, &fileName, &setName, &manifest,
+                                                                       &buildQueue, &cacheSet, skipWrites, verbose, emitDevCaches, isLocallyBuiltCache](const std::string& arch) {
             std::string configPath;
             std::string runtimePath =  "/System/Library/Caches/com.apple.dyld/";
             if (manifest.platform() == dyld3::Platform::macOS) {
@@ -219,11 +221,16 @@ bool build(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& mas
                 configPath = masterDstRoot + runtimePath;
             }
 
+            mkpath_np(configPath.c_str(), 0755);
             if (manifest.platform() == dyld3::Platform::macOS) {
-                buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch, cacheSet, arch, false, setName + "/" + arch, verbose));
+                buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch, cacheSet, arch, false, setName + "/" + arch,
+                                                             isLocallyBuiltCache, skipWrites, 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));
+                if (emitDevCaches)
+                    buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch + ".development", cacheSet, arch, false, setName + "/" + arch,
+                                                                 isLocallyBuiltCache, skipWrites, verbose));
+                buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch, cacheSet, arch, true, setName + "/" + arch,
+                                                             isLocallyBuiltCache, skipWrites, verbose));
             }
         });
     }
@@ -234,7 +241,7 @@ bool build(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& mas
 
     dispatch_sync(warningQueue, ^{
         auto manifestWarnings = diags.warnings();
-        warnings.insert(manifestWarnings.begin(), manifestWarnings.end());
+        //warnings.insert(manifestWarnings.begin(), manifestWarnings.end());
     });
 
     dispatch_apply(buildQueue.size(), queue, ^(size_t index) {
@@ -252,39 +259,21 @@ bool build(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& mas
             for (const auto& configName : queueEntry.configNames) {
                 auto& configResults = manifest.configuration(configName).architecture(queueEntry.options.archName).results;
                 for (const auto& mh : results.evictions) {
-                    auto parser = dyld3::MachOParser(mh);
-                    configResults.exclude(&parser, "VM overflow, evicting");
+                    configResults.exclude(mh, "VM overflow, evicting");
                 }
                 configResults.warnings = results.warnings;
                 if (queueEntry.options.optimizeStubs) {
-                    configResults.developmentCache.cdHash = chooseSecondCdHash ? results.cdHashSecond : results.cdHashFirst;
+                    configResults.productionCache.cdHash = chooseSecondCdHash ? results.cdHashSecond : results.cdHashFirst;
                 } else {
-                    configResults.productionCache.cdHash =  chooseSecondCdHash ? results.cdHashSecond : results.cdHashFirst;
+                    configResults.developmentCache.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 {
+            cacheBuildFailure = true;
+        } else if (skipWrites) {
             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);
         }
     });
     
index 89cd41872e2bd9cc0b7f22d9faa8d053173e4afe..37df074cb6d1cab617f6dd08f03aa6388b5ef9e4 100644 (file)
 #include <sys/errno.h>
 #include <sys/fcntl.h>
 #include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 #include <mach/mach.h>
 #include <mach/mach_time.h>
+#include <mach/mach_vm.h>
 #include <mach-o/loader.h>
 #include <mach-o/fat.h>
 #include <mach/shared_region.h>
 #include <unordered_map>
 #include <unordered_set>
 
-#include "MachOParser.h"
+#include "MachOFileAbstraction.hpp"
 #include "CodeSigningTypes.h"
 #include "DyldSharedCache.h"
 #include "CacheBuilder.h"
 #include "FileAbstraction.hpp"
-#include "LaunchCacheWriter.h"
 #include "Trie.hpp"
+#include "FileUtils.h"
 #include "Diagnostics.h"
-#include "ImageProxy.h"
+#include "ClosureBuilder.h"
+#include "Closure.h"
+#include "StringUtils.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
+    #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
+
+#ifndef ARM64_32_SHARED_REGION_START
+    #define ARM64_32_SHARED_REGION_START 0x1A000000ULL
+    #define ARM64_32_SHARED_REGION_SIZE  0x26000000ULL
 #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 }
+    { 0x7FFF20000000ULL,            0xEFE00000ULL,              0x40000000, 0xFFFF000000000000, "x86_64",  0,          0,          0,          12, 2, true,  true  },
+    { 0x7FFF20000000ULL,            0xEFE00000ULL,              0x40000000, 0xFFFF000000000000, "x86_64h", 0,          0,          0,          12, 2, true,  true  },
+    { SHARED_REGION_BASE_I386,      SHARED_REGION_SIZE_I386,    0x00200000,                0x0, "i386",    0,          0,          0,          12, 0, false, false },
+    { ARM64_SHARED_REGION_START,    ARM64_SHARED_REGION_SIZE,   0x02000000, 0x00FFFF0000000000, "arm64",   0x0000C000, 0x00100000, 0x07F00000, 14, 2, false, true  },
+#if SUPPORT_ARCH_arm64e
+    { ARM64_SHARED_REGION_START,    ARM64_SHARED_REGION_SIZE,   0x02000000, 0x00FFFF0000000000, "arm64e",  0x0000C000, 0x00100000, 0x07F00000, 14, 2, false, true  },
+#endif
+#if SUPPORT_ARCH_arm64_32
+    { ARM64_32_SHARED_REGION_START, ARM64_32_SHARED_REGION_SIZE,0x02000000,         0xC0000000, "arm64_32",0x0000C000, 0x00100000, 0x07F00000, 14, 6, false, false },
+#endif
+    { ARM_SHARED_REGION_START,      ARM_SHARED_REGION_SIZE,     0x02000000,         0xE0000000, "armv7s",  0,          0,          0,          14, 4, false, false },
+    { ARM_SHARED_REGION_START,      ARM_SHARED_REGION_SIZE,     0x00400000,         0xE0000000, "armv7k",  0,          0,          0,          14, 4, false, false },
+    { 0x40000000,                   0x40000000,                 0x02000000,                0x0, "sim-x86", 0,          0,          0,          14, 0, false, false }
 };
 
 
@@ -82,17 +97,16 @@ const char* const CacheBuilder::_s_neverStubEliminate[] = {
 };
 
 
-CacheBuilder::CacheBuilder(const DyldSharedCache::CreateOptions& options)
+CacheBuilder::CacheBuilder(const DyldSharedCache::CreateOptions& options, const dyld3::closure::FileSystem& fileSystem)
     : _options(options)
-    , _buffer(nullptr)
+    , _fileSystem(fileSystem)
+    , _fullAllocatedBuffer(0)
     , _diagnostics(options.loggingPrefix, options.verbose)
     , _archLayout(nullptr)
     , _aliasCount(0)
     , _slideInfoFileOffset(0)
     , _slideInfoBufferSizeAllocated(0)
     , _allocatedBufferSize(0)
-    , _currentFileSize(0)
-    , _vmSize(0)
     , _branchPoolsLinkEditStartAddr(0)
 {
 
@@ -106,6 +120,10 @@ CacheBuilder::CacheBuilder(const DyldSharedCache::CreateOptions& options)
             break;
         }
     }
+
+    if (!_archLayout) {
+        _diagnostics.error("Tool was built without support for: '%s'", targetArch.c_str());
+    }
 }
 
 
@@ -119,26 +137,28 @@ const std::set<std::string> CacheBuilder::warnings()
     return _diagnostics.warnings();
 }
 
-const std::set<const mach_header*> CacheBuilder::evictions()
+const std::set<const dyld3::MachOAnalyzer*> CacheBuilder::evictions()
 {
     return _evictions;
 }
 
 void CacheBuilder::deleteBuffer()
 {
-    vm_deallocate(mach_task_self(), (vm_address_t)_buffer, _allocatedBufferSize);
-    _buffer = nullptr;
+    vm_deallocate(mach_task_self(), _fullAllocatedBuffer, _archLayout->sharedMemorySize);
+    _fullAllocatedBuffer = 0;
     _allocatedBufferSize = 0;
 }
 
-std::vector<DyldSharedCache::MappedMachO>
-CacheBuilder::makeSortedDylibs(const std::vector<DyldSharedCache::MappedMachO>& dylibs, const std::unordered_map<std::string, unsigned> sortOrder)
+
+void CacheBuilder::makeSortedDylibs(const std::vector<LoadedMachO>& dylibs, const std::unordered_map<std::string, unsigned> sortOrder)
 {
-    std::vector<DyldSharedCache::MappedMachO> sortedDylibs = dylibs;
+    for (const LoadedMachO& dylib : dylibs) {
+        _sortedDylibs.push_back({ &dylib, dylib.mappedFile.runtimePath, {} });
+    }
 
-    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);
+    std::sort(_sortedDylibs.begin(), _sortedDylibs.end(), [&](const DylibInfo& a, const DylibInfo& b) {
+        const auto& orderA = sortOrder.find(a.input->mappedFile.runtimePath);
+        const auto& orderB = sortOrder.find(b.input->mappedFile.runtimePath);
         bool foundA = (orderA != sortOrder.end());
         bool foundB = (orderB != sortOrder.end());
 
@@ -152,10 +172,8 @@ CacheBuilder::makeSortedDylibs(const std::vector<DyldSharedCache::MappedMachO>&
         else if ( foundB )
              return false;
         else
-             return a.runtimePath < b.runtimePath;
+             return a.input->mappedFile.runtimePath < b.input->mappedFile.runtimePath;
     });
-
-    return sortedDylibs;
 }
 
 
@@ -166,301 +184,712 @@ inline uint32_t absolutetime_to_milliseconds(uint64_t abstime)
 
 struct DylibAndSize
 {
-    const char*     installName;
-    uint64_t        size;
+    const CacheBuilder::LoadedMachO*    input;
+    const char*                         installName;
+    uint64_t                            size;
 };
 
-bool CacheBuilder::cacheOverflow(const dyld_cache_mapping_info regions[3])
+uint64_t CacheBuilder::cacheOverflowAmount()
 {
     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) );
+        if ( _readExecuteRegion.sizeInUse > 0x60000000 )
+            return (_readExecuteRegion.sizeInUse - 0x60000000);
+
+        if ( _readWriteRegion.sizeInUse > 0x40000000 )
+            return (_readWriteRegion.sizeInUse - 0x40000000);
+
+        if ( _readOnlyRegion.sizeInUse > 0x3FE00000 )
+            return (_readOnlyRegion.sizeInUse - 0x3FE00000);
     }
     else {
-        return (_vmSize > _archLayout->sharedMemorySize);
+        bool alreadyOptimized = (_readOnlyRegion.sizeInUse != _readOnlyRegion.bufferSize);
+        uint64_t vmSize = _readOnlyRegion.unslidLoadAddress - _readExecuteRegion.unslidLoadAddress;
+        if ( alreadyOptimized )
+            vmSize += _readOnlyRegion.sizeInUse;
+        else if ( _options.excludeLocalSymbols )
+            vmSize += (_readOnlyRegion.sizeInUse * 37/100); // assume locals removal and LINKEDIT optimzation reduces LINKEDITs %25 of original size
+        else
+            vmSize += (_readOnlyRegion.sizeInUse * 80/100); // assume LINKEDIT optimzation reduces LINKEDITs to %80 of original size
+        if ( vmSize > _archLayout->sharedMemorySize )
+            return vmSize - _archLayout->sharedMemorySize;
     }
+    // fits in shared region
+    return 0;
 }
 
-void CacheBuilder::build(const std::vector<DyldSharedCache::MappedMachO>& dylibs,
-                         const std::vector<DyldSharedCache::MappedMachO>& otherOsDylibsInput,
-                         const std::vector<DyldSharedCache::MappedMachO>& osExecutables)
+size_t CacheBuilder::evictLeafDylibs(uint64_t reductionTarget, std::vector<const LoadedMachO*>& overflowDylibs)
 {
-    // <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;
+    // build count of how many references there are to each dylib
+    __block std::map<std::string, unsigned int> referenceCount;
+    for (const DylibInfo& dylib : _sortedDylibs) {
+        dylib.input->mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+            referenceCount[loadPath] += 1;
+        });
     }
-    uint64_t t1 = mach_absolute_time();
 
+    // find all dylibs not referenced
+    std::vector<DylibAndSize> unreferencedDylibs;
+    for (const DylibInfo& dylib : _sortedDylibs) {
+        const char* installName = dylib.input->mappedFile.mh->installName();
+        if ( referenceCount.count(installName) == 0 ) {
+            // conservative: sum up all segments except LINKEDIT
+            __block uint64_t segsSize = 0;
+            dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool& stop) {
+                if ( strcmp(info.segName, "__LINKEDIT") != 0 )
+                    segsSize += info.vmSize;
+            });
+            unreferencedDylibs.push_back({ dylib.input, installName, segsSize });
+        }
+    }
+    // sort leaf dylibs by size
+    std::sort(unreferencedDylibs.begin(), unreferencedDylibs.end(), [&](const DylibAndSize& a, const DylibAndSize& b) {
+        return ( a.size > b.size );
+    });
 
-    // make copy of dylib list and sort
-    std::vector<DyldSharedCache::MappedMachO> sortedDylibs = makeSortedDylibs(dylibs, _options.dylibOrdering);
-    std::vector<DyldSharedCache::MappedMachO> otherOsDylibs = otherOsDylibsInput;
+     // build set of dylibs that if removed will allow cache to build
+    for (DylibAndSize& dylib : unreferencedDylibs) {
+        if ( _options.verbose )
+            _diagnostics.warning("to prevent cache overflow, not caching %s", dylib.installName);
+        _evictions.insert(dylib.input->mappedFile.mh);
+        // Track the evicted dylibs so we can try build "other" dlopen closures for them.
+        overflowDylibs.push_back(dylib.input);
+        if ( dylib.size > reductionTarget )
+            break;
+        reductionTarget -= dylib.size;
+    }
 
-    // assign addresses for each segment of each dylib in new cache
-    dyld_cache_mapping_info regions[3];
-    SegmentMapping segmentMapping = assignSegmentAddresses(sortedDylibs, regions);
-    while ( cacheOverflow(regions) ) {
-        if ( !_options.evictLeafDylibsOnOverflow ) {
-            _diagnostics.error("cache overflow: %lluMB (max %lluMB)", _vmSize / 1024 / 1024, (_archLayout->sharedMemorySize) / 1024 / 1024);
-            return;
+    // prune _sortedDylibs
+    _sortedDylibs.erase(std::remove_if(_sortedDylibs.begin(), _sortedDylibs.end(), [&](const DylibInfo& dylib) {
+        return (_evictions.count(dylib.input->mappedFile.mh) != 0);
+    }),_sortedDylibs.end());
+
+    return _evictions.size();
+}
+
+// Handles building a list of input files to the CacheBuilder itself.
+class CacheInputBuilder {
+public:
+    CacheInputBuilder(const dyld3::closure::FileSystem& fileSystem,
+                      std::string reqArchitecture, dyld3::Platform reqPlatform)
+    : fileSystem(fileSystem), reqArchitecture(reqArchitecture), reqPlatform(reqPlatform) { }
+
+    // Loads and maps any MachOs in the given list of files.
+    void loadMachOs(std::vector<CacheBuilder::InputFile>& inputFiles,
+                    std::vector<CacheBuilder::LoadedMachO>& dylibsToCache,
+                    std::vector<CacheBuilder::LoadedMachO>& otherDylibs,
+                    std::vector<CacheBuilder::LoadedMachO>& executables,
+                    std::vector<CacheBuilder::LoadedMachO>& couldNotLoadFiles) {
+
+        std::map<std::string, uint64_t> dylibInstallNameMap;
+        for (CacheBuilder::InputFile& inputFile : inputFiles) {
+            dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(inputFile.diag, fileSystem, inputFile.path, reqArchitecture.c_str(), reqPlatform);
+            const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
+            if (ma == nullptr) {
+                couldNotLoadFiles.emplace_back((CacheBuilder::LoadedMachO){ DyldSharedCache::MappedMachO(), loadedFileInfo, &inputFile });
+                continue;
+            }
+
+            DyldSharedCache::MappedMachO mappedFile(inputFile.path, ma, loadedFileInfo.sliceLen, false, false,
+                                                    loadedFileInfo.sliceOffset, loadedFileInfo.mtime, loadedFileInfo.inode);
+
+            // The file can be loaded with the given slice, but we may still want to exlude it from the cache.
+            if (ma->isDylib()) {
+                std::string installName = ma->installName();
+
+                // Let the platform exclude the file before we do anything else.
+                if (platformExcludesInstallName(installName)) {
+                    inputFile.diag.verbose("Platform excluded file\n");
+                    fileSystem.unloadFile(loadedFileInfo);
+                    continue;
+                }
+
+                if (!ma->canBePlacedInDyldCache(inputFile.path, ^(const char* msg) {
+                    inputFile.diag.warning("Dylib located at '%s' cannot be placed in cache because: %s", inputFile.path, msg);
+                })) {
+                    // TODO: Add exclusion lists here?
+                    // Probably not as we already applied the dylib exclusion list.
+                    otherDylibs.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
+                    continue;
+                }
+
+                // Otherwise see if we have another file with this install name
+                auto iteratorAndInserted = dylibInstallNameMap.insert(std::make_pair(installName, dylibsToCache.size()));
+                if (iteratorAndInserted.second) {
+                    // We inserted the dylib so we haven't seen another with this name.
+                    if (installName[0] != '@' && installName != inputFile.path) {
+                        inputFile.diag.warning("Dylib located at '%s' has installname '%s'", inputFile.path, installName.c_str());
+                    }
+
+                    dylibsToCache.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
+                } else {
+                    // We didn't insert this one so we've seen it before.
+                    CacheBuilder::LoadedMachO& previousLoadedMachO = dylibsToCache[iteratorAndInserted.first->second];
+                    inputFile.diag.warning("Multiple dylibs claim installname '%s' ('%s' and '%s')", installName.c_str(), inputFile.path, previousLoadedMachO.mappedFile.runtimePath.c_str());
+
+                    // This is the "Good" one, overwrite
+                    if (inputFile.path == installName) {
+                        // Unload the old one
+                        fileSystem.unloadFile(previousLoadedMachO.loadedFileInfo);
+
+                        // And replace with this one.
+                        previousLoadedMachO.mappedFile = mappedFile;
+                        previousLoadedMachO.loadedFileInfo = loadedFileInfo;
+                    }
+                }
+            } else if (ma->isBundle()) {
+                // TODO: Add exclusion lists here?
+                otherDylibs.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
+            } else if (ma->isDynamicExecutable()) {
+                if (platformExcludesExecutablePath_macOS(inputFile.path)) {
+                    inputFile.diag.verbose("Platform excluded file\n");
+                    fileSystem.unloadFile(loadedFileInfo);
+                    continue;
+                }
+                executables.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
+            } else {
+                inputFile.diag.verbose("Unsupported mach file type\n");
+                fileSystem.unloadFile(loadedFileInfo);
+            }
         }
-        // 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;
+    }
+
+private:
+
+
+
+    static bool platformExcludesInstallName_macOS(const std::string& installName) {
+        return false;
+    }
+
+    static bool platformExcludesInstallName_iOS(const std::string& installName) {
+        if ( installName == "/System/Library/Caches/com.apple.xpc/sdk.dylib" )
+            return true;
+        if ( installName == "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib" )
+            return true;
+        return false;
+    }
+
+    static bool platformExcludesInstallName_tvOS(const std::string& installName) {
+        return platformExcludesInstallName_iOS(installName);
+    }
+
+    static bool platformExcludesInstallName_watchOS(const std::string& installName) {
+        return platformExcludesInstallName_iOS(installName);
+    }
+
+    static bool platformExcludesInstallName_bridgeOS(const std::string& installName) {
+        return platformExcludesInstallName_iOS(installName);
+    }
+
+    // Returns true if the current platform requires that this install name be excluded from the shared cache
+    // Note that this overrides any exclusion from anywhere else.
+    bool platformExcludesInstallName(const std::string& installName) {
+        switch (reqPlatform) {
+            case dyld3::Platform::unknown:
+                return false;
+            case dyld3::Platform::macOS:
+                return platformExcludesInstallName_macOS(installName);
+            case dyld3::Platform::iOS:
+                return platformExcludesInstallName_iOS(installName);
+            case dyld3::Platform::tvOS:
+                return platformExcludesInstallName_tvOS(installName);
+            case dyld3::Platform::watchOS:
+                return platformExcludesInstallName_watchOS(installName);
+            case dyld3::Platform::bridgeOS:
+                return platformExcludesInstallName_bridgeOS(installName);
+            case dyld3::Platform::iOSMac:
+                return false;
+            case dyld3::Platform::iOS_simulator:
+                return false;
+            case dyld3::Platform::tvOS_simulator:
+                return false;
+            case dyld3::Platform::watchOS_simulator:
+                return false;
+        }
+    }
+
+
+
+
+    static bool platformExcludesExecutablePath_macOS(const std::string& path) {
+        return false;
+    }
+
+    static bool platformExcludesExecutablePath_iOS(const std::string& path) {
+        //HACK exclude all launchd and installd variants until we can do something about xpcd_cache.dylib and friends
+        if (path == "/sbin/launchd"
+            || path == "/usr/local/sbin/launchd.debug"
+            || path == "/usr/local/sbin/launchd.development"
+            || path == "/usr/libexec/installd") {
+            return true;
+        }
+        return false;
+    }
+
+    static bool platformExcludesExecutablePath_tvOS(const std::string& path) {
+        return platformExcludesExecutablePath_iOS(path);
+    }
+
+    static bool platformExcludesExecutablePath_watchOS(const std::string& path) {
+        return platformExcludesExecutablePath_iOS(path);
+    }
+
+    static bool platformExcludesExecutablePath_bridgeOS(const std::string& path) {
+        return platformExcludesExecutablePath_iOS(path);
+    }
+
+    // Returns true if the current platform requires that this path be excluded from the shared cache
+    // Note that this overrides any exclusion from anywhere else.
+    bool platformExcludesExecutablePath(const std::string& path) {
+        switch (reqPlatform) {
+            case dyld3::Platform::unknown:
+                return false;
+            case dyld3::Platform::macOS:
+                return platformExcludesExecutablePath_macOS(path);
+            case dyld3::Platform::iOS:
+                return platformExcludesExecutablePath_iOS(path);
+            case dyld3::Platform::tvOS:
+                return platformExcludesExecutablePath_tvOS(path);
+            case dyld3::Platform::watchOS:
+                return platformExcludesExecutablePath_watchOS(path);
+            case dyld3::Platform::bridgeOS:
+                return platformExcludesExecutablePath_bridgeOS(path);
+            case dyld3::Platform::iOSMac:
+                return false;
+            case dyld3::Platform::iOS_simulator:
+                return false;
+            case dyld3::Platform::tvOS_simulator:
+                return false;
+            case dyld3::Platform::watchOS_simulator:
+                return false;
+        }
+    }
+
+    const dyld3::closure::FileSystem&                   fileSystem;
+    std::string                                         reqArchitecture;
+    dyld3::Platform                                     reqPlatform;
+};
+
+static void verifySelfContained(std::vector<CacheBuilder::LoadedMachO>& dylibsToCache,
+                                std::vector<CacheBuilder::LoadedMachO>& otherDylibs,
+                                std::vector<CacheBuilder::LoadedMachO>& couldNotLoadFiles)
+{
+    // build map of dylibs
+    __block std::map<std::string, const CacheBuilder::LoadedMachO*> knownDylibs;
+    __block std::map<std::string, const CacheBuilder::LoadedMachO*> allDylibs;
+    for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+        knownDylibs.insert({ dylib.mappedFile.runtimePath, &dylib });
+        allDylibs.insert({ dylib.mappedFile.runtimePath, &dylib });
+        if (const char* installName = dylib.mappedFile.mh->installName()) {
+            knownDylibs.insert({ installName, &dylib });
+            allDylibs.insert({ installName, &dylib });
+        }
+    }
+
+    for (const CacheBuilder::LoadedMachO& dylib : otherDylibs) {
+        allDylibs.insert({ dylib.mappedFile.runtimePath, &dylib });
+        if (const char* installName = dylib.mappedFile.mh->installName())
+            allDylibs.insert({ installName, &dylib });
+    }
+
+    for (const CacheBuilder::LoadedMachO& dylib : couldNotLoadFiles) {
+        allDylibs.insert({ dylib.inputFile->path, &dylib });
+    }
+
+    // check all dependencies to assure every dylib in cache only depends on other dylibs in cache
+    __block std::map<std::string, std::set<std::string>> badDylibs;
+    __block bool doAgain = true;
+    while ( doAgain ) {
+        doAgain = false;
+        // scan dylib list making sure all dependents are in dylib list
+        for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+            if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 )
+                continue;
+            dylib.mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+                if (isWeak)
+                    return;
+                if ( knownDylibs.count(loadPath) == 0 ) {
+                    badDylibs[dylib.mappedFile.runtimePath].insert(std::string("Could not find dependency '") + loadPath + "'");
+                    knownDylibs.erase(dylib.mappedFile.runtimePath);
+                    knownDylibs.erase(dylib.mappedFile.mh->installName());
+                    doAgain = true;
+                }
+            });
+        }
+    }
+
+    // Now walk the dylibs which depend on missing dylibs and see if any of them are required binaries.
+    for (auto badDylibsIterator : badDylibs) {
+        const std::string& dylibRuntimePath = badDylibsIterator.first;
+        auto requiredDylibIterator = allDylibs.find(dylibRuntimePath);
+        if (requiredDylibIterator == allDylibs.end())
+            continue;
+        if (!requiredDylibIterator->second->inputFile->mustBeIncluded())
+            continue;
+        // This dylib is required so mark all dependencies as requried too
+        __block std::vector<const CacheBuilder::LoadedMachO*> worklist;
+        worklist.push_back(requiredDylibIterator->second);
+        while (!worklist.empty()) {
+            const CacheBuilder::LoadedMachO* dylib = worklist.back();
+            worklist.pop_back();
+            if (!dylib->mappedFile.mh)
+                continue;
+            dylib->mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+                if (isWeak)
+                    return;
+                auto dylibIterator = allDylibs.find(loadPath);
+                if (dylibIterator != allDylibs.end()) {
+                    if (dylibIterator->second->inputFile->state == CacheBuilder::InputFile::Unset) {
+                        dylibIterator->second->inputFile->state = CacheBuilder::InputFile::MustBeIncludedForDependent;
+                        worklist.push_back(dylibIterator->second);
+                    }
+                }
             });
         }
+    }
 
-        // 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;
+    // FIXME: Make this an option we can pass in
+    const bool evictLeafDylibs = true;
+    if (evictLeafDylibs) {
+        doAgain = true;
+        while ( doAgain ) {
+            doAgain = false;
+
+            // build count of how many references there are to each dylib
+            __block std::set<std::string> referencedDylibs;
+            for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+                if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 )
+                    continue;
+                dylib.mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+                    referencedDylibs.insert(loadPath);
                 });
-                unreferencedDylibs.push_back({installName, segsSize});
+            }
+
+            // find all dylibs not referenced
+            std::vector<DylibAndSize> unreferencedDylibs;
+            for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+                if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 )
+                    continue;
+                const char* installName = dylib.mappedFile.mh->installName();
+                if ( (referencedDylibs.count(installName) == 0) && (dylib.inputFile->state == CacheBuilder::InputFile::MustBeExcludedIfUnused) ) {
+                    badDylibs[dylib.mappedFile.runtimePath].insert(std::string("It has been explicitly excluded as it is unused"));
+                    doAgain = true;
+                }
             }
         }
-        // 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;
+    // Move bad dylibs from dylibs to cache to other dylibs.
+    for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+        auto i = badDylibs.find(dylib.mappedFile.runtimePath);
+        if ( i != badDylibs.end()) {
+            otherDylibs.push_back(dylib);
+            for (const std::string& reason : i->second )
+                otherDylibs.back().inputFile->diag.warning("Dylib located at '%s' not placed in shared cache because: %s", dylib.mappedFile.runtimePath.c_str(), reason.c_str());
+        }
+    }
+
+    const auto& badDylibsLambdaRef = badDylibs;
+    dylibsToCache.erase(std::remove_if(dylibsToCache.begin(), dylibsToCache.end(), [&](const CacheBuilder::LoadedMachO& dylib) {
+        if (badDylibsLambdaRef.find(dylib.mappedFile.runtimePath) != badDylibsLambdaRef.end())
+            return true;
+        return false;
+    }), dylibsToCache.end());
+}
+
+// This is the new build API which takes the raw files (which could be FAT) and tries to build a cache from them.
+// We should remove the other build() method, or make it private so that this can wrap it.
+void CacheBuilder::build(std::vector<CacheBuilder::InputFile>& inputFiles,
+                         std::vector<DyldSharedCache::FileAlias>& aliases) {
+    // First filter down to files which are actually MachO's
+    CacheInputBuilder cacheInputBuilder(_fileSystem, _archLayout->archName, _options.platform);
+
+    std::vector<LoadedMachO> dylibsToCache;
+    std::vector<LoadedMachO> otherDylibs;
+    std::vector<LoadedMachO> executables;
+    std::vector<LoadedMachO> couldNotLoadFiles;
+    cacheInputBuilder.loadMachOs(inputFiles, dylibsToCache, otherDylibs, executables, couldNotLoadFiles);
+
+    verifySelfContained(dylibsToCache, otherDylibs, couldNotLoadFiles);
+
+    // Check for required binaries before we try to build the cache
+    if (!_diagnostics.hasError()) {
+        // If we succeeded in building, then now see if there was a missing required file, and if so why its missing.
+        std::string errorString;
+        for (const LoadedMachO& dylib : otherDylibs) {
+            if (dylib.inputFile->mustBeIncluded()) {
+                // An error loading a required file must be propagated up to the top level diagnostic handler.
+                bool gotWarning = false;
+                for (const std::string& warning : dylib.inputFile->diag.warnings()) {
+                    gotWarning = true;
+                    std::string message = warning;
+                    if (message.back() == '\n')
+                        message.pop_back();
+                    if (!errorString.empty())
+                        errorString += "ERROR: ";
+                    errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: " + message + "\n";
+                }
+                if (!gotWarning) {
+                    if (!errorString.empty())
+                        errorString += "ERROR: ";
+                    errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: 'unknown error.  Please report to dyld'\n";
+                }
+            }
         }
-        // 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() ) {
-                    _evictions.insert(parser.header());
-                    otherOsDylibs.push_back(*it);
-                    sortedDylibs.erase(it);
-                    break;
+        for (const LoadedMachO& dylib : couldNotLoadFiles) {
+            if (dylib.inputFile->mustBeIncluded()) {
+                if (dylib.inputFile->diag.hasError()) {
+                    if (!errorString.empty())
+                        errorString += "ERROR: ";
+                    errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: " + dylib.inputFile->diag.errorMessage() + "\n";
+                } else {
+                    if (!errorString.empty())
+                        errorString += "ERROR: ";
+                    errorString += "Required binary was not included in the shared cache '" + std::string(dylib.inputFile->path) + "' because: 'unknown error.  Please report to dyld'\n";
+
                 }
             }
         }
-        // re-layout cache
-        segmentMapping = assignSegmentAddresses(sortedDylibs, regions);
-        if ( unreferencedDylibs.size() == 0 && 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;
+        if (!errorString.empty()) {
+            _diagnostics.error("%s", errorString.c_str());
         }
     }
 
-    // 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 ) {
+    if (!_diagnostics.hasError())
+        build(dylibsToCache, otherDylibs, executables, aliases);
+
+    if (!_diagnostics.hasError()) {
+        // If we succeeded in building, then now see if there was a missing required file, and if so why its missing.
+        std::string errorString;
+        for (CacheBuilder::InputFile& inputFile : inputFiles) {
+            if (inputFile.mustBeIncluded() && inputFile.diag.hasError()) {
+                // An error loading a required file must be propagated up to the top level diagnostic handler.
+                std::string message = inputFile.diag.errorMessage();
+                if (message.back() == '\n')
+                    message.pop_back();
+                errorString += "Required binary was not included in the shared cache '" + std::string(inputFile.path) + "' because: " + message + "\n";
+            }
+        }
+        if (!errorString.empty()) {
+            _diagnostics.error("%s", errorString.c_str());
+        }
+    }
+
+    // Add all the warnings from the input files to the top level warnings on the main diagnostics object.
+    for (CacheBuilder::InputFile& inputFile : inputFiles) {
+        for (const std::string& warning : inputFile.diag.warnings())
+            _diagnostics.warning("%s", warning.c_str());
+    }
+
+    // Clean up the loaded files
+    for (LoadedMachO& loadedMachO : dylibsToCache)
+        _fileSystem.unloadFile(loadedMachO.loadedFileInfo);
+    for (LoadedMachO& loadedMachO : otherDylibs)
+        _fileSystem.unloadFile(loadedMachO.loadedFileInfo);
+    for (LoadedMachO& loadedMachO : executables)
+        _fileSystem.unloadFile(loadedMachO.loadedFileInfo);
+}
+
+void CacheBuilder::build(const std::vector<DyldSharedCache::MappedMachO>& dylibs,
+                         const std::vector<DyldSharedCache::MappedMachO>& otherOsDylibsInput,
+                         const std::vector<DyldSharedCache::MappedMachO>& osExecutables,
+                         std::vector<DyldSharedCache::FileAlias>& aliases) {
+
+    std::vector<LoadedMachO> dylibsToCache;
+    std::vector<LoadedMachO> otherDylibs;
+    std::vector<LoadedMachO> executables;
+
+    for (const DyldSharedCache::MappedMachO& mappedMachO : dylibs) {
+        dyld3::closure::LoadedFileInfo loadedFileInfo;
+        loadedFileInfo.fileContent      = mappedMachO.mh;
+        loadedFileInfo.fileContentLen   = mappedMachO.length;
+        loadedFileInfo.sliceOffset      = mappedMachO.sliceFileOffset;
+        loadedFileInfo.sliceLen         = mappedMachO.length;
+        loadedFileInfo.inode            = mappedMachO.inode;
+        loadedFileInfo.mtime            = mappedMachO.modTime;
+        loadedFileInfo.path             = mappedMachO.runtimePath.c_str();
+        dylibsToCache.emplace_back((LoadedMachO){ mappedMachO, loadedFileInfo, nullptr });
+    }
+
+    for (const DyldSharedCache::MappedMachO& mappedMachO : otherOsDylibsInput) {
+        dyld3::closure::LoadedFileInfo loadedFileInfo;
+        loadedFileInfo.fileContent      = mappedMachO.mh;
+        loadedFileInfo.fileContentLen   = mappedMachO.length;
+        loadedFileInfo.sliceOffset      = mappedMachO.sliceFileOffset;
+        loadedFileInfo.sliceLen         = mappedMachO.length;
+        loadedFileInfo.inode            = mappedMachO.inode;
+        loadedFileInfo.mtime            = mappedMachO.modTime;
+        loadedFileInfo.path             = mappedMachO.runtimePath.c_str();
+        otherDylibs.emplace_back((LoadedMachO){ mappedMachO, loadedFileInfo, nullptr });
+    }
+
+    for (const DyldSharedCache::MappedMachO& mappedMachO : osExecutables) {
+        dyld3::closure::LoadedFileInfo loadedFileInfo;
+        loadedFileInfo.fileContent      = mappedMachO.mh;
+        loadedFileInfo.fileContentLen   = mappedMachO.length;
+        loadedFileInfo.sliceOffset      = mappedMachO.sliceFileOffset;
+        loadedFileInfo.sliceLen         = mappedMachO.length;
+        loadedFileInfo.inode            = mappedMachO.inode;
+        loadedFileInfo.mtime            = mappedMachO.modTime;
+        loadedFileInfo.path             = mappedMachO.runtimePath.c_str();
+        executables.emplace_back((LoadedMachO){ mappedMachO, loadedFileInfo, nullptr });
+    }
+
+    build(dylibsToCache, otherDylibs, executables, aliases);
+}
+
+void CacheBuilder::build(const std::vector<LoadedMachO>& dylibs,
+                         const std::vector<LoadedMachO>& otherOsDylibsInput,
+                         const std::vector<LoadedMachO>& osExecutables,
+                         std::vector<DyldSharedCache::FileAlias>& aliases)
+{
+    // <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;
+    }
+    uint64_t t1 = mach_absolute_time();
+
+    // make copy of dylib list and sort
+    makeSortedDylibs(dylibs, _options.dylibOrdering);
+
+    // allocate space used by largest possible cache plus room for LINKEDITS before optimization
+    _allocatedBufferSize = _archLayout->sharedMemorySize * 1.50;
+    if ( vm_allocate(mach_task_self(), &_fullAllocatedBuffer, _allocatedBufferSize, VM_FLAGS_ANYWHERE) != 0 ) {
         _diagnostics.error("could not allocate buffer");
         return;
     }
-    _currentFileSize = _allocatedBufferSize;
 
-    // write unoptimized cache
-    writeCacheHeader(regions, sortedDylibs, segmentMapping);
-    copyRawSegments(sortedDylibs, segmentMapping);
-    adjustAllImagesForNewSegmentLocations(sortedDylibs, segmentMapping);
+    // assign addresses for each segment of each dylib in new cache
+    assignSegmentAddresses();
+    std::vector<const LoadedMachO*> overflowDylibs;
+    while ( cacheOverflowAmount() != 0 ) {
+        if ( !_options.evictLeafDylibsOnOverflow ) {
+            _diagnostics.error("cache overflow by %lluMB", cacheOverflowAmount() / 1024 / 1024);
+            return;
+        }
+        size_t evictionCount = evictLeafDylibs(cacheOverflowAmount(), overflowDylibs);
+        // re-layout cache
+        for (DylibInfo& dylib : _sortedDylibs)
+            dylib.cacheLocation.clear();
+        assignSegmentAddresses();
+
+        _diagnostics.verbose("cache overflow, evicted %lu leaf dylibs\n", evictionCount);
+    }
+    markPaddingInaccessible();
+
+     // copy all segments into cache
+    uint64_t t2 = mach_absolute_time();
+    writeCacheHeader();
+    copyRawSegments();
+
+    // rebase all dylibs for new location in cache
+    uint64_t t3 = mach_absolute_time();
+    _aslrTracker.setDataRegion(_readWriteRegion.buffer, _readWriteRegion.sizeInUse);
+    adjustAllImagesForNewSegmentLocations();
     if ( _diagnostics.hasError() )
         return;
 
-    bindAllImagesInCacheFile(regions);
+    // build ImageArray for dyld3, which has side effect of binding all cached dylibs
+    uint64_t t4 = mach_absolute_time();
+    buildImageArray(aliases);
     if ( _diagnostics.hasError() )
         return;
 
     // optimize ObjC
+    uint64_t t5 = mach_absolute_time();
+    DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
     if ( _options.optimizeObjC )
-        optimizeObjC(_buffer, _archLayout->is64, _options.optimizeStubs, _pointersForASLR, _diagnostics);
+        optimizeObjC();
     if ( _diagnostics.hasError() )
         return;
 
+
     // optimize away stubs
+    uint64_t t6 = mach_absolute_time();
     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) {
+        const uint64_t* p = (uint64_t*)((uint8_t*)dyldCache + dyldCache->header.branchPoolsOffset);
+        for (uint32_t i=0; i < dyldCache->header.branchPoolsCount; ++i) {
             uint64_t poolAddr = p[i];
             branchPoolStartAddrs.push_back(poolAddr);
             branchPoolOffsets.push_back(poolAddr - cacheStartAddress);
         }
-        bypassStubs(_buffer, branchPoolStartAddrs, _s_neverStubEliminate, _diagnostics);
+        optimizeAwayStubs(branchPoolStartAddrs, _branchPoolsLinkEditStartAddr);
     }
-    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)
+
+    // FIPS seal corecrypto, This must be done after stub elimination (so that __TEXT,__text is not changed after sealing)
     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();
+    uint64_t t7 = mach_absolute_time();
+    optimizeLinkedit(branchPoolOffsets);
 
-    // 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);
+    // copy ImageArray to end of read-only region
+    addImageArray();
     if ( _diagnostics.hasError() )
         return;
-    addCachedDylibsImageGroup(dylibGroup);
-    if ( _diagnostics.hasError() )
-        return;
-
-    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);
+    // compute and add dlopen closures for all other dylibs
+    addOtherImageArray(otherOsDylibsInput, overflowDylibs);
     if ( _diagnostics.hasError() )
         return;
-    addCachedOtherDylibsImageGroup(otherGroup);
-    if ( _diagnostics.hasError() )
-        return;
-
-    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);
+    // compute and add launch closures to end of read-only region
+    uint64_t t8 = mach_absolute_time();
+    addClosures(osExecutables);
     if ( _diagnostics.hasError() )
         return;
 
-    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;
+    // update final readOnly region size
+    dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + dyldCache->header.mappingOffset);
+    mappings[2].size = _readOnlyRegion.sizeInUse;
+    if ( _options.excludeLocalSymbols )
+        dyldCache->header.localSymbolsOffset = _readOnlyRegion.cacheFileOffset + _readOnlyRegion.sizeInUse;
 
-    // record cache bounds
-    _buffer->header.sharedRegionStart = _archLayout->sharedMemoryStart;
-    _buffer->header.sharedRegionSize  = _archLayout->sharedMemorySize;
+    // record max slide now that final size is established
     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);
+        uint64_t maxSlide0 = 0x60000000 - _readExecuteRegion.sizeInUse; // TEXT region has 1.5GB region
+        uint64_t maxSlide1 = 0x40000000 - _readWriteRegion.sizeInUse;
+        uint64_t maxSlide2 = 0x3FE00000 - _readOnlyRegion.sizeInUse;
+        dyldCache->header.maxSlide = std::min(std::min(maxSlide0, maxSlide1), maxSlide2);
     }
     else {
-        _buffer->header.maxSlide = (_archLayout->sharedMemoryStart + _archLayout->sharedMemorySize) - (mappings[2].address + mappings[2].size);
+        dyldCache->header.maxSlide = (_archLayout->sharedMemoryStart + _archLayout->sharedMemorySize) - (_readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse);
     }
 
-    // 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);
+    uint64_t t9 = 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 SUPPORT_ARCH_arm64e
+        if ( strcmp(_archLayout->archName, "arm64e") == 0 )
+            writeSlideInfoV3(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
+        else
+#endif
+        if ( _archLayout->is64 )
+            writeSlideInfoV2<Pointer64<LittleEndian>>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
+        else
+#if SUPPORT_ARCH_arm64_32
+        if ( strcmp(_archLayout->archName, "arm64_32") == 0 )
+            writeSlideInfoV4<Pointer32<LittleEndian>>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
+        else
+#endif
+            writeSlideInfoV2<Pointer32<LittleEndian>>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
     }
 
-    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;
+    uint64_t t10 = mach_absolute_time();
 
     // 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);
+    if ( cacheOverflowAmount() != 0 ) {
+        _diagnostics.error("cache overflow after optimizations 0x%llX -> 0x%llX", _readExecuteRegion.unslidLoadAddress, _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse);
         return;
     }
 
@@ -469,31 +898,26 @@ void CacheBuilder::build(const std::vector<DyldSharedCache::MappedMachO>& dylibs
     if ( _diagnostics.hasError() )
         return;
 
-    uint64_t t8 = mach_absolute_time();
+    uint64_t t11 = 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;
+        fprintf(stderr, "time to layout cache: %ums\n", absolutetime_to_milliseconds(t2-t1));
+        fprintf(stderr, "time to copy cached dylibs into buffer: %ums\n", absolutetime_to_milliseconds(t3-t2));
+        fprintf(stderr, "time to adjust segments for new split locations: %ums\n", absolutetime_to_milliseconds(t4-t3));
+        fprintf(stderr, "time to bind all images: %ums\n", absolutetime_to_milliseconds(t5-t4));
+        fprintf(stderr, "time to optimize Objective-C: %ums\n", absolutetime_to_milliseconds(t6-t5));
+        fprintf(stderr, "time to do stub elimination: %ums\n", absolutetime_to_milliseconds(t7-t6));
+        fprintf(stderr, "time to optimize LINKEDITs: %ums\n", absolutetime_to_milliseconds(t8-t7));
+        fprintf(stderr, "time to build %lu closures: %ums\n", osExecutables.size(), absolutetime_to_milliseconds(t9-t8));
+        fprintf(stderr, "time to compute slide info: %ums\n", absolutetime_to_milliseconds(t10-t9));
+        fprintf(stderr, "time to compute UUID and codesign cache file: %ums\n", absolutetime_to_milliseconds(t11-t10));
     }
 
     return;
 }
 
 
-void CacheBuilder::writeCacheHeader(const dyld_cache_mapping_info regions[3], const std::vector<DyldSharedCache::MappedMachO>& dylibs, const SegmentMapping& segmentMappings)
+void CacheBuilder::writeCacheHeader()
 {
     // "dyld_v1" + spaces + archName(), with enough spaces to pad to 15 bytes
     std::string magic = "dyld_v1";
@@ -502,60 +926,83 @@ void CacheBuilder::writeCacheHeader(const dyld_cache_mapping_info regions[3], co
     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];
+    dyld_cache_header* dyldCacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer;
+    memcpy(dyldCacheHeader->magic, magic.c_str(), 16);
+    dyldCacheHeader->mappingOffset        = sizeof(dyld_cache_header);
+    dyldCacheHeader->mappingCount         = 3;
+    dyldCacheHeader->imagesOffset         = (uint32_t)(dyldCacheHeader->mappingOffset + 3*sizeof(dyld_cache_mapping_info) + sizeof(uint64_t)*_branchPoolStarts.size());
+    dyldCacheHeader->imagesCount          = (uint32_t)_sortedDylibs.size() + _aliasCount;
+    dyldCacheHeader->dyldBaseAddress      = 0;
+    dyldCacheHeader->codeSignatureOffset  = 0;
+    dyldCacheHeader->codeSignatureSize    = 0;
+    dyldCacheHeader->slideInfoOffset      = _slideInfoFileOffset;
+    dyldCacheHeader->slideInfoSize        = _slideInfoBufferSizeAllocated;
+    dyldCacheHeader->localSymbolsOffset   = 0;
+    dyldCacheHeader->localSymbolsSize     = 0;
+    dyldCacheHeader->cacheType            = _options.optimizeStubs ? kDyldSharedCacheTypeProduction : kDyldSharedCacheTypeDevelopment;
+    dyldCacheHeader->accelerateInfoAddr   = 0;
+    dyldCacheHeader->accelerateInfoSize   = 0;
+    bzero(dyldCacheHeader->uuid, 16);// overwritten later by recomputeCacheUUID()
+    dyldCacheHeader->branchPoolsOffset    = dyldCacheHeader->mappingOffset + 3*sizeof(dyld_cache_mapping_info);
+    dyldCacheHeader->branchPoolsCount     = (uint32_t)_branchPoolStarts.size();
+    dyldCacheHeader->imagesTextOffset     = dyldCacheHeader->imagesOffset + sizeof(dyld_cache_image_info)*dyldCacheHeader->imagesCount;
+    dyldCacheHeader->imagesTextCount      = _sortedDylibs.size();
+    dyldCacheHeader->dylibsImageGroupAddr = 0;
+    dyldCacheHeader->dylibsImageGroupSize = 0;
+    dyldCacheHeader->otherImageGroupAddr  = 0;
+    dyldCacheHeader->otherImageGroupSize  = 0;
+    dyldCacheHeader->progClosuresAddr     = 0;
+    dyldCacheHeader->progClosuresSize     = 0;
+    dyldCacheHeader->progClosuresTrieAddr = 0;
+    dyldCacheHeader->progClosuresTrieSize = 0;
+    dyldCacheHeader->platform             = (uint8_t)_options.platform;
+    dyldCacheHeader->formatVersion        = dyld3::closure::kFormatVersion;
+    dyldCacheHeader->dylibsExpectedOnDisk = !_options.dylibsRemovedDuringMastering;
+    dyldCacheHeader->simulator            = _options.forSimulator;
+    dyldCacheHeader->locallyBuiltCache    = _options.isLocallyBuiltCache;
+    dyldCacheHeader->formatVersion        = dyld3::closure::kFormatVersion;
+    dyldCacheHeader->sharedRegionStart    = _archLayout->sharedMemoryStart;
+    dyldCacheHeader->sharedRegionSize     = _archLayout->sharedMemorySize;
+
+   // fill in mappings
+    dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + dyldCacheHeader->mappingOffset);
+    mappings[0].address    = _readExecuteRegion.unslidLoadAddress;
+    mappings[0].fileOffset = 0;
+    mappings[0].size       = _readExecuteRegion.sizeInUse;
+    mappings[0].maxProt    = VM_PROT_READ | VM_PROT_EXECUTE;
+    mappings[0].initProt   = VM_PROT_READ | VM_PROT_EXECUTE;
+    mappings[1].address    = _readWriteRegion.unslidLoadAddress;
+    mappings[1].fileOffset = _readExecuteRegion.sizeInUse;
+    mappings[1].size       = _readWriteRegion.sizeInUse;
+    mappings[1].maxProt    = VM_PROT_READ | VM_PROT_WRITE;
+    mappings[1].initProt   = VM_PROT_READ | VM_PROT_WRITE;
+    mappings[2].address    = _readOnlyRegion.unslidLoadAddress;
+    mappings[2].fileOffset = _readExecuteRegion.sizeInUse + _readWriteRegion.sizeInUse;
+    mappings[2].size       = _readOnlyRegion.sizeInUse;
+    mappings[2].maxProt    = VM_PROT_READ;
+    mappings[2].initProt   = VM_PROT_READ;
 
     // fill in branch pool addresses
-    uint64_t* p = (uint64_t*)((char*)_buffer + _buffer->header.branchPoolsOffset);
+    uint64_t* p = (uint64_t*)(_readExecuteRegion.buffer + dyldCacheHeader->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;
+    dyld_cache_image_info* images = (dyld_cache_image_info*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesOffset);
+    for (const DylibInfo& dylib : _sortedDylibs) {
+        const char* installName = dylib.input->mappedFile.mh->installName();
+        images->address = dylib.cacheLocation[0].dstCacheUnslidAddress;
         if ( _options.dylibsRemovedDuringMastering ) {
             images->modTime = 0;
             images->inode   = pathHash(installName);
         }
         else {
-            images->modTime = dylib.modTime;
-            images->inode   = dylib.inode;
+            images->modTime = dylib.input->mappedFile.modTime;
+            images->inode   = dylib.input->mappedFile.inode;
         }
-        uint32_t installNameOffsetInTEXT =  (uint32_t)(installName - (char*)dylib.mh);
-        images->pathFileOffset = (uint32_t)segs[0].dstCacheOffset + installNameOffsetInTEXT;
+        uint32_t installNameOffsetInTEXT =  (uint32_t)(installName - (char*)dylib.input->mappedFile.mh);
+        images->pathFileOffset = (uint32_t)dylib.cacheLocation[0].dstCacheFileOffset + installNameOffsetInTEXT;
         ++images;
     }
     // append aliases image records and strings
@@ -582,547 +1029,603 @@ void CacheBuilder::writeCacheHeader(const dyld_cache_mapping_info regions[3], co
     }
 */
     // 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());
+    dyld_cache_image_text_info* textImages = (dyld_cache_image_text_info*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesTextOffset);
+    uint32_t stringOffset = (uint32_t)(dyldCacheHeader->imagesTextOffset + sizeof(dyld_cache_image_text_info) * _sortedDylibs.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;
+    for (const DylibInfo& dylib : _sortedDylibs) {
+        dylib.input->mappedFile.mh->getUuid(textImages->uuid);
+        textImages->loadAddress     = dylib.cacheLocation[0].dstCacheUnslidAddress;
+        textImages->textSegmentSize = (uint32_t)dylib.cacheLocation[0].dstCacheSegmentSize;
         textImages->pathOffset      = stringOffset;
-        const char* installName = parser.installName();
-        ::strcpy((char*)_buffer + stringOffset, installName);
+        const char* installName = dylib.input->mappedFile.mh->installName();
+        ::strcpy((char*)_readExecuteRegion.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);
+    const dyld_cache_image_info* firstImage = (dyld_cache_image_info*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesOffset);
     assert(stringOffset <= (firstImage->address - mappings[0].address));
 }
 
-
-void CacheBuilder::copyRawSegments(const std::vector<DyldSharedCache::MappedMachO>& dylibs, const SegmentMapping& mapping)
+void CacheBuilder::copyRawSegments()
 {
-    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);
+    const bool log = false;
+    dispatch_apply(_sortedDylibs.size(), DISPATCH_APPLY_AUTO, ^(size_t index) {
+        const DylibInfo& dylib = _sortedDylibs[index];
+        for (const SegmentMappingInfo& info : dylib.cacheLocation) {
+            if (log) 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, info.dstSegment, info.dstCacheUnslidAddress, dylib.input->mappedFile.runtimePath.c_str());
+            ::memcpy(info.dstSegment, info.srcSegment, info.copySegmentSize);
+            if (uint64_t paddingSize = info.dstCacheSegmentSize - info.copySegmentSize) {
+                ::memset((char*)info.dstSegment + info.copySegmentSize, 0, paddingSize);
+            }
         }
-    }
-}
-
-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])
+void CacheBuilder::adjustAllImagesForNewSegmentLocations()
 {
-    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 std::vector<Diagnostics> diags;
+    diags.resize(_sortedDylibs.size());
 
-    __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;
+    if (_options.platform == dyld3::Platform::macOS) {
+        dispatch_apply(_sortedDylibs.size(), DISPATCH_APPLY_AUTO, ^(size_t index) {
+            const DylibInfo& dylib = _sortedDylibs[index];
+            adjustDylibSegments(dylib, diags[index]);
         });
-        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());
+    } else {
+        // Note this has to be done in serial because the LOH Tracker isn't thread safe
+        for (size_t index = 0; index != _sortedDylibs.size(); ++index) {
+            const DylibInfo& dylib = _sortedDylibs[index];
+            adjustDylibSegments(dylib, diags[index]);
         }
-        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);
+    for (const Diagnostics& diag : diags) {
+        if ( diag.hasError() ) {
+            _diagnostics.error("%s", diag.errorMessage().c_str());
+            break;
         }
-        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])
+void CacheBuilder::assignSegmentAddresses()
 {
     // 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 )
+    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);
+    startOffset += sizeof(dyld_cache_image_info) * _sortedDylibs.size();
+    startOffset += sizeof(dyld_cache_image_text_info) * _sortedDylibs.size();
+    for (const DylibInfo& dylib : _sortedDylibs) {
+        startOffset += (strlen(dylib.input->mappedFile.mh->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
-
+    _readExecuteRegion.buffer               = (uint8_t*)_fullAllocatedBuffer;
+    _readExecuteRegion.bufferSize           = 0;
+    _readExecuteRegion.sizeInUse            = 0;
+    _readExecuteRegion.unslidLoadAddress    = _archLayout->sharedMemoryStart;
+    _readExecuteRegion.cacheFileOffset      = 0;
+    __block uint64_t addr = _readExecuteRegion.unslidLoadAddress + 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) )
+    for (DylibInfo& dylib : _sortedDylibs) {
+        __block uint64_t textSegVmAddr = 0;
+        dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+            if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+                textSegVmAddr = segInfo.vmAddr;
+            if ( segInfo.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) ) {
+            if ( (_archLayout->branchPoolTextSize != 0) && ((addr + segInfo.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;
+            addr = align(addr, std::max((int)segInfo.p2align, (int)12));
+            uint64_t offsetInRegion = addr - _readExecuteRegion.unslidLoadAddress;
+            SegmentMappingInfo loc;
+            loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+            loc.segName                = segInfo.segName;
+            loc.dstSegment             = _readExecuteRegion.buffer + offsetInRegion;
+            loc.dstCacheUnslidAddress  = addr;
+            loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
+            loc.dstCacheSegmentSize    = (uint32_t)align(segInfo.sizeOfSections, 12);
+            loc.copySegmentSize        = (uint32_t)align(segInfo.sizeOfSections, 12);
+            loc.srcSegmentIndex        = segInfo.segIndex;
+            dylib.cacheLocation.push_back(loc);
+            addr += loc.dstCacheSegmentSize;
         });
     }
     // align TEXT region end
     uint64_t endTextAddress = align(addr, _archLayout->sharedRegionAlignP2);
-    regions[0].size         = endTextAddress - regions[0].address;
+    _readExecuteRegion.bufferSize = endTextAddress - _readExecuteRegion.unslidLoadAddress;
+    _readExecuteRegion.sizeInUse  = _readExecuteRegion.bufferSize;
 
     // 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;
+    _readWriteRegion.buffer               = (uint8_t*)_fullAllocatedBuffer + addr - _archLayout->sharedMemoryStart;
+    _readWriteRegion.bufferSize           = 0;
+    _readWriteRegion.sizeInUse            = 0;
+    _readWriteRegion.unslidLoadAddress    = addr;
+    _readWriteRegion.cacheFileOffset      = _readExecuteRegion.sizeInUse;
 
     // 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) )
+    for (DylibInfo& dylib : _sortedDylibs) {
+        __block uint64_t textSegVmAddr = 0;
+       dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+            if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+                textSegVmAddr = segInfo.vmAddr;
+            if ( segInfo.protections != (VM_PROT_READ | VM_PROT_WRITE) )
                 return;
-            if ( strcmp(segName, "__DATA_CONST") != 0 )
+            if ( strcmp(segInfo.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;
+            addr = align(addr, segInfo.p2align);
+            size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+            uint64_t offsetInRegion = addr - _readWriteRegion.unslidLoadAddress;
+            SegmentMappingInfo loc;
+            loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+            loc.segName                = segInfo.segName;
+            loc.dstSegment             = _readWriteRegion.buffer + offsetInRegion;
+            loc.dstCacheUnslidAddress  = addr;
+            loc.dstCacheFileOffset     = (uint32_t)(_readWriteRegion.cacheFileOffset + offsetInRegion);
+            loc.dstCacheSegmentSize    = (uint32_t)segInfo.sizeOfSections;
+            loc.copySegmentSize        = (uint32_t)copySize;
+            loc.srcSegmentIndex        = segInfo.segIndex;
+            dylib.cacheLocation.push_back(loc);
+            addr += loc.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) )
+    for (DylibInfo& dylib : _sortedDylibs) {
+        __block uint64_t textSegVmAddr = 0;
+       dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+            if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+                textSegVmAddr = segInfo.vmAddr;
+            if ( segInfo.protections != (VM_PROT_READ | VM_PROT_WRITE) )
                 return;
-            if ( strcmp(segName, "__DATA_CONST") == 0 )
+            if ( strcmp(segInfo.segName, "__DATA_CONST") == 0 )
                 return;
-            if ( strcmp(segName, "__DATA_DIRTY") == 0 )
+            if ( strcmp(segInfo.segName, "__DATA_DIRTY") == 0 )
                 return;
             if ( dataConstSegmentCount > 10 ) {
                 // Pack __DATA segments only if we also have __DATA_CONST segments
-                addr = align(addr, p2align);
+                addr = align(addr, segInfo.p2align);
             }
             else {
                 // Keep __DATA segments 4K or more aligned
-                addr = align(addr, std::max(p2align, (uint8_t)12));
+                addr = align(addr, std::max((int)segInfo.p2align, (int)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;
+            size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+            uint64_t offsetInRegion = addr - _readWriteRegion.unslidLoadAddress;
+            SegmentMappingInfo loc;
+            loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+            loc.segName                = segInfo.segName;
+            loc.dstSegment             = _readWriteRegion.buffer + offsetInRegion;
+            loc.dstCacheUnslidAddress  = addr;
+            loc.dstCacheFileOffset     = (uint32_t)(_readWriteRegion.cacheFileOffset + offsetInRegion);
+            loc.dstCacheSegmentSize    = (uint32_t)segInfo.sizeOfSections;
+            loc.copySegmentSize        = (uint32_t)copySize;
+            loc.srcSegmentIndex        = segInfo.segIndex;
+            dylib.cacheLocation.push_back(loc);
+            addr += loc.dstCacheSegmentSize;
         });
     }
 
-    // layout all __DATA_DIRTY segments, sorted
+    // layout all __DATA_DIRTY segments, sorted (FIXME)
+       const size_t dylibCount = _sortedDylibs.size();
+    uint32_t dirtyDataSortIndexes[dylibCount];
+    for (size_t i=0; i < dylibCount; ++i)
+        dirtyDataSortIndexes[i] = (uint32_t)i;
+    std::sort(&dirtyDataSortIndexes[0], &dirtyDataSortIndexes[dylibCount], [&](const uint32_t& a, const uint32_t& b) {
+        const auto& orderA = _options.dirtyDataSegmentOrdering.find(_sortedDylibs[a].input->mappedFile.runtimePath);
+        const auto& orderB = _options.dirtyDataSegmentOrdering.find(_sortedDylibs[b].input->mappedFile.runtimePath);
+        bool foundA = (orderA != _options.dirtyDataSegmentOrdering.end());
+        bool foundB = (orderB != _options.dirtyDataSegmentOrdering.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 _sortedDylibs[a].input->mappedFile.runtimePath < _sortedDylibs[b].input->mappedFile.runtimePath;
+    });
     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) )
+    for (size_t i=0; i < dylibCount; ++i) {
+        DylibInfo& dylib  = _sortedDylibs[dirtyDataSortIndexes[i]];
+        __block uint64_t textSegVmAddr = 0;
+        dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+            if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+                textSegVmAddr = segInfo.vmAddr;
+            if ( segInfo.protections != (VM_PROT_READ | VM_PROT_WRITE) )
                 return;
-            if ( strcmp(segName, "__DATA_DIRTY") != 0 )
+            if ( strcmp(segInfo.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;
+            addr = align(addr, segInfo.p2align);
+            size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+            uint64_t offsetInRegion = addr - _readWriteRegion.unslidLoadAddress;
+            SegmentMappingInfo loc;
+            loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+            loc.segName                = segInfo.segName;
+            loc.dstSegment             = _readWriteRegion.buffer + offsetInRegion;
+            loc.dstCacheUnslidAddress  = addr;
+            loc.dstCacheFileOffset     = (uint32_t)(_readWriteRegion.cacheFileOffset + offsetInRegion);
+            loc.dstCacheSegmentSize    = (uint32_t)segInfo.sizeOfSections;
+            loc.copySegmentSize        = (uint32_t)copySize;
+            loc.srcSegmentIndex        = segInfo.segIndex;
+            dylib.cacheLocation.push_back(loc);
+            addr += loc.dstCacheSegmentSize;
         });
     }
 
     // align DATA region end
     uint64_t endDataAddress = align(addr, _archLayout->sharedRegionAlignP2);
-    regions[1].size         = endDataAddress - regions[1].address;
+    _readWriteRegion.bufferSize   = endDataAddress - _readWriteRegion.unslidLoadAddress;
+    _readWriteRegion.sizeInUse    = _readWriteRegion.bufferSize;
 
     // 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;
+    _readOnlyRegion.buffer               = (uint8_t*)_fullAllocatedBuffer + addr - _archLayout->sharedMemoryStart;
+    _readOnlyRegion.bufferSize           = 0;
+    _readOnlyRegion.sizeInUse            = 0;
+    _readOnlyRegion.unslidLoadAddress    = addr;
+    _readOnlyRegion.cacheFileOffset      = _readWriteRegion.cacheFileOffset + _readWriteRegion.sizeInUse;
 
     // 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;
+        size_t slideInfoSize = sizeof(dyld_cache_slide_info);
+        slideInfoSize = std::max(slideInfoSize, sizeof(dyld_cache_slide_info2));
+        slideInfoSize = std::max(slideInfoSize, sizeof(dyld_cache_slide_info3));
+        slideInfoSize = std::max(slideInfoSize, sizeof(dyld_cache_slide_info4));
+        _slideInfoBufferSizeAllocated = align(slideInfoSize + (_readWriteRegion.sizeInUse/4096) * _archLayout->slideInfoBytesPerPage, _archLayout->sharedRegionAlignP2);
+        _slideInfoFileOffset = _readOnlyRegion.cacheFileOffset;
         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 )
+    for (DylibInfo& dylib : _sortedDylibs) {
+        __block uint64_t textSegVmAddr = 0;
+        dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+            if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+                textSegVmAddr = segInfo.vmAddr;
+            if ( segInfo.protections != VM_PROT_READ )
                 return;
-            if ( strcmp(segName, "__LINKEDIT") == 0 )
+            if ( strcmp(segInfo.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;
+            addr = align(addr, std::max((int)segInfo.p2align, (int)12));
+            uint64_t offsetInRegion = addr - _readOnlyRegion.unslidLoadAddress;
+            SegmentMappingInfo loc;
+            loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+            loc.segName                = segInfo.segName;
+            loc.dstSegment             = _readOnlyRegion.buffer + offsetInRegion;
+            loc.dstCacheUnslidAddress  = addr;
+            loc.dstCacheFileOffset     = (uint32_t)(_readOnlyRegion.cacheFileOffset + offsetInRegion);
+            loc.dstCacheSegmentSize    = (uint32_t)align(segInfo.sizeOfSections, 12);
+            loc.copySegmentSize        = (uint32_t)segInfo.sizeOfSections;
+            loc.srcSegmentIndex        = segInfo.segIndex;
+            dylib.cacheLocation.push_back(loc);
+            addr += loc.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 )
+    // layout all LINKEDIT segments (after other read-only segments), aligned to 16KB
+    addr = align(addr, 14);
+    _nonLinkEditReadOnlySize =  addr - _readOnlyRegion.unslidLoadAddress;
+    for (DylibInfo& dylib : _sortedDylibs) {
+        __block uint64_t textSegVmAddr = 0;
+        dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+            if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+                textSegVmAddr = segInfo.vmAddr;
+            if ( segInfo.protections != VM_PROT_READ )
                 return;
-            if ( strcmp(segName, "__LINKEDIT") != 0 )
+            if ( strcmp(segInfo.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;
+            addr = align(addr, std::max((int)segInfo.p2align, (int)12));
+            size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+            uint64_t offsetInRegion = addr - _readOnlyRegion.unslidLoadAddress;
+            SegmentMappingInfo loc;
+            loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+            loc.segName                = segInfo.segName;
+            loc.dstSegment             = _readOnlyRegion.buffer + offsetInRegion;
+            loc.dstCacheUnslidAddress  = addr;
+            loc.dstCacheFileOffset     = (uint32_t)(_readOnlyRegion.cacheFileOffset + offsetInRegion);
+            loc.dstCacheSegmentSize    = (uint32_t)align(segInfo.sizeOfSections, 12);
+            loc.copySegmentSize        = (uint32_t)copySize;
+            loc.srcSegmentIndex        = segInfo.segIndex;
+            dylib.cacheLocation.push_back(loc);
+            addr += loc.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;
+    // align r/o region end
+    uint64_t endReadOnlyAddress = align(addr, _archLayout->sharedRegionAlignP2);
+    _readOnlyRegion.bufferSize  = endReadOnlyAddress - _readOnlyRegion.unslidLoadAddress;
+    _readOnlyRegion.sizeInUse   = _readOnlyRegion.bufferSize;
+
+    //fprintf(stderr, "RX region=%p -> %p, logical addr=0x%llX\n", _readExecuteRegion.buffer, _readExecuteRegion.buffer+_readExecuteRegion.bufferSize, _readExecuteRegion.unslidLoadAddress);
+    //fprintf(stderr, "RW region=%p -> %p, logical addr=0x%llX\n", _readWriteRegion.buffer,   _readWriteRegion.buffer+_readWriteRegion.bufferSize, _readWriteRegion.unslidLoadAddress);
+    //fprintf(stderr, "RO region=%p -> %p, logical addr=0x%llX\n", _readOnlyRegion.buffer,    _readOnlyRegion.buffer+_readOnlyRegion.bufferSize, _readOnlyRegion.unslidLoadAddress);
+
+    // sort SegmentMappingInfo for each image to be in the same order as original segments
+    for (DylibInfo& dylib : _sortedDylibs) {
+        std::sort(dylib.cacheLocation.begin(), dylib.cacheLocation.end(), [&](const SegmentMappingInfo& a, const SegmentMappingInfo& b) {
+            return a.srcSegmentIndex < b.srcSegmentIndex;
+        });
+    }
+}
+
+void CacheBuilder::markPaddingInaccessible()
+{
+    // region between RX and RW
+    uint8_t* startPad1 = _readExecuteRegion.buffer+_readExecuteRegion.sizeInUse;
+    uint8_t* endPad1   = _readWriteRegion.buffer;
+    ::vm_protect(mach_task_self(), (vm_address_t)startPad1, endPad1-startPad1, false, 0);
+
+    // region between RW and RO
+    uint8_t* startPad2 = _readWriteRegion.buffer+_readWriteRegion.sizeInUse;
+    uint8_t* endPad2   = _readOnlyRegion.buffer;
+    ::vm_protect(mach_task_self(), (vm_address_t)startPad2, endPad2-startPad2, false, 0);
+}
+
+
+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   = "???";
+    uint64_t unslidVmAddr = ((uint8_t*)contentPtr - _readExecuteRegion.buffer) + _readExecuteRegion.unslidLoadAddress;
+    const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    cache->forEachImage(^(const mach_header* mh, const char* installName) {
+        ((dyld3::MachOLoaded*)mh)->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool &stop) {
+            if ( (unslidVmAddr >= info.vmAddr) && (unslidVmAddr < (info.vmAddr+info.vmSize)) ) {
+                foundDylibName = installName;
+                foundSegName   = info.segName;
+                stop           = true;
+            }
+        });
+    });
+}
+
+
+template <typename P>
+bool CacheBuilder::makeRebaseChainV2(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;
+    }
+    //fprintf(stderr, "  too big delta = %d, lastOffset=0x%03X, offset=0x%03X\n", 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 (unsigned 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::addPageStartsV2(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(uint32_t 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 ( !makeRebaseChainV2<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 v2 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(const bool bitmap[], unsigned dataPageCount)
+{
+    typedef typename P::uint_t    pint_t;
+    typedef typename P::E         E;
+    const uint32_t pageSize = 4096;
 
-    // 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;
-    }
+    // fill in fixed info
+    assert(_slideInfoFileOffset != 0);
+    dyld_cache_slide_info2* info = (dyld_cache_slide_info2*)_readOnlyRegion.buffer;
+    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
 
-    // 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;
-        });
+    // set page starts and extras for each page
+    std::vector<uint16_t> pageStarts;
+    std::vector<uint16_t> pageExtras;
+    pageStarts.reserve(dataPageCount);
+    uint8_t* pageContent = _readWriteRegion.buffer;
+    const bool* bitmapForPage = bitmap;
+    for (unsigned i=0; i < dataPageCount; ++i) {
+        //warning("page[%d]", i);
+        addPageStartsV2<P>(pageContent, bitmapForPage, info, pageStarts, pageExtras);
+        if ( _diagnostics.hasError() ) {
+            return;
+        }
+        pageContent += pageSize;
+        bitmapForPage += (sizeof(bool)*(pageSize/4));
     }
 
-    return result;
+    // 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
+    uint64_t slideInfoSize = align(info->page_extras_offset + pageExtras.size()*sizeof(uint16_t), _archLayout->sharedRegionAlignP2);
+    if ( slideInfoSize > _slideInfoBufferSizeAllocated ) {
+        _diagnostics.error("kernel slide info overflow buffer");
+    }
+    ((dyld_cache_header*)_readExecuteRegion.buffer)->slideInfoSize = slideInfoSize;
+    //fprintf(stderr, "pageCount=%u, page_starts_count=%lu, page_extras_count=%lu\n", dataPageCount, pageStarts.size(), pageExtras.size());
 }
 
-uint64_t CacheBuilder::pathHash(const char* path)
+// fits in to int16_t
+static bool smallValue(uint64_t value)
 {
-    uint64_t sum = 0;
-    for (const char* s=path; *s != '\0'; ++s)
-        sum += sum*4 + *s;
-    return sum;
+    uint32_t high = (value & 0xFFFF8000);
+    return (high == 0) || (high == 0xFFFF8000);
 }
 
-
-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)
+bool CacheBuilder::makeRebaseChainV4(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t offset, const dyld_cache_slide_info4* info)
 {
     typedef typename P::uint_t     pint_t;
 
@@ -1152,7 +1655,7 @@ bool CacheBuilder::makeRebaseChain(uint8_t* pageContent, uint16_t lastLocationOf
         P::setP(*lastLoc, newLastValue);
         return true;
     }
-    //warning("  too big delta = %d, lastOffset=0x%03X, offset=0x%03X", offset - lastLocationOffset, lastLocationOffset, offset);
+    //fprintf(stderr, "  too big delta = %d, lastOffset=0x%03X, offset=0x%03X\n", offset - lastLocationOffset, lastLocationOffset, offset);
 
     // distance between rebase locations is too far
     // see if we can make a chain from non-rebase locations
@@ -1162,7 +1665,7 @@ bool CacheBuilder::makeRebaseChain(uint8_t* pageContent, uint16_t lastLocationOf
         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 ) {
+            if ( smallValue(value) ) {
                 // Steal values of 0 to be used in the rebase chain
                 nonRebaseLocationOffsets[nrIndex] = i+j;
                 break;
@@ -1171,7 +1674,8 @@ bool CacheBuilder::makeRebaseChain(uint8_t* pageContent, uint16_t lastLocationOf
         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);
+            //fprintf(stderr, "   no way to make non-rebase delta chain, terminate off=0x%03X, old value=0x%08lX, new value=0x%08lX\n",
+            //                lastLocationOffset, (long)lastValue, (long)newValue);
             P::setP(*lastLoc, newValue);
             return false;
         }
@@ -1182,15 +1686,15 @@ bool CacheBuilder::makeRebaseChain(uint8_t* pageContent, uint16_t lastLocationOf
     // 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) {
+    for (unsigned 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);
+        if ( smallValue(value) )
+            newValue = (value & valueMask) | (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);
@@ -1201,8 +1705,8 @@ bool CacheBuilder::makeRebaseChain(uint8_t* pageContent, uint16_t lastLocationOf
     uint32_t delta3 = offset - prevOffset;
     pint_t value = (pint_t)P::getP(*prevLoc);
     pint_t newValue;
-    if ( value == 0 )
-        newValue = (delta3 << deltaShift);
+    if ( smallValue(value) )
+        newValue = (value & valueMask) | (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);
@@ -1213,7 +1717,7 @@ bool CacheBuilder::makeRebaseChain(uint8_t* pageContent, uint16_t lastLocationOf
 
 
 template <typename P>
-void CacheBuilder::addPageStarts(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info2* info,
+void CacheBuilder::addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info4* info,
                                 std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras)
 {
     typedef typename P::uint_t     pint_t;
@@ -1223,26 +1727,26 @@ void CacheBuilder::addPageStarts(uint8_t* pageContent, const bool bitmap[], cons
     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 startValue = DYLD_CACHE_SLIDE4_PAGE_NO_REBASE;
     uint16_t lastLocationOffset = 0xFFFF;
-    for(int i=0; i < pageSize/4; ++i) {
+    for(uint32_t i=0; i < pageSize/4; ++i) {
         unsigned offset = i*4;
         if ( bitmap[i] ) {
-            if ( startValue == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) {
+            if ( startValue == DYLD_CACHE_SLIDE4_PAGE_NO_REBASE ) {
                 // found first rebase location in page
                 startValue = i;
             }
-            else if ( !makeRebaseChain<P>(pageContent, lastLocationOffset, offset, info) ) {
+            else if ( !makeRebaseChainV4<P>(pageContent, lastLocationOffset, offset, info) ) {
                 // can't record all rebasings in one chain
-                if ( (startValue & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0 ) {
+                if ( (startValue & DYLD_CACHE_SLIDE4_PAGE_USE_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");
+                    if ( indexInExtras >= DYLD_CACHE_SLIDE4_PAGE_INDEX ) {
+                        _diagnostics.error("rebase overflow in v4 page extras");
                         return;
                     }
                     pageExtras.push_back(startValue);
-                    startValue = indexInExtras | DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA;
+                    startValue = indexInExtras | DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA;
                 }
                 pageExtras.push_back(i);
             }
@@ -1256,55 +1760,26 @@ void CacheBuilder::addPageStarts(uint8_t* pageContent, const bool bitmap[], cons
         pint_t newValue = ((lastValue - valueAdd) & valueMask);
         P::setP(*lastLoc, newValue);
     }
-    if ( startValue & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) {
+    if ( startValue & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA ) {
         // add end bit to extras
-        pageExtras.back() |= DYLD_CACHE_SLIDE_PAGE_ATTR_END;
+        pageExtras.back() |= DYLD_CACHE_SLIDE4_PAGE_EXTRA_END;
     }
     pageStarts.push_back(startValue);
 }
 
+
+
 template <typename P>
-void CacheBuilder::writeSlideInfoV2()
+void CacheBuilder::writeSlideInfoV4(const bool bitmap[], unsigned dataPageCount)
 {
     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;
+    dyld_cache_slide_info4* info = (dyld_cache_slide_info4*)_readOnlyRegion.buffer;
+    info->version    = 4;
     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
@@ -1312,25 +1787,21 @@ void CacheBuilder::writeSlideInfoV2()
     // 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;;
+    pageStarts.reserve(dataPageCount);
+    uint8_t* pageContent = _readWriteRegion.buffer;
     const bool* bitmapForPage = bitmap;
-    for (unsigned i=0; i < pageCount; ++i) {
-        //warning("page[%d]", i);
-        addPageStarts<P>(pageContent, bitmapForPage, info, pageStarts, pageExtras);
+    for (unsigned i=0; i < dataPageCount; ++i) {
+        addPageStartsV4<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_offset = sizeof(dyld_cache_slide_info4);
     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_offset = (unsigned)(sizeof(dyld_cache_slide_info4)+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);
@@ -1339,11 +1810,12 @@ void CacheBuilder::writeSlideInfoV2()
     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");
+    uint64_t slideInfoSize = align(info->page_extras_offset + pageExtras.size()*sizeof(uint16_t), _archLayout->sharedRegionAlignP2);
+    if ( slideInfoSize > _slideInfoBufferSizeAllocated ) {
+        _diagnostics.error("kernel slide info v4 overflow buffer");
     }
-    //warning("pageCount=%u, page_starts_count=%lu, page_extras_count=%lu", pageCount, pageStarts.size(), pageExtras.size());
+    ((dyld_cache_header*)_readExecuteRegion.buffer)->slideInfoSize = slideInfoSize;
+    //fprintf(stderr, "pageCount=%u, page_starts_count=%lu, page_extras_count=%lu\n", dataPageCount, pageStarts.size(), pageExtras.size());
 }
 
 
@@ -1405,50 +1877,100 @@ void CacheBuilder::writeSlideInfoV1()
 
 */
 
-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;
+uint16_t CacheBuilder::pageStartV3(uint8_t* pageContent, uint32_t pageSize, const bool bitmap[])
+{
+    const int maxPerPage = pageSize / 4;
+    uint16_t result = DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE;
+    dyld3::MachOLoaded::ChainedFixupPointerOnDisk* lastLoc = nullptr;
+    for (int i=0; i < maxPerPage; ++i) {
+        if ( bitmap[i] ) {
+            if ( result == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE ) {
+                // found first rebase location in page
+                result = i * 4;
             }
-
-            if (illegalSectionSize) {
-                _diagnostics.error("FIPS section %s/%s extends beyond the end of the segment", segName, sectionName);
-                return;
+            dyld3::MachOLoaded::ChainedFixupPointerOnDisk* loc = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)(pageContent + i*4);;
+            if ( lastLoc != nullptr ) {
+                // update chain (original chain may be wrong because of segment packing)
+                lastLoc->plainRebase.next = loc - lastLoc;
             }
+            lastLoc = loc;
+        }
+    }
+    if ( lastLoc != nullptr ) {
+        // mark last one as end of chain
+        lastLoc->plainRebase.next = 0;
+       }
+    return result;
+}
 
-            //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;
-        });
+
+void CacheBuilder::writeSlideInfoV3(const bool bitmap[], unsigned dataPageCount)
+{
+       const uint32_t pageSize = 4096;
+
+    // fill in fixed info
+    assert(_slideInfoFileOffset != 0);
+    dyld_cache_slide_info3* info = (dyld_cache_slide_info3*)_readOnlyRegion.buffer;
+    info->version           = 3;
+    info->page_size         = pageSize;
+    info->page_starts_count = dataPageCount;
+    info->auth_value_add    = _archLayout->sharedMemoryStart;
+    
+    // fill in per-page starts
+    uint8_t* pageContent = _readWriteRegion.buffer;
+    const bool* bitmapForPage = bitmap;
+    for (unsigned i=0; i < dataPageCount; ++i) {
+        info->page_starts[i] = pageStartV3(pageContent, pageSize, bitmapForPage);
+        pageContent += pageSize;
+        bitmapForPage += (sizeof(bool)*(pageSize/4));
+    }
+
+    // update header with final size
+    dyld_cache_header* dyldCacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer;
+    dyldCacheHeader->slideInfoSize = align(__offsetof(dyld_cache_slide_info3, page_starts[dataPageCount]), _archLayout->sharedRegionAlignP2);
+    if ( dyldCacheHeader->slideInfoSize > _slideInfoBufferSizeAllocated ) {
+        _diagnostics.error("kernel slide info overflow buffer");
+    }
+}
+
+
+void CacheBuilder::fipsSign()
+{
+    // find libcorecrypto.dylib in cache being built
+    DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    __block const dyld3::MachOLoaded* ml = nullptr;
+    dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
+        if ( strcmp(installName, "/usr/lib/system/libcorecrypto.dylib") == 0 )
+            ml = (dyld3::MachOLoaded*)mh;
     });
+    if ( ml == nullptr ) {
+        _diagnostics.warning("Could not find libcorecrypto.dylib, skipping FIPS sealing");
+        return;
+    }
+
+    // find location in libcorecrypto.dylib to store hash of __text section
+    uint64_t hashStoreSize;
+    const void* hashStoreLocation = ml->findSectionContent("__TEXT", "__fips_hmacs", hashStoreSize);
+    if ( hashStoreLocation == nullptr ) {
+        _diagnostics.warning("Could not find __TEXT/__fips_hmacs section in libcorecrypto.dylib, skipping FIPS sealing");
+        return;
+    }
+    if ( hashStoreSize != 32 ) {
+        _diagnostics.warning("__TEXT/__fips_hmacs section in libcorecrypto.dylib is not 32 bytes in size, skipping FIPS sealing");
+        return;
+    }
 
-    if (!found) {
-        _diagnostics.warning("Could not find /usr/lib/system/libcorecrypto.dylib, skipping FIPS sealing");
+    // compute hmac hash of __text section
+    uint64_t textSize;
+    const void* textLocation = ml->findSectionContent("__TEXT", "__text", textSize);
+    if ( textLocation == nullptr ) {
+        _diagnostics.warning("Could not find __TEXT/__text section in libcorecrypto.dylib, skipping FIPS sealing");
+        return;
     }
+    unsigned char hmac_key = 0;
+    CCHmac(kCCHmacAlgSHA256, &hmac_key, 1, textLocation, textSize, (void*)hashStoreLocation); // store hash directly into hashStoreLocation
 }
 
 void CacheBuilder::codeSign()
@@ -1487,9 +2009,7 @@ void CacheBuilder::codeSign()
             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;
+    size_t          inBbufferSize = _readExecuteRegion.sizeInUse+_readWriteRegion.sizeInUse+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse;
 
     // layout code signature contents
     uint32_t blobCount     = agile ? 4 : 3;
@@ -1510,14 +2030,18 @@ void CacheBuilder::codeSign()
     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);
+    // allocate space for blob
+    vm_address_t codeSigAlloc;
+    if ( vm_allocate(mach_task_self(), &codeSigAlloc, sigSize, VM_FLAGS_ANYWHERE) != 0 ) {
+        _diagnostics.error("could not allocate code signature buffer");
         return;
     }
+    _codeSignatureRegion.buffer     = (uint8_t*)codeSigAlloc;
+    _codeSignatureRegion.bufferSize = sigSize;
+    _codeSignatureRegion.sizeInUse  = sigSize;
 
     // create overall code signature which is a superblob
-    CS_SuperBlob* sb = reinterpret_cast<CS_SuperBlob*>(csBuffer);
+    CS_SuperBlob* sb = reinterpret_cast<CS_SuperBlob*>(_codeSignatureRegion.buffer);
     sb->magic           = htonl(CSMAGIC_EMBEDDED_SIGNATURE);
     sb->length          = htonl(sbSize);
     sb->count           = htonl(blobCount);
@@ -1560,10 +2084,9 @@ void CacheBuilder::codeSign()
     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
+    cd->execSegBase     = htonll(_readExecuteRegion.cacheFileOffset); // base of TEXT segment
+    cd->execSegLimit    = htonll(_readExecuteRegion.sizeInUse);       // size of TEXT segment
+    cd->execSegFlags    = 0;                                          // not a main binary
 
     // initialize dynamic fields of Code Directory
     strcpy((char*)cd + idOffset, cacheIdentifier.c_str());
@@ -1624,22 +2147,54 @@ void CacheBuilder::codeSign()
     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;
+    dyld_cache_header* cache = (dyld_cache_header*)_readExecuteRegion.buffer;
+    cache->codeSignatureOffset  = inBbufferSize;
+    cache->codeSignatureSize    = sigSize;
+
+    const uint32_t rwSlotStart = (uint32_t)(_readExecuteRegion.sizeInUse / CS_PAGE_SIZE);
+    const uint32_t roSlotStart = (uint32_t)(rwSlotStart + _readWriteRegion.sizeInUse / CS_PAGE_SIZE);
+    const uint32_t localsSlotStart = (uint32_t)(roSlotStart + _readOnlyRegion.sizeInUse / CS_PAGE_SIZE);
+    auto codeSignPage = ^(size_t i) {
+        const uint8_t* code = nullptr;
+        // move to correct region
+        if ( i < rwSlotStart )
+            code = _readExecuteRegion.buffer + (i * CS_PAGE_SIZE);
+        else if ( i >= rwSlotStart && i < roSlotStart )
+            code = _readWriteRegion.buffer + ((i - rwSlotStart) * CS_PAGE_SIZE);
+        else if ( i >= roSlotStart && i < localsSlotStart )
+            code = _readOnlyRegion.buffer + ((i - roSlotStart) * CS_PAGE_SIZE);
+        else
+            code = _localSymbolsRegion.buffer + ((i - localsSlotStart) * CS_PAGE_SIZE);
 
-    // compute hashes
-    const uint8_t* code = inBuffer;
-    for (uint32_t i=0; i < slotCount; ++i) {
-        CCDigest(dscDigestFormat, code, CS_PAGE_SIZE, hashSlot);
-        hashSlot += dscHashSize;
+        CCDigest(dscDigestFormat, code, CS_PAGE_SIZE, hashSlot + (i * dscHashSize));
 
         if ( agile ) {
-            CCDigest(kCCDigestSHA256, code, CS_PAGE_SIZE, hash256Slot);
-            hash256Slot += CS_HASH_SIZE_SHA256;
+            CCDigest(kCCDigestSHA256, code, CS_PAGE_SIZE, hash256Slot + (i * CS_HASH_SIZE_SHA256));
         }
-        code += CS_PAGE_SIZE;
+    };
+
+    // compute hashes
+    dispatch_apply(slotCount, DISPATCH_APPLY_AUTO, ^(size_t i) {
+        codeSignPage(i);
+    });
+
+    // Now that we have a code signature, compute a UUID from it.
+
+    // Clear existing UUID, then MD5 whole cache buffer.
+    {
+        uint8_t* uuidLoc = cache->uuid;
+        assert(uuid_is_null(uuidLoc));
+        static_assert(offsetof(dyld_cache_header, uuid) / CS_PAGE_SIZE == 0, "uuid is expected in the first page of the cache");
+        CC_MD5((const void*)cd, (unsigned)cdSize, 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;
+
+        // Now codesign page 0 again
+        codeSignPage(0);
     }
 
     // hash of entire code directory (cdHash) uses same hash as each page
@@ -1656,9 +2211,6 @@ void CacheBuilder::codeSign()
     else {
         memset(_cdHashSecond, 0, 20);
     }
-
-    // increase file size to include newly append code signature
-    _currentFileSize += sigSize;
 }
 
 const bool CacheBuilder::agileSignature()
@@ -1684,92 +2236,345 @@ const std::string CacheBuilder::cdHashSecond()
     return cdHash(_cdHashSecond);
 }
 
-void CacheBuilder::addCachedDylibsImageGroup(dyld3::ImageProxyGroup* dylibGroup)
+
+void CacheBuilder::buildImageArray(std::vector<DyldSharedCache::FileAlias>& aliases)
 {
-    const dyld3::launch_cache::binary_format::ImageGroup* groupBinary = dylibGroup->makeImageGroupBinary(_diagnostics, _s_neverStubEliminate);
-    if (!groupBinary)
+    typedef dyld3::closure::ClosureBuilder::CachedDylibInfo         CachedDylibInfo;
+    typedef dyld3::closure::Image::PatchableExport::PatchLocation   PatchLocation;
+    typedef uint64_t                                                CacheOffset;
+
+    // convert STL data structures to simple arrays to passe to makeDyldCacheImageArray()
+    __block std::vector<CachedDylibInfo> dylibInfos;
+    __block std::unordered_map<dyld3::closure::ImageNum, const dyld3::MachOLoaded*> imageNumToML;
+    DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    cache->forEachImage(^(const mach_header* mh, const char* installName) {
+        uint64_t mtime;
+        uint64_t inode;
+        cache->getIndexedImageEntry((uint32_t)dylibInfos.size(), mtime, inode);
+        CachedDylibInfo entry;
+        entry.fileInfo.fileContent  = mh;
+        entry.fileInfo.path         = installName;
+        entry.fileInfo.sliceOffset  = 0;
+        entry.fileInfo.inode        = inode;
+        entry.fileInfo.mtime        = mtime;
+        dylibInfos.push_back(entry);
+        imageNumToML[(dyld3::closure::ImageNum)(dylibInfos.size())] = (dyld3::MachOLoaded*)mh;
+    });
+
+    // Convert symlinks from STL to simple char pointers.
+    std::vector<dyld3::closure::ClosureBuilder::CachedDylibAlias> dylibAliases;
+    dylibAliases.reserve(aliases.size());
+    for (const auto& alias : aliases)
+        dylibAliases.push_back({ alias.realPath.c_str(), alias.aliasPath.c_str() });
+
+
+    __block std::unordered_map<const dyld3::MachOLoaded*, std::set<CacheOffset>>    dylibToItsExports;
+    __block std::unordered_map<CacheOffset, std::vector<PatchLocation>>             exportsToUses;
+    __block std::unordered_map<CacheOffset, const char*>                            exportsToName;
+
+    dyld3::closure::ClosureBuilder::CacheDylibsBindingHandlers handlers;
+
+    handlers.chainedBind = ^(dyld3::closure::ImageNum, const dyld3::MachOLoaded* imageLoadAddress,
+                             const dyld3::Array<uint64_t>& starts,
+                             const dyld3::Array<dyld3::closure::Image::ResolvedSymbolTarget>& targets,
+                             const dyld3::Array<dyld3::closure::ClosureBuilder::ResolvedTargetInfo>& targetInfos) {
+        for (uint64_t start : starts) {
+            dyld3::closure::Image::forEachChainedFixup((void*)imageLoadAddress, start, ^(uint64_t* fixupLoc, dyld3::MachOLoaded::ChainedFixupPointerOnDisk fixupInfo, bool& stop) {
+                 // record location in aslr tracker so kernel can slide this on page-in
+                _aslrTracker.add(fixupLoc);
+
+                // if bind, record info for patch table and convert to rebase
+                if ( fixupInfo.plainBind.bind ) {
+                    dyld3::closure::Image::ResolvedSymbolTarget               target     = targets[fixupInfo.plainBind.ordinal];
+                    const dyld3::closure::ClosureBuilder::ResolvedTargetInfo& targetInfo = targetInfos[fixupInfo.plainBind.ordinal];
+                    dyld3::MachOLoaded::ChainedFixupPointerOnDisk*            loc;
+                    uint64_t                                                  offsetInCache;
+                    switch ( target.sharedCache.kind ) {
+                        case dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache:
+                            loc = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)fixupLoc;
+                            offsetInCache = target.sharedCache.offset - targetInfo.addend;
+                            dylibToItsExports[targetInfo.foundInDylib].insert(offsetInCache);
+                            exportsToName[offsetInCache] = targetInfo.foundSymbolName;
+                            if ( fixupInfo.authBind.auth ) {
+                                // turn this auth bind into an auth rebase into the cache
+                                loc->authRebase.bind = 0;
+                                loc->authRebase.target = target.sharedCache.offset;
+                                exportsToUses[offsetInCache].push_back(PatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, targetInfo.addend, *loc));
+                            }
+                            else {
+                                // turn this plain bind into an plain rebase into the cache
+                                loc->plainRebase.bind = 0;
+                                loc->plainRebase.target = _archLayout->sharedMemoryStart + target.sharedCache.offset;
+                                exportsToUses[offsetInCache].push_back(PatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, targetInfo.addend));
+                            }
+                            break;
+                        case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute:
+                             if ( _archLayout->is64 )
+                                *((uint64_t*)fixupLoc) = target.absolute.value;
+                            else
+                                *((uint32_t*)fixupLoc) = (uint32_t)(target.absolute.value);
+                            // don't record absolute targets for ASLR
+                            break;
+                        default:
+                            assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache");
+                    }
+                }
+            });
+        }
+    };
+
+    handlers.rebase = ^(dyld3::closure::ImageNum imageNum, const dyld3::MachOLoaded* imageToFix, uint32_t runtimeOffset) {
+        // record location in aslr tracker so kernel can slide this on page-in
+        uint8_t* fixupLoc = (uint8_t*)imageToFix+runtimeOffset;
+        _aslrTracker.add(fixupLoc);
+    };
+
+    handlers.bind = ^(dyld3::closure::ImageNum imageNum, const dyld3::MachOLoaded* mh,
+                      uint32_t runtimeOffset, dyld3::closure::Image::ResolvedSymbolTarget target,
+                      const dyld3::closure::ClosureBuilder::ResolvedTargetInfo& targetInfo) {
+        uint8_t* fixupLoc = (uint8_t*)mh+runtimeOffset;
+
+        // binder is called a second time for weak_bind info, which we ignore when building cache
+        const bool weakDefUseAlreadySet = targetInfo.weakBindCoalese && _aslrTracker.has(fixupLoc);
+
+        // do actual bind that sets pointer in image to unslid target address
+        uint64_t offsetInCache;
+        switch ( target.sharedCache.kind ) {
+            case dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache:
+                offsetInCache = target.sharedCache.offset - targetInfo.addend;
+                dylibToItsExports[targetInfo.foundInDylib].insert(offsetInCache);
+                exportsToUses[offsetInCache].push_back(PatchLocation(fixupLoc - _readExecuteRegion.buffer, targetInfo.addend));
+                exportsToName[offsetInCache] = targetInfo.foundSymbolName;
+                if ( !weakDefUseAlreadySet ) {
+                    if ( _archLayout->is64 )
+                        *((uint64_t*)fixupLoc) = _archLayout->sharedMemoryStart + target.sharedCache.offset;
+                    else
+                        *((uint32_t*)fixupLoc) = (uint32_t)(_archLayout->sharedMemoryStart + target.sharedCache.offset);
+                    // record location in aslr tracker so kernel can slide this on page-in
+                    _aslrTracker.add(fixupLoc);
+                }
+                break;
+            case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute:
+                 if ( _archLayout->is64 )
+                    *((uint64_t*)fixupLoc) = target.absolute.value;
+                else
+                    *((uint32_t*)fixupLoc) = (uint32_t)(target.absolute.value);
+                // don't record absolute targets for ASLR
+                // HACK: Split seg may have added a target.  Remove it
+                _aslrTracker.remove(fixupLoc);
+                if ( (targetInfo.libOrdinal > 0) && (targetInfo.libOrdinal <= mh->dependentDylibCount()) ) {
+                    _missingWeakImports[fixupLoc] = mh->dependentDylibLoadPath(targetInfo.libOrdinal - 1);
+                }
+                break;
+            default:
+                assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache");
+        }
+    };
+
+    handlers.forEachExportsPatch = ^(dyld3::closure::ImageNum imageNum, void (^handler)(const dyld3::closure::ClosureBuilder::CacheDylibsBindingHandlers::PatchInfo&)) {
+        const dyld3::MachOLoaded* ml = imageNumToML[imageNum];
+        for (CacheOffset exportCacheOffset : dylibToItsExports[ml]) {
+            dyld3::closure::ClosureBuilder::CacheDylibsBindingHandlers::PatchInfo info;
+            std::vector<PatchLocation>& uses = exportsToUses[exportCacheOffset];
+            uses.erase(std::unique(uses.begin(), uses.end()), uses.end());
+            info.exportCacheOffset = (uint32_t)exportCacheOffset;
+            info.exportSymbolName  = exportsToName[exportCacheOffset];
+            info.usesCount         = (uint32_t)uses.size();
+            info.usesArray         = &uses.front();
+            handler(info);
+        }
+    };
+
+
+    // build ImageArray for all dylibs in dyld cache
+    dyld3::closure::PathOverrides pathOverrides;
+    dyld3::closure::ClosureBuilder cb(dyld3::closure::kFirstDyldCacheImageNum, _fileSystem, cache, false, pathOverrides, dyld3::closure::ClosureBuilder::AtPath::none, nullptr, _archLayout->archName, _options.platform, &handlers);
+    dyld3::Array<CachedDylibInfo> dylibs(&dylibInfos[0], dylibInfos.size(), dylibInfos.size());
+    const dyld3::Array<dyld3::closure::ClosureBuilder::CachedDylibAlias> aliasesArray(dylibAliases.data(), dylibAliases.size(), dylibAliases.size());
+    _imageArray = cb.makeDyldCacheImageArray(_options.optimizeStubs, dylibs, aliasesArray);
+    if ( cb.diagnostics().hasError() ) {
+        _diagnostics.error("%s", cb.diagnostics().errorMessage().c_str());
         return;
+    }
+}
 
-    dyld3::launch_cache::ImageGroup group(groupBinary);
-    size_t groupSize = group.size();
+void CacheBuilder::addImageArray()
+{
+    // build trie of dylib paths
+    __block std::vector<DylibIndexTrie::Entry> dylibEntrys;
+    _imageArray->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
+        dylibEntrys.push_back(DylibIndexTrie::Entry(image->path(), DylibIndex(image->imageNum()-1)));
+        image->forEachAlias(^(const char *aliasPath, bool &innerStop) {
+            dylibEntrys.push_back(DylibIndexTrie::Entry(aliasPath, DylibIndex(image->imageNum()-1)));
+        });
+    });
+    DylibIndexTrie dylibsTrie(dylibEntrys);
+    std::vector<uint8_t> trieBytes;
+    dylibsTrie.emit(trieBytes);
+    while ( (trieBytes.size() % 4) != 0 )
+        trieBytes.push_back(0);
 
-    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);
+    // check for fit
+    uint64_t imageArraySize = _imageArray->size();
+    size_t freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
+    if ( imageArraySize+trieBytes.size() > freeSpace ) {
+        _diagnostics.error("cache buffer too small to hold ImageArray and Trie (buffer size=%lldMB, imageArray size=%lldMB, trie size=%luKB, free space=%ldMB)",
+                            _allocatedBufferSize/1024/1024, imageArraySize/1024/1024, trieBytes.size()/1024, freeSpace/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);
+    // copy into cache and update header
+    DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    dyldCache->header.dylibsImageArrayAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
+    dyldCache->header.dylibsImageArraySize = imageArraySize;
+    dyldCache->header.dylibsTrieAddr       = dyldCache->header.dylibsImageArrayAddr + imageArraySize;
+    dyldCache->header.dylibsTrieSize       = trieBytes.size();
+    ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse, _imageArray, imageArraySize);
+    ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse + imageArraySize, &trieBytes[0], trieBytes.size());
+    _readOnlyRegion.sizeInUse += align(imageArraySize+trieBytes.size(),14);
 }
 
-
-void CacheBuilder::addCachedOtherDylibsImageGroup(dyld3::ImageProxyGroup* otherGroup)
+void CacheBuilder::addOtherImageArray(const std::vector<LoadedMachO>& otherDylibsAndBundles, std::vector<const LoadedMachO*>& overflowDylibs)
 {
-    const dyld3::launch_cache::binary_format::ImageGroup* groupBinary = otherGroup->makeImageGroupBinary(_diagnostics);
-    if (!groupBinary)
-        return;
+    DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    dyld3::closure::PathOverrides pathOverrides;
+    dyld3::closure::ClosureBuilder cb(dyld3::closure::kFirstOtherOSImageNum, _fileSystem, cache, false, pathOverrides, dyld3::closure::ClosureBuilder::AtPath::none, nullptr, _archLayout->archName, _options.platform);
+
+    // make ImageArray for other dylibs and bundles
+    STACK_ALLOC_ARRAY(dyld3::closure::LoadedFileInfo, others, otherDylibsAndBundles.size() + overflowDylibs.size());
+    for (const LoadedMachO& other : otherDylibsAndBundles) {
+        if ( !contains(other.loadedFileInfo.path, ".app/") )
+            others.push_back(other.loadedFileInfo);
+    }
+
+    for (const LoadedMachO* dylib : overflowDylibs) {
+        if (dylib->mappedFile.mh->canHavePrecomputedDlopenClosure(dylib->mappedFile.runtimePath.c_str(), ^(const char*) {}) )
+            others.push_back(dylib->loadedFileInfo);
+    }
 
-    dyld3::launch_cache::ImageGroup group(groupBinary);
-    size_t groupSize = group.size();
+    // Sort the others array by name so that it is deterministic
+    std::sort(others.begin(), others.end(),
+              [](const dyld3::closure::LoadedFileInfo& a, const dyld3::closure::LoadedFileInfo& b) {
+                  return strcmp(a.path, b.path) < 0;
+              });
 
-    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);
+    const dyld3::closure::ImageArray* otherImageArray = cb.makeOtherDylibsImageArray(others, (uint32_t)_sortedDylibs.size());
+
+    // build trie of paths
+    __block std::vector<DylibIndexTrie::Entry> otherEntrys;
+    otherImageArray->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
+        if ( !image->isInvalid() )
+            otherEntrys.push_back(DylibIndexTrie::Entry(image->path(), DylibIndex(image->imageNum())));
+    });
+    DylibIndexTrie dylibsTrie(otherEntrys);
+    std::vector<uint8_t> trieBytes;
+    dylibsTrie.emit(trieBytes);
+    while ( (trieBytes.size() % 4) != 0 )
+        trieBytes.push_back(0);
+
+    // check for fit
+    uint64_t imageArraySize = otherImageArray->size();
+    size_t freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
+    if ( imageArraySize+trieBytes.size() > freeSpace ) {
+        _diagnostics.error("cache buffer too small to hold ImageArray and Trie (buffer size=%lldMB, imageArray size=%lldMB, trie size=%luKB, free space=%ldMB)",
+                           _allocatedBufferSize/1024/1024, imageArraySize/1024/1024, trieBytes.size()/1024, freeSpace/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);
+    // copy into cache and update header
+    DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    dyldCache->header.otherImageArrayAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
+    dyldCache->header.otherImageArraySize = imageArraySize;
+    dyldCache->header.otherTrieAddr       = dyldCache->header.otherImageArrayAddr + imageArraySize;
+    dyldCache->header.otherTrieSize       = trieBytes.size();
+    ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse, otherImageArray, imageArraySize);
+    ::memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse + imageArraySize, &trieBytes[0], trieBytes.size());
+    _readOnlyRegion.sizeInUse += align(imageArraySize+trieBytes.size(),14);
 }
 
-void CacheBuilder::addClosures(const std::map<std::string, const dyld3::launch_cache::binary_format::Closure*>& closures)
+
+void CacheBuilder::addClosures(const std::vector<LoadedMachO>& osExecutables)
 {
+    const DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
+
+    __block std::vector<Diagnostics> osExecutablesDiags;
+    __block std::vector<const dyld3::closure::LaunchClosure*> osExecutablesClosures;
+    osExecutablesDiags.resize(osExecutables.size());
+    osExecutablesClosures.resize(osExecutables.size());
+
+    dispatch_apply(osExecutables.size(), DISPATCH_APPLY_AUTO, ^(size_t index) {
+        const LoadedMachO& loadedMachO = osExecutables[index];
+        // don't pre-build closures for staged apps into dyld cache, since they won't run from that location
+        if ( startsWith(loadedMachO.mappedFile.runtimePath, "/private/var/staged_system_apps/") ) {
+            return;
+        }
+        dyld3::closure::PathOverrides pathOverrides;
+        dyld3::closure::ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, _fileSystem, dyldCache, false, pathOverrides, dyld3::closure::ClosureBuilder::AtPath::all, nullptr, _archLayout->archName, _options.platform, nullptr);
+        bool issetuid = false;
+        if ( this->_options.platform == dyld3::Platform::macOS )
+            _fileSystem.fileExists(loadedMachO.loadedFileInfo.path, nullptr, nullptr, &issetuid);
+        const dyld3::closure::LaunchClosure* mainClosure = builder.makeLaunchClosure(loadedMachO.loadedFileInfo, issetuid);
+        if ( builder.diagnostics().hasError() ) {
+           osExecutablesDiags[index].error("%s", builder.diagnostics().errorMessage().c_str());
+        }
+        else {
+            assert(mainClosure != nullptr);
+            osExecutablesClosures[index] = mainClosure;
+        }
+    });
+
+    std::map<std::string, const dyld3::closure::LaunchClosure*> closures;
+    for (uint64_t i = 0, e = osExecutables.size(); i != e; ++i) {
+        const LoadedMachO& loadedMachO = osExecutables[i];
+        const Diagnostics& diag = osExecutablesDiags[i];
+        if (diag.hasError()) {
+            if ( _options.verbose ) {
+                _diagnostics.warning("building closure for '%s': %s", loadedMachO.mappedFile.runtimePath.c_str(), diag.errorMessage().c_str());
+                for (const std::string& warn : diag.warnings() )
+                    _diagnostics.warning("%s", warn.c_str());
+            }
+            if ( loadedMachO.inputFile && (loadedMachO.inputFile->mustBeIncluded()) ) {
+                loadedMachO.inputFile->diag.error("%s", diag.errorMessage().c_str());
+            }
+        } else {
+            // Note, a closure could be null here if it has a path we skip.
+            if (osExecutablesClosures[i] != nullptr)
+                closures[loadedMachO.mappedFile.runtimePath] = osExecutablesClosures[i];
+        }
+    }
+
+    osExecutablesDiags.clear();
+    osExecutablesClosures.clear();
+
     // preflight space needed
     size_t closuresSpace = 0;
     for (const auto& entry : closures) {
-        dyld3::launch_cache::Closure closure(entry.second);
-        closuresSpace += closure.size();
+        closuresSpace += entry.second->size();
     }
-    size_t freeSpace = _allocatedBufferSize - _currentFileSize;
+    size_t freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
     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;
+    DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    cache->header.progClosuresAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
+    uint8_t* closuresBase = _readOnlyRegion.buffer + _readOnlyRegion.sizeInUse;
     std::vector<DylibIndexTrie::Entry> closureEntrys;
     uint32_t currentClosureOffset = 0;
     for (const auto& entry : closures) {
-        const dyld3::launch_cache::binary_format::Closure* closBuf = entry.second;
+        const dyld3::closure::LaunchClosure* closure = entry.second;
         closureEntrys.push_back(DylibIndexTrie::Entry(entry.first, DylibIndex(currentClosureOffset)));
-        dyld3::launch_cache::Closure closure(closBuf);
-        size_t size = closure.size();
+        size_t size = closure->size();
         assert((size % 4) == 0);
-        memcpy(closuresBase+currentClosureOffset, closBuf, size);
+        memcpy(closuresBase+currentClosureOffset, closure, size);
         currentClosureOffset += size;
         freeSpace -= size;
-        free((void*)closBuf);
+        closure->deallocate();
     }
-    _buffer->header.progClosuresSize = currentClosureOffset;
-    _currentFileSize += currentClosureOffset;
-    freeSpace = _allocatedBufferSize - _currentFileSize;
-
+    cache->header.progClosuresSize = currentClosureOffset;
+    _readOnlyRegion.sizeInUse += currentClosureOffset;
+    freeSpace = _readOnlyRegion.bufferSize - _readOnlyRegion.sizeInUse;
     // build trie of indexes into closures list
     DylibIndexTrie closureTrie(closureEntrys);
     std::vector<uint8_t> trieBytes;
@@ -1781,10 +2586,147 @@ void CacheBuilder::addClosures(const std::map<std::string, const dyld3::launch_c
                             _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();
+    memcpy(_readOnlyRegion.buffer + _readOnlyRegion.sizeInUse, &trieBytes[0], trieBytes.size());
+    cache->header.progClosuresTrieAddr = _readOnlyRegion.unslidLoadAddress + _readOnlyRegion.sizeInUse;
+    cache->header.progClosuresTrieSize = trieBytes.size();
+    _readOnlyRegion.sizeInUse += trieBytes.size();
+    _readOnlyRegion.sizeInUse = align(_readOnlyRegion.sizeInUse, 14);
+}
+
+
+bool CacheBuilder::writeCache(void (^cacheSizeCallback)(uint64_t size), bool (^copyCallback)(const uint8_t* src, uint64_t size, uint64_t dstOffset))
+{
+    const dyld_cache_header*       cacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer;
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + cacheHeader->mappingOffset);
+    assert(_readExecuteRegion.sizeInUse       == mappings[0].size);
+    assert(_readWriteRegion.sizeInUse         == mappings[1].size);
+    assert(_readOnlyRegion.sizeInUse          == mappings[2].size);
+    assert(_readExecuteRegion.cacheFileOffset == mappings[0].fileOffset);
+    assert(_readWriteRegion.cacheFileOffset   == mappings[1].fileOffset);
+    assert(_readOnlyRegion.cacheFileOffset    == mappings[2].fileOffset);
+    assert(_codeSignatureRegion.sizeInUse     == cacheHeader->codeSignatureSize);
+    assert(cacheHeader->codeSignatureOffset   == mappings[2].fileOffset+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse);
+    cacheSizeCallback(_readExecuteRegion.sizeInUse+_readWriteRegion.sizeInUse+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse+_codeSignatureRegion.sizeInUse);
+    bool fullyWritten = copyCallback(_readExecuteRegion.buffer, _readExecuteRegion.sizeInUse, mappings[0].fileOffset);
+    fullyWritten &= copyCallback(_readWriteRegion.buffer, _readWriteRegion.sizeInUse, mappings[1].fileOffset);
+    fullyWritten &= copyCallback(_readOnlyRegion.buffer, _readOnlyRegion.sizeInUse, mappings[2].fileOffset);
+    if ( _localSymbolsRegion.sizeInUse != 0 ) {
+        assert(cacheHeader->localSymbolsOffset == mappings[2].fileOffset+_readOnlyRegion.sizeInUse);
+        fullyWritten &= copyCallback(_localSymbolsRegion.buffer, _localSymbolsRegion.sizeInUse, cacheHeader->localSymbolsOffset);
+    }
+    fullyWritten &= copyCallback(_codeSignatureRegion.buffer, _codeSignatureRegion.sizeInUse, cacheHeader->codeSignatureOffset);
+    return fullyWritten;
+}
+
+
+void CacheBuilder::writeFile(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 ) {
+        auto cacheSizeCallback = ^(uint64_t size) {
+            ::ftruncate(fd, size);
+        };
+        auto copyCallback = ^(const uint8_t* src, uint64_t size, uint64_t dstOffset) {
+            uint64_t writtenSize = pwrite(fd, src, size, dstOffset);
+            return writtenSize == size;
+        };
+        bool fullyWritten = writeCache(cacheSizeCallback, copyCallback);
+        if ( fullyWritten ) {
+            ::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; // success
+            }
+        }
+        else {
+            _diagnostics.error("could not write file %s", pathTemplateSpace);
+        }
+        ::close(fd);
+        ::unlink(pathTemplateSpace);
+    }
+    else {
+        _diagnostics.error("could not open file %s", pathTemplateSpace);
+    }
 }
 
+void CacheBuilder::writeBuffer(uint8_t*& buffer, uint64_t& bufferSize) {
+    auto cacheSizeCallback = ^(uint64_t size) {
+        buffer = (uint8_t*)malloc(size);
+        bufferSize = size;
+    };
+    auto copyCallback = ^(const uint8_t* src, uint64_t size, uint64_t dstOffset) {
+        memcpy(buffer + dstOffset, src, size);
+        return true;
+    };
+    bool fullyWritten = writeCache(cacheSizeCallback, copyCallback);
+    assert(fullyWritten);
+}
+
+void CacheBuilder::writeMapFile(const std::string& path)
+{
+    const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    std::string mapContent = cache->mapFile();
+    safeSave(mapContent.c_str(), mapContent.size(), path);
+}
+
+void CacheBuilder::writeMapFileBuffer(uint8_t*& buffer, uint64_t& bufferSize)
+{
+    const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    std::string mapContent = cache->mapFile();
+    buffer = (uint8_t*)malloc(mapContent.size() + 1);
+    bufferSize = mapContent.size() + 1;
+    memcpy(buffer, mapContent.data(), bufferSize);
+}
+
+
+void CacheBuilder::forEachCacheDylib(void (^callback)(const std::string& path)) {
+    for (const DylibInfo& dylibInfo : _sortedDylibs)
+        callback(dylibInfo.runtimePath);
+}
+
+
+CacheBuilder::ASLR_Tracker::~ASLR_Tracker()
+{
+    if ( _bitmap != nullptr )
+        ::free(_bitmap);
+}
+
+void CacheBuilder::ASLR_Tracker::setDataRegion(const void* rwRegionStart, size_t rwRegionSize)
+{
+    _pageCount   = (unsigned)(rwRegionSize+_pageSize-1)/_pageSize;
+    _regionStart = (uint8_t*)rwRegionStart;
+    _endStart    = (uint8_t*)rwRegionStart + rwRegionSize;
+    _bitmap      = (bool*)calloc(_pageCount*(_pageSize/4)*sizeof(bool), 1);
+}
+
+void CacheBuilder::ASLR_Tracker::add(void* loc)
+{
+    uint8_t* p = (uint8_t*)loc;
+    assert(p >= _regionStart);
+    assert(p < _endStart);
+    _bitmap[(p-_regionStart)/4] = true;
+}
+
+void CacheBuilder::ASLR_Tracker::remove(void* loc)
+{
+    uint8_t* p = (uint8_t*)loc;
+    assert(p >= _regionStart);
+    assert(p < _endStart);
+    _bitmap[(p-_regionStart)/4] = false;
+}
+
+bool CacheBuilder::ASLR_Tracker::has(void* loc)
+{
+    uint8_t* p = (uint8_t*)loc;
+    assert(p >= _regionStart);
+    assert(p < _endStart);
+    return _bitmap[(p-_regionStart)/4];
+}
+
+
+
 
index 582ac3ecf5f623f187d843346f210396c09a0839..746cb63c95e10e1d4d2370c169be72fae7b9976d 100644 (file)
 
 #include <string>
 #include <vector>
+#include <map>
 #include <unordered_map>
 #include <unordered_set>
 
+#include "ClosureFileSystem.h"
 #include "DyldSharedCache.h"
 #include "Diagnostics.h"
-#include "ImageProxy.h"
+#include "MachOAnalyzer.h"
 
 
-namespace  dyld3 {
-  namespace launch_cache {
-    namespace binary_format {
-      struct ImageGroup;
-      struct Closure;
-    }
-  }
-}
 
+template <typename P> class LinkeditOptimizer;
+
+
+class CacheBuilder {
+public:
+    CacheBuilder(const DyldSharedCache::CreateOptions& options, const dyld3::closure::FileSystem& fileSystem);
+
+    struct InputFile {
+        enum State {
+            Unset,
+            MustBeIncluded,
+            MustBeIncludedForDependent,
+            MustBeExcludedIfUnused
+        };
+        InputFile(const char* path, State state) : path(path), state(state) { }
+        const char*     path;
+        State           state = Unset;
+        Diagnostics     diag;
 
-struct CacheBuilder {
+        bool mustBeIncluded() const {
+            return (state == MustBeIncluded) || (state == MustBeIncludedForDependent);
+        }
+    };
 
-                                        CacheBuilder(const DyldSharedCache::CreateOptions& options);
+    // Contains a MachO which has been loaded from the file system and may potentially need to be unloaded later.
+    struct LoadedMachO {
+        DyldSharedCache::MappedMachO    mappedFile;
+        dyld3::closure::LoadedFileInfo  loadedFileInfo;
+        InputFile*                      inputFile;
+    };
 
-    void                                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 std::set<const mach_header*>  evictions();
-    const bool                          agileSignature();
-    const std::string                   cdHashFirst();
-    const std::string                   cdHashSecond();
+    void                                        build(std::vector<InputFile>& inputFiles,
+                                                      std::vector<DyldSharedCache::FileAlias>& aliases);
+    void                                        build(const std::vector<LoadedMachO>& dylibs,
+                                                      const std::vector<LoadedMachO>& otherOsDylibsInput,
+                                                      const std::vector<LoadedMachO>& osExecutables,
+                                                      std::vector<DyldSharedCache::FileAlias>& aliases);
+    void                                        build(const std::vector<DyldSharedCache::MappedMachO>&  dylibsToCache,
+                                                      const std::vector<DyldSharedCache::MappedMachO>&  otherOsDylibs,
+                                                      const std::vector<DyldSharedCache::MappedMachO>&  osExecutables,
+                                                      std::vector<DyldSharedCache::FileAlias>& aliases);
+    void                                        writeFile(const std::string& path);
+    void                                        writeBuffer(uint8_t*& buffer, uint64_t& size);
+    void                                        writeMapFile(const std::string& path);
+    void                                        writeMapFileBuffer(uint8_t*& buffer, uint64_t& bufferSize);
+    void                                        deleteBuffer();
+    std::string                                 errorMessage();
+    const std::set<std::string>                 warnings();
+    const std::set<const dyld3::MachOAnalyzer*> evictions();
+    const bool                                  agileSignature();
+    const std::string                           cdHashFirst();
+    const std::string                           cdHashSecond();
+
+    void forEachCacheDylib(void (^callback)(const std::string& path));
 
     struct SegmentMappingInfo {
         const void*     srcSegment;
         const char*     segName;
-        uint64_t        dstCacheAddress;
-        uint32_t        dstCacheOffset;
+        void*           dstSegment;
+        uint64_t        dstCacheUnslidAddress;
+        uint32_t        dstCacheFileOffset;
         uint32_t        dstCacheSegmentSize;
         uint32_t        copySegmentSize;
         uint32_t        srcSegmentIndex;
     };
 
-private:
+    class ASLR_Tracker
+    {
+    public:
+                ~ASLR_Tracker();
+
+        void        setDataRegion(const void* rwRegionStart, size_t rwRegionSize);
+        void        add(void* p);
+        void        remove(void* p);
+        bool        has(void* p);
+        const bool* bitmap()        { return _bitmap; }
+        unsigned    dataPageCount() { return _pageCount; }
+
+    private:
+
+        uint8_t*     _regionStart    = nullptr;
+        uint8_t*     _endStart       = nullptr;
+        bool*        _bitmap         = nullptr;
+        unsigned     _pageCount      = 0;
+        unsigned     _pageSize       = 4096;
+    };
+
+    typedef std::map<uint64_t, std::set<void*>> LOH_Tracker;
 
-    typedef std::unordered_map<const mach_header*, std::vector<SegmentMappingInfo>> SegmentMapping;
+    struct Region
+    {
+        uint8_t*    buffer                 = nullptr;
+        uint64_t    bufferSize             = 0;
+        uint64_t    sizeInUse              = 0;
+        uint64_t    unslidLoadAddress      = 0;
+        uint64_t    cacheFileOffset        = 0;
+    };
 
+private:
+    template <typename P>
+    friend class LinkeditOptimizer;
+    
     struct ArchLayout
     {
         uint64_t    sharedMemoryStart;
@@ -87,6 +151,7 @@ private:
         uint32_t    branchPoolLinkEditSize;
         uint32_t    branchReach;
         uint8_t     sharedRegionAlignP2;
+        uint8_t     slideInfoBytesPerPage;
         bool        sharedRegionsAreDiscontiguous;
         bool        is64;
     };
@@ -94,50 +159,95 @@ private:
     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);
+    struct UnmappedRegion
+    {
+        uint8_t*    buffer                 = nullptr;
+        uint64_t    bufferSize             = 0;
+        uint64_t    sizeInUse              = 0;
+    };
+
+    struct DylibInfo
+    {
+        const LoadedMachO*              input;
+        std::string                     runtimePath;
+        std::vector<SegmentMappingInfo> cacheLocation;
+    };
 
-    SegmentMapping assignSegmentAddresses(const std::vector<DyldSharedCache::MappedMachO>& dylibs, struct dyld_cache_mapping_info regions[3]);
+    void        makeSortedDylibs(const std::vector<LoadedMachO>& dylibs, const std::unordered_map<std::string, unsigned> sortOrder);
+    void        assignSegmentAddresses();
 
-    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);
+    uint64_t    cacheOverflowAmount();
+    size_t      evictLeafDylibs(uint64_t reductionTarget, std::vector<const LoadedMachO*>& overflowDylibs);
 
     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        writeCacheHeader();
+    void        copyRawSegments();
+    void        adjustAllImagesForNewSegmentLocations();
     void        writeSlideInfoV1();
-    void        recomputeCacheUUID(void);
+    void        writeSlideInfoV3(const bool bitmap[], unsigned dataPageCoun);
+    uint16_t    pageStartV3(uint8_t* pageContent, uint32_t pageSize, const bool bitmap[]);
     void        findDylibAndSegment(const void* contentPtr, std::string& dylibName, std::string& segName);
+    void        addImageArray();
+    void        buildImageArray(std::vector<DyldSharedCache::FileAlias>& aliases);
+    void        addOtherImageArray(const std::vector<LoadedMachO>&, std::vector<const LoadedMachO*>& overflowDylibs);
+    void        addClosures(const std::vector<LoadedMachO>&);
+    void        markPaddingInaccessible();
+
+    bool        writeCache(void (^cacheSizeCallback)(uint64_t size), bool (^copyCallback)(const uint8_t* src, uint64_t size, uint64_t dstOffset));
 
-    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(const bool bitmap[], unsigned dataPageCount);
+    template <typename P> bool makeRebaseChainV2(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info2* info);
+    template <typename P> void addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info2* info,
+                                             std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras);
 
-    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,
+    template <typename P> void writeSlideInfoV4(const bool bitmap[], unsigned dataPageCount);
+    template <typename P> bool makeRebaseChainV4(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info4* info);
+    template <typename P> void addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info4* info,
                                              std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras);
 
+    // implemented in AdjustDylibSegemnts.cpp
+    void        adjustDylibSegments(const DylibInfo& dylib, Diagnostics& diag) const;
+
+    // implemented in OptimizerLinkedit.cpp
+    void        optimizeLinkedit(const std::vector<uint64_t>& branchPoolOffsets);
+
+    // implemented in OptimizerObjC.cpp
+    void        optimizeObjC();
+
+    // implemented in OptimizerBranches.cpp
+    void        optimizeAwayStubs(const std::vector<uint64_t>& branchPoolStartAddrs, uint64_t branchPoolsLinkEditStartAddr);
+
+
+    typedef std::unordered_map<std::string, const dyld3::MachOAnalyzer*> InstallNameToMA;
+
 
     const DyldSharedCache::CreateOptions&       _options;
-    DyldSharedCache*                            _buffer;
+    const dyld3::closure::FileSystem&           _fileSystem;
+    Region                                      _readExecuteRegion;
+    Region                                      _readWriteRegion;
+    Region                                      _readOnlyRegion;
+    UnmappedRegion                              _localSymbolsRegion;
+    UnmappedRegion                              _codeSignatureRegion;
+    vm_address_t                                _fullAllocatedBuffer;
+    uint64_t                                    _nonLinkEditReadOnlySize;
     Diagnostics                                 _diagnostics;
-    std::set<const mach_header*>               _evictions;
+    std::set<const dyld3::MachOAnalyzer*>       _evictions;
     const ArchLayout*                           _archLayout;
     uint32_t                                    _aliasCount;
     uint64_t                                    _slideInfoFileOffset;
     uint64_t                                    _slideInfoBufferSizeAllocated;
     uint64_t                                    _allocatedBufferSize;
-    uint64_t                                    _currentFileSize;
-    uint64_t                                    _vmSize;
+    std::vector<DylibInfo>                      _sortedDylibs;
+    InstallNameToMA                             _installNameToCacheDylib;
     std::unordered_map<std::string, uint32_t>   _dataDirtySegsOrder;
-    std::vector<void*>                          _pointersForASLR;
-    dyld3::ImageProxyGroup::PatchTable          _patchTable;
+    // Note this is mutable as the only parallel writes to it are done atomically to the bitmap
+    mutable ASLR_Tracker                        _aslrTracker;
+    std::map<void*, std::string>                _missingWeakImports;
+    mutable LOH_Tracker                         _lohTracker;
+    const dyld3::closure::ImageArray*           _imageArray;
+    uint32_t                                    _sharedStringsPoolVmOffset;
     std::vector<uint64_t>                       _branchPoolStarts;
     uint64_t                                    _branchPoolsLinkEditStartAddr;
     uint8_t                                     _cdHashFirst[20];
@@ -145,18 +255,6 @@ private:
 };
 
 
-// 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)
index f544c190e27bf1cdd57748bb1cc0b0b83aeab7f2..9816d0c409cfbc033ad0fc327ef5b68e01051596 100644 (file)
@@ -37,7 +37,7 @@
 #include <CommonCrypto/CommonDigest.h>
 #include <CommonCrypto/CommonDigestSPI.h>
 
-#if !DYLD_IN_PROCESS
+#if BUILDING_CACHE_BUILDER
 #include <set>
 #include <string>
 #include <vector>
 #endif
 
 #define NO_ULEB
-#include "MachOParser.h"
+#include "MachOLoaded.h"
+#include "ClosureFileSystemPhysical.h"
 #include "CacheBuilder.h"
 #include "DyldSharedCache.h"
-#include "LaunchCache.h"
 #include "Trie.hpp"
 #include "StringUtils.h"
+#include "FileUtils.h"
 
 
 
-#if !DYLD_IN_PROCESS
+#if BUILDING_CACHE_BUILDER
 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);
+    const char* prefix = nullptr;
+    if ( (options.pathPrefixes.size() == 1) && !options.pathPrefixes[0].empty() )
+        prefix = options.pathPrefixes[0].c_str();
+    // FIXME: This prefix will be applied to dylib closures and executable closures, even though
+    // the old code didn't have a prefix on cache dylib closures
+    dyld3::closure::FileSystemPhysical fileSystem(prefix);
+    CacheBuilder   cache(options, fileSystem);
+    if (!cache.errorMessage().empty()) {
+        results.errorMessage = cache.errorMessage();
+        return results;
+    }
 
-    cache.build(dylibsToCache, otherOsDylibs, osExecutables);
+    std::vector<FileAlias> aliases;
+    switch ( options.platform ) {
+        case dyld3::Platform::iOS:
+        case dyld3::Platform::watchOS:
+        case dyld3::Platform::tvOS:
+            // FIXME: embedded cache builds should be getting aliases from manifest
+            aliases.push_back({"/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit", "/System/Library/Frameworks/IOKit.framework/IOKit"});
+            aliases.push_back({"/usr/lib/libstdc++.6.dylib",                                  "/usr/lib/libstdc++.dylib"});
+            aliases.push_back({"/usr/lib/libstdc++.6.dylib",                                  "/usr/lib/libstdc++.6.0.9.dylib"});
+            aliases.push_back({"/usr/lib/libz.1.dylib",                                       "/usr/lib/libz.dylib"});
+            aliases.push_back({"/usr/lib/libSystem.B.dylib",                                  "/usr/lib/libSystem.dylib"});
+            break;
+        default:
+            break;
+    }
 
-    results.agileSignature = cache.agileSignature();
-    results.cdHashFirst = cache.cdHashFirst();
-    results.cdHashSecond = cache.cdHashSecond();
-    results.warnings = cache.warnings();
-    results.evictions = cache.evictions();
+    cache.build(dylibsToCache, otherOsDylibs, osExecutables, aliases);
 
+    results.agileSignature = cache.agileSignature();
+    results.cdHashFirst    = cache.cdHashFirst();
+    results.cdHashSecond   = cache.cdHashSecond();
+    results.warnings       = cache.warnings();
+    results.evictions      = cache.evictions();
     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();
+        if ( !options.outputFilePath.empty() )  {
+            // write cache file, if path non-empty
+            cache.writeFile(options.outputFilePath);
+        }
+        if ( !options.outputMapFilePath.empty() ) {
+            // write map file, if path non-empty
+            cache.writeMapFile(options.outputMapFilePath);
+        }
     }
+    results.errorMessage = cache.errorMessage();
+    cache.deleteBuffer();
     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)) {
+        if ( dylib.mh->canBePlacedInDyldCache(dylib.runtimePath.c_str(), ^(const char* msg) { badDylibs[dylib.runtimePath].insert(msg);}) ) {
             knownDylibs.insert(dylib.runtimePath);
-            knownDylibs.insert(parser.installName());
-        } else {
-            badDylibs[dylib.runtimePath] = reasons;
+            knownDylibs.insert(dylib.mh->installName());
         }
     }
 
@@ -111,41 +135,33 @@ bool DyldSharedCache::verifySelfContained(std::vector<MappedMachO>& dylibsToCach
         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) {
+            dylib.mh->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);
-                        }
+                        badDylibs[dylib.runtimePath].insert(std::string("Could not find dependency '") + loadPath +"'");
                         knownDylibs.erase(dylib.runtimePath);
-                        dyld3::MachOParser parserBad(dylib.mh);
-                        knownDylibs.erase(parserBad.installName());
+                        knownDylibs.erase(dylib.mh->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());
+                        if ( foundMapping.mh->canBePlacedInDyldCache(foundMapping.runtimePath.c_str(), ^(const char* msg) { badDylibs[foundMapping.runtimePath].insert(msg);})) {
+                            // see if existing mapping was returned
+                            bool alreadyInVector = false;
+                            for (const MappedMachO& existing : dylibsToCache) {
+                                if ( existing.mh == foundMapping.mh ) {
+                                    alreadyInVector = true;
+                                    break;
+                                }
                             }
+                            if ( !alreadyInVector )
+                                foundMappings.push_back(foundMapping);
+                            knownDylibs.insert(loadPath);
+                            knownDylibs.insert(foundMapping.runtimePath);
+                            knownDylibs.insert(foundMapping.mh->installName());
                         }
                    }
                 }
@@ -179,6 +195,33 @@ void DyldSharedCache::forEachRegion(void (^handler)(const void* content, uint64_
     }
 }
 
+bool DyldSharedCache::inCache(const void* addr, size_t length, bool& readOnly) const
+{
+    // quick out if before start of cache
+    if ( addr < this )
+        return false;
+
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+    uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
+    uintptr_t unslidStart = (uintptr_t)addr - slide;
+
+    // quick out if after end of cache
+    if ( unslidStart > (mappings[2].address + mappings[2].size) )
+        return false;
+
+    // walk cache regions
+    const dyld_cache_mapping_info* mappingsEnd = &mappings[header.mappingCount];
+    uintptr_t unslidEnd = unslidStart + length;
+    for (const dyld_cache_mapping_info* m=mappings; m < mappingsEnd; ++m) {
+        if ( (unslidStart >= m->address) && (unslidEnd < (m->address+m->size)) ) {
+            readOnly = ((m->initProt & VM_PROT_WRITE) == 0);
+            return true;
+        }
+    }
+
+    return false;
+}
+
 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);
@@ -220,7 +263,16 @@ void DyldSharedCache::forEachImageEntry(void (^handler)(const char* path, uint64
     }
 }
 
-void DyldSharedCache::forEachImageTextSegment(void (^handler)(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName)) const
+const mach_header* DyldSharedCache::getIndexedImageEntry(uint32_t index, 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);
+    mTime = dylibs[index].modTime;
+    inode = dylibs[index].inode;
+    return (mach_header*)((uint8_t*)this + dylibs[index].address - mappings[0].address);
+}
+
+void DyldSharedCache::forEachImageTextSegment(void (^handler)(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName, bool& stop)) const
 {
     // check for old cache without imagesText array
     if ( header.mappingOffset < 123 )
@@ -229,13 +281,31 @@ void DyldSharedCache::forEachImageTextSegment(void (^handler)(uint64_t loadAddre
     // 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);
+    bool stop = false;
+    for (const dyld_cache_image_text_info* p=imagesText; p < imagesTextEnd && !stop; ++p) {
+        handler(p->loadAddress, p->textSegmentSize, p->uuid, (char*)this + p->pathOffset, stop);
     }
 }
 
+bool DyldSharedCache::addressInText(uint32_t cacheOffset, uint32_t* imageIndex) const
+{
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+    if ( cacheOffset > mappings[0].size )
+        return false;
+    uint64_t targetAddr = mappings[0].address + cacheOffset;
+    // 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) {
+        if ( (p->loadAddress <= targetAddr) && (targetAddr < p->loadAddress+p->textSegmentSize) ) {
+            *imageIndex = (uint32_t)(p-imagesText);
+            return true;
+        }
+    }
+    return false;
+}
 
-std::string DyldSharedCache::archName() const
+const char* DyldSharedCache::archName() const
 {
     const char* archSubString = ((char*)this) + 8;
     while (*archSubString == ' ')
@@ -244,12 +314,12 @@ std::string DyldSharedCache::archName() const
 }
 
 
-uint32_t DyldSharedCache::platform() const
+dyld3::Platform DyldSharedCache::platform() const
 {
-    return header.platform;
+    return (dyld3::Platform)header.platform;
 }
 
-#if !DYLD_IN_PROCESS
+#if BUILDING_CACHE_BUILDER
 std::string DyldSharedCache::mapFile() const
 {
     __block std::string             result;
@@ -280,10 +350,10 @@ std::string DyldSharedCache::mapFile() const
 
     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) {
+        const dyld3::MachOFile* mf = (dyld3::MachOFile*)mh;
+        mf->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool& stop) {
             char lineBuffer[256];
-            sprintf(lineBuffer, "\t%16s 0x%08llX -> 0x%08llX\n", segName, vmAddr, vmAddr+vmSize);
+            sprintf(lineBuffer, "\t%16s 0x%08llX -> 0x%08llX\n", info.segName, info.vmAddr, info.vmAddr+info.vmSize);
             result += lineBuffer;
         });
         result += "\n";
@@ -319,8 +389,178 @@ uint64_t DyldSharedCache::mappedSize() const
     return (endAddr - startAddr);
 }
 
+bool DyldSharedCache::findMachHeaderImageIndex(const mach_header* mh, uint32_t& imageIndex) const
+{
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+    uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
+    uint64_t unslidMh = (uintptr_t)mh - slide;
+    const dyld_cache_image_info* dylibs = (dyld_cache_image_info*)((char*)this + header.imagesOffset);
+    for (uint32_t i=0; i < header.imagesCount; ++i) {
+        if ( dylibs[i].address == unslidMh ) {
+            imageIndex = i;
+            return true;
+        }
+    }
+    return false;
+}
 
+bool DyldSharedCache::hasImagePath(const char* dylibPath, uint32_t& imageIndex) const
+{
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+    if ( mappings[0].fileOffset != 0 )
+        return false;
+    if ( header.mappingOffset >= 0x118 ) {
+        uintptr_t      slide           = (uintptr_t)this - (uintptr_t)(mappings[0].address);
+        const uint8_t* dylibTrieStart  = (uint8_t*)(this->header.dylibsTrieAddr + slide);
+        const uint8_t* dylibTrieEnd    = dylibTrieStart + this->header.dylibsTrieSize;
+
+        Diagnostics diag;
+        const uint8_t* imageNode = dyld3::MachOLoaded::trieWalk(diag, dylibTrieStart, dylibTrieEnd, dylibPath);
+        if ( imageNode != NULL ) {
+            imageIndex = (uint32_t)dyld3::MachOFile::read_uleb128(diag, imageNode, dylibTrieEnd);
+            return true;
+        }
+    }
+    else {
+        const dyld_cache_image_info* dylibs = (dyld_cache_image_info*)((char*)this + header.imagesOffset);
+        uint64_t firstImageOffset = 0;
+        uint64_t firstRegionAddress = mappings[0].address;
+        for (uint32_t i=0; i < header.imagesCount; ++i) {
+            const char* aPath  = (char*)this + dylibs[i].pathFileOffset;
+            if ( strcmp(aPath, dylibPath) == 0 ) {
+                imageIndex = i;
+                return true;
+            }
+            uint64_t offset = dylibs[i].address - firstRegionAddress;
+            if ( firstImageOffset == 0 )
+                firstImageOffset = offset;
+            // skip over aliases
+            if ( dylibs[i].pathFileOffset < firstImageOffset)
+                continue;
+        }
+    }
 
+    return false;
+}
+
+const dyld3::closure::Image* DyldSharedCache::findDlopenOtherImage(const char* path) const
+{
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+    if ( mappings[0].fileOffset != 0 )
+        return nullptr;
+    if ( header.mappingOffset < sizeof(dyld_cache_header) )
+        return nullptr;
+    if ( header.otherImageArrayAddr == 0 )
+        return nullptr;
+    uintptr_t      slide           = (uintptr_t)this - (uintptr_t)(mappings[0].address);
+    const uint8_t* dylibTrieStart  = (uint8_t*)(this->header.otherTrieAddr + slide);
+    const uint8_t* dylibTrieEnd    = dylibTrieStart + this->header.otherTrieSize;
+
+    Diagnostics diag;
+    const uint8_t* imageNode = dyld3::MachOLoaded::trieWalk(diag, dylibTrieStart, dylibTrieEnd, path);
+    if ( imageNode != NULL ) {
+        dyld3::closure::ImageNum imageNum = (uint32_t)dyld3::MachOFile::read_uleb128(diag, imageNode, dylibTrieEnd);
+        uint64_t arrayAddrOffset = header.otherImageArrayAddr - mappings[0].address;
+        const dyld3::closure::ImageArray* otherImageArray = (dyld3::closure::ImageArray*)((char*)this + arrayAddrOffset);
+        return otherImageArray->imageForNum(imageNum);
+    }
+
+    return nullptr;
+}
+
+
+
+
+const dyld3::closure::LaunchClosure* DyldSharedCache::findClosure(const char* executablePath) const
+{
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+    uintptr_t      slide                = (uintptr_t)this - (uintptr_t)(mappings[0].address);
+    const uint8_t* executableTrieStart  = (uint8_t*)(this->header.progClosuresTrieAddr + slide);
+    const uint8_t* executableTrieEnd    = executableTrieStart + this->header.progClosuresTrieSize;
+    const uint8_t* closuresStart        = (uint8_t*)(this->header.progClosuresAddr + slide);
+
+    Diagnostics diag;
+    const uint8_t* imageNode = dyld3::MachOLoaded::trieWalk(diag, executableTrieStart, executableTrieEnd, executablePath);
+    if ( (imageNode == NULL) && (strncmp(executablePath, "/System/", 8) == 0) ) {
+        // anything in /System/ should have a closure.  Perhaps it was launched via symlink path
+        char realPath[PATH_MAX];
+        if ( realpath(executablePath, realPath) != NULL )
+            imageNode = dyld3::MachOLoaded::trieWalk(diag, executableTrieStart, executableTrieEnd, realPath);
+    }
+    if ( imageNode != NULL ) {
+        uint32_t closureOffset = (uint32_t)dyld3::MachOFile::read_uleb128(diag, imageNode, executableTrieEnd);
+        if ( closureOffset < this->header.progClosuresSize )
+            return (dyld3::closure::LaunchClosure*)((uint8_t*)closuresStart + closureOffset);
+    }
+
+    return nullptr;
+}
+
+#if !BUILDING_LIBDYLD && !BUILDING_DYLD
+void DyldSharedCache::forEachLaunchClosure(void (^handler)(const char* executableRuntimePath, const dyld3::closure::LaunchClosure* closure)) const
+{
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+    uintptr_t      slide                = (uintptr_t)this - (uintptr_t)(mappings[0].address);
+    const uint8_t* executableTrieStart  = (uint8_t*)(this->header.progClosuresTrieAddr + slide);
+    const uint8_t* executableTrieEnd    = executableTrieStart + this->header.progClosuresTrieSize;
+    const uint8_t* closuresStart        = (uint8_t*)(this->header.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 < this->header.progClosuresSize )
+                handler(entry.name.c_str(), (const dyld3::closure::LaunchClosure*)(closuresStart+offset));
+        }
+    }
+}
+
+void DyldSharedCache::forEachDlopenImage(void (^handler)(const char* runtimePath, const dyld3::closure::Image* image)) const
+{
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+    uintptr_t      slide           = (uintptr_t)this - (uintptr_t)(mappings[0].address);
+    const uint8_t* otherTrieStart  = (uint8_t*)(this->header.otherTrieAddr + slide);
+    const uint8_t* otherTrieEnd    = otherTrieStart + this->header.otherTrieSize;
+
+    std::vector<DylibIndexTrie::Entry> otherEntries;
+    if ( Trie<DylibIndex>::parseTrie(otherTrieStart, otherTrieEnd, otherEntries) ) {
+        for (const DylibIndexTrie::Entry& entry : otherEntries ) {
+            dyld3::closure::ImageNum imageNum = entry.info.index;
+            uint64_t arrayAddrOffset = header.otherImageArrayAddr - mappings[0].address;
+            const dyld3::closure::ImageArray* otherImageArray = (dyld3::closure::ImageArray*)((char*)this + arrayAddrOffset);
+            handler(entry.name.c_str(), otherImageArray->imageForNum(imageNum));
+        }
+    }
+}
+#endif
+
+const dyld3::closure::ImageArray* DyldSharedCache::cachedDylibsImageArray() const
+{
+    // check for old cache without imagesArray
+    if ( header.mappingOffset < 0x100 )
+        return nullptr;
+
+    if ( header.dylibsImageArrayAddr == 0 )
+        return nullptr;
+        
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+    uint64_t arrayAddrOffset = header.dylibsImageArrayAddr - mappings[0].address;
+    return (dyld3::closure::ImageArray*)((char*)this + arrayAddrOffset);
+}
+
+const dyld3::closure::ImageArray* DyldSharedCache::otherOSImageArray() const
+{
+    // check for old cache without imagesArray
+    if ( header.mappingOffset < sizeof(dyld_cache_header) )
+        return nullptr;
+
+    if ( header.otherImageArrayAddr == 0 )
+        return nullptr;
+
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+    uint64_t arrayAddrOffset = header.otherImageArrayAddr - mappings[0].address;
+    return (dyld3::closure::ImageArray*)((char*)this + arrayAddrOffset);
+}
 
 
 
index 56008b5107bd8228ae268117c1ecbe1350fad8cc..3feea530284ebf6eef7509a8e63f72e5c930ca6b 100644 (file)
 #include <string>
 #include <vector>
 #include <unordered_map>
+#include <uuid/uuid.h>
 
 #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;
-    }
-  }
-}
+#include "MachOAnalyzer.h"
+#include "Closure.h"
 
 
 class VIS_HIDDEN DyldSharedCache
@@ -59,17 +50,19 @@ public:
 
     struct CreateOptions
     {
+        std::string                                 outputFilePath;
+        std::string                                 outputMapFilePath;
         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                                        isLocallyBuiltCache;
         bool                                        verbose;
         bool                                        evictLeafDylibsOnOverflow;
         std::unordered_map<std::string, unsigned>   dylibOrdering;
@@ -82,11 +75,11 @@ public:
     {
                                     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)
+                                    MappedMachO(const std::string& path, const dyld3::MachOAnalyzer* 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;
+        const dyld3::MachOAnalyzer* mh;
         size_t                      length;
         uint64_t                    isSetUID        :  1,
                                     protectedBySIP  :  1,
@@ -97,14 +90,19 @@ public:
 
     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;
-        std::set<const mach_header*>    evictions;
-        bool                            agileSignature = false;
-        std::string                     cdHashFirst;
-        std::string                     cdHashSecond;
+        std::string                             errorMessage;
+        std::set<std::string>                   warnings;
+        std::set<const dyld3::MachOAnalyzer*>   evictions;
+        bool                                    agileSignature  = false;
+        std::string                             cdHashFirst;
+        std::string                             cdHashSecond;
+    };
+
+
+    struct FileAlias
+    {
+        std::string             realPath;
+        std::string             aliasPath;
     };
 
 
@@ -149,13 +147,13 @@ public:
     //
     // Returns the architecture name of the shared cache, e.g. "arm64"
     //
-    std::string         archName() const;
+    const char*         archName() const;
 
 
     //
     // Returns the platform the cache is for
     //
-    uint32_t            platform() const;
+    dyld3::Platform    platform() const;
 
 
     //
@@ -165,6 +163,17 @@ public:
 
 
     //
+    // Searches cache for dylib with specified path
+    //
+    bool                hasImagePath(const char* dylibPath, uint32_t& imageIndex) const;
+
+
+    //
+    // Searches cache for dylib with specified mach_header
+    //
+    bool                findMachHeaderImageIndex(const mach_header* mh, uint32_t& imageIndex) const;
+
+   //
     // Iterates over each dylib in the cache
     //
     void                forEachImageEntry(void (^handler)(const char* path, uint64_t mTime, uint64_t inode)) const;
@@ -173,7 +182,13 @@ public:
     //
     // 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;
+    const mach_header*  getIndexedImageEntry(uint32_t index, uint64_t& mTime, uint64_t& node) 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, bool& stop)) const;
 
 
     //
@@ -182,6 +197,12 @@ public:
     void                forEachRegion(void (^handler)(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions)) const;
 
 
+    //
+    // Returns if an address range is in this cache, and if so if in a read-only area
+    //
+    bool                inCache(const void* addr, size_t length, bool& readOnly) const;
+
+
     //
     // returns address the cache would load at if unslid
     //
@@ -200,6 +221,49 @@ public:
     uint64_t            mappedSize() const;
 
 
+    //
+    // searches cache for pre-built closure for program
+    //
+    const dyld3::closure::LaunchClosure* findClosure(const char* executablePath) const;
+
+
+    //
+    // iterates all pre-built closures for program
+    //
+    void forEachLaunchClosure(void (^handler)(const char* executableRuntimePath, const dyld3::closure::LaunchClosure* closure)) const;
+
+
+    //
+    // iterates all pre-built Image* for OS dylibs/bundles not in dyld cache
+    //
+    void forEachDlopenImage(void (^handler)(const char* runtimePath, const dyld3::closure::Image* image)) const;
+
+
+    //
+    // returns the ImageArray pointer to Images in dyld shared cache
+    //
+    const dyld3::closure::ImageArray*  cachedDylibsImageArray() const;
+
+
+    //
+    // returns the ImageArray pointer to Images in OS with pre-build dlopen closure
+    //
+    const dyld3::closure::ImageArray*  otherOSImageArray() const;
+
+
+    //
+    // searches cache for pre-built dlopen closure for OS dylib/bundle
+    //
+    const dyld3::closure::Image* findDlopenOtherImage(const char* path) const;
+
+
+    //
+    // returns true if the offset is in the TEXT of some cached dylib and sets *index to the dylib index
+    //
+    bool              addressInText(uint32_t cacheOffset, uint32_t* index) const;
+
+
+
     dyld_cache_header header;
 };
 
index 19f02a4a85d935e0bc152446c6a739670479e3f6..1e34aae5dbae40248727bca56cf98ab2a04182ec 100644 (file)
@@ -45,6 +45,7 @@
 #include <sstream>
 
 #include "FileUtils.h"
+#include "StringUtils.h"
 #include "Diagnostics.h"
 
 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 101200
@@ -52,7 +53,7 @@ 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)
+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, bool recurse)
 {
     std::string fullDirPath = pathPrefix + path;
     DIR* dir = ::opendir(fullDirPath.c_str());
@@ -81,7 +82,8 @@ void iterateDirectoryTree(const std::string& pathPrefix, const std::string& path
                     break;
                 if ( dirFilter(dirAndFile) )
                     break;
-                iterateDirectoryTree(pathPrefix, dirAndFile, dirFilter, fileCallback);
+                if (recurse)
+                    iterateDirectoryTree(pathPrefix, dirAndFile, dirFilter, fileCallback, processFiles, true);
                 break;
             case DT_LNK:
                 // don't follow symlinks, dylib will be found through absolute path
@@ -101,7 +103,7 @@ bool safeSave(const void* buffer, size_t bufferLen, const std::string& path)
     int fd = mkstemp(pathTemplateSpace);
     if ( fd != -1 ) {
         ssize_t writtenSize = pwrite(fd, buffer, bufferLen, 0);
-        if ( writtenSize == bufferLen ) {
+        if ( (size_t)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);
@@ -114,13 +116,13 @@ bool safeSave(const void* buffer, size_t bufferLen, const std::string& path)
     return false; // failure
 }
 
-const void* mapFileReadOnly(const std::string& path, size_t& mappedSize)
+const void* mapFileReadOnly(const char* path, size_t& mappedSize)
 {
     struct stat statBuf;
-    if ( ::stat(path.c_str(), &statBuf) != 0 )
+    if ( ::stat(path, &statBuf) != 0 )
         return nullptr;
 
-    int fd = ::open(path.c_str(), O_RDONLY);
+    int fd = ::open(path, O_RDONLY);
     if ( fd < 0 )
         return nullptr;
 
@@ -181,24 +183,37 @@ bool fileExists(const std::string& path)
 //
 // 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> parseOrderFile(const std::string& orderFileData) {
     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++;
+    if (orderFileData.empty())
+        return order;
+
+    std::stringstream myData(orderFileData);
+
+    uint32_t count = 0;
+    std::string line;
+    while ( std::getline(myData, line) ) {
+        size_t pos = line.find('#');
+        if ( pos != std::string::npos )
+            line.resize(pos);
+        while ( !line.empty() && isspace(line.back()) ) {
+            line.pop_back();
         }
-        myfile.close();
+        if ( !line.empty() )
+            order[line] = count++;
+    }
+    return order;
+}
+
+std::string loadOrderFile(const std::string& orderFilePath) {
+    std::string order;
+
+    size_t size = 0;
+    char* data = (char*)mapFileReadOnly(orderFilePath.c_str(), size);
+    if (data) {
+        order = std::string(data, size);
+        ::munmap((void*)data, size);
     }
 
     return order;
@@ -305,23 +320,6 @@ 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;
@@ -410,5 +408,173 @@ std::pair<uint8_t*, struct stat> FileCache::fill(Diagnostics& diags, const std::
     return std::make_pair((uint8_t*)buffer_ptr, stat_buf);
 }
 
+static void normalizePath(std::string& path) {
+    // Remove a bunch of stuff we don't need, like trailing slashes.
+    while ( !path.empty() && (path.back() == '/'))
+        path.pop_back();
+}
+
+void SymlinkResolver::addFile(Diagnostics& diags, std::string path) {
+    if (path.front() != '/') {
+        diags.error("Path must start with '/'");
+        return;
+    }
+    if (symlinks.find(path) != symlinks.end()) {
+        diags.error("Cannot add regular file as it is already a symlink");
+        return;
+    }
+    filePaths.insert(path);
+}
+
+void SymlinkResolver::addSymlink(Diagnostics& diags, std::string fromPath, std::string toPath) {
+    normalizePath(fromPath);
+    normalizePath(toPath);
+    if (fromPath.front() != '/') {
+        diags.error("Path must start with '/'");
+        return;
+    }
+    if (filePaths.find(fromPath) != filePaths.end()) {
+        diags.error("Cannot add symlink from '%s' as it is already a regular path", fromPath.c_str());
+        return;
+    }
+    auto itAndInserted = symlinks.insert({ fromPath, toPath });
+    if (!itAndInserted.second) {
+        // The path is already a symlink.  Make sure its a dupe.
+        if (toPath != itAndInserted.first->second) {
+            diags.error("Duplicate symlink for path '%s'", fromPath.c_str());
+            return;
+        }
+    }
+}
+
+std::string SymlinkResolver::realPath(Diagnostics& diags, const std::string& originalPath) const {
+    // First make sure the path doesn't have any magic in it.
+    std::string path = originalPath;
+    normalizePath(path);
+
+    std::set<std::string> seenSymlinks;
+
+    // Now see if any prefix is a symlink
+    if (path.front() != '/')
+        return path;
+
+    std::string::size_type prev_pos = 0;
+    while (prev_pos != std::string::npos) {
+        std::string::size_type pos = path.find("/", prev_pos + 1);
+
+        // First look to see if this path component is special, eg, ., .., etc.
+        std::string component = path.substr(prev_pos, pos - prev_pos);
+        if (component == "/..") {
+            // Fold with the previous path component.
+            if (prev_pos == 0) {
+                // This is the root path, and .. applied to / is just /
+                path = path.substr(3);
+                prev_pos = 0;
+            } else {
+                std::string::size_type lastSlashPos = path.rfind("/", prev_pos - 1);
+                path = path.substr(0, lastSlashPos) + path.substr(pos);
+                prev_pos = lastSlashPos;
+            }
+            continue;
+        } else if (component == "/.") {
+            if (prev_pos == 0) {
+                // Path starts with /./ so just remove the first one.
+                path = path.substr(2);
+            } else {
+                if (pos == std::string::npos) {
+                    // Trailing . on the path
+                    path = path.substr(0, prev_pos );
+                } else {
+                    path = path.substr(0, prev_pos) + path.substr(pos);
+                }
+            }
+            continue;
+        } else if (component == "/") {
+            // Path must contain // somewhere so strip out the duplicates.
+            if (prev_pos == 0) {
+                // Path starts with // so just remove the first one.
+                path = path.substr(1);
+            } else {
+                if (pos == std::string::npos) {
+                    // Trailing / on the path
+                    path = path.substr(0, prev_pos);
+                    prev_pos = pos;
+                } else {
+                    path = path.substr(0, pos) + path.substr(pos + 1);
+                }
+            }
+            continue;
+        }
+
+        // Path is not special, so see if it is a symlink to something.
+        std::string prefix = path.substr(0, pos);
+        //printf("%s\n", prefix.c_str());
+        auto it = symlinks.find(prefix);
+        if (it == symlinks.end()) {
+            // This is not a symlink so move to the next prefix.
+            prev_pos = pos;
+            continue;
+        }
+
+        // If we've already done this prefix then error out.
+        if (seenSymlinks.count(prefix)) {
+            diags.error("Loop in symlink processing for '%s'", originalPath.c_str());
+            return std::string();
+        }
+
+        seenSymlinks.insert(prefix);
+
+        // This is a symlink, so resolve the new path.
+        std::string toPath = it->second;
+        if (toPath.front() == '/') {
+            // Symlink points to an absolute address so substitute the whole prefix for the new path
+            // If we didn't substitute the last component of the path then there is also a path suffix.
+            std::string pathSuffix = "";
+            if (pos != std::string::npos) {
+                std::string::size_type nextSlashPos = path.find("/", pos + 1);
+                if (nextSlashPos != std::string::npos)
+                    pathSuffix = path.substr(nextSlashPos);
+            }
+            path = toPath + pathSuffix;
+            prev_pos = 0;
+            continue;
+        }
+
+        // Symlink points to a relative path so we need to do more processing to get the real path.
+
+        // First calculate which part of the previous prefix we'll keep.  Eg, in /a/b/c where "b -> blah", we want to keep /a here.
+        std::string prevPrefix = path.substr(0, prev_pos);
+        //printf("prevPrefix %s\n", prevPrefix.c_str());
+
+        // If we didn't substitute the last component of the path then there is also a path suffix.
+        std::string pathSuffix = "";
+        if (prefix.size() != path.size())
+            pathSuffix = path.substr(pos);
+
+        // The new path is the remaining prefix, plus the symlink target, plus any remaining suffix from the original path.
+        path = prevPrefix + "/" + toPath + pathSuffix;
+        prev_pos = 0;
+    }
+    return path;
+}
+
+std::vector<DyldSharedCache::FileAlias> SymlinkResolver::getResolvedSymlinks(Diagnostics& diags) {
+    diags.assertNoError();
+    std::vector<DyldSharedCache::FileAlias> aliases;
+    for (auto& fromPathAndToPath : symlinks) {
+        std::string newPath = realPath(diags, fromPathAndToPath.first);
+        if (diags.hasError()) {
+            aliases.clear();
+            return aliases;
+        }
+
+        if (filePaths.count(newPath)) {
+            aliases.push_back({ newPath, fromPathAndToPath.first });
+            // printf("symlink ('%s' -> '%s') resolved to '%s'\n", fromPathAndToPath.first.c_str(), fromPathAndToPath.second.c_str(), newPath.c_str());
+        }
+    }
+    return aliases;
+}
+
 #endif // BUILDING_CACHE_BUILDER
 
index dbf6ae466a6e6931e2b1c2eccd5aac4a7b15cc20..fe3f21edf2f66c1b39819ac3e49e86a2a8102fb1 100644 (file)
 
 #include <stdint.h>
 
+#include <map>
+#include <set>
 #include <string>
 #include <vector>
 #include <unordered_map>
 #include <unordered_set>
 #include <dispatch/dispatch.h>
 
+#include "DyldSharedCache.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);
@@ -59,7 +61,7 @@ extern FileCache fileCache;
 // 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);
+                          void (^callback)(const std::string& path, const struct stat& statBuf), bool processFiles=true, bool recurse=true);
 
 
 //
@@ -69,14 +71,15 @@ void iterateDirectoryTree(const std::string& pathPrefix, const std::string& path
 bool safeSave(const void* buffer, size_t bufferLen, const std::string& path);
 
 
-const void* mapFileReadOnly(const std::string& path, size_t& mappedSize);
+const void* mapFileReadOnly(const char* 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::unordered_map<std::string, uint32_t> parseOrderFile(const std::string& orderFileData);
+std::string loadOrderFile(const std::string& orderFilePath);
 
 std::string normalize_absolute_file_path(std::string path);
 std::string basePath(const std::string& path);
@@ -86,7 +89,21 @@ std::string realFilePath(const std::string& path);
 
 std::string toolDir();
 
+class SymlinkResolver {
+public:
+    SymlinkResolver() { }
+
+    void addFile(Diagnostics& diags, std::string path);
+
+    void addSymlink(Diagnostics& diags, std::string fromPath, std::string toPath);
 
+    std::string realPath(Diagnostics& diags, const std::string& path) const;
 
+    std::vector<DyldSharedCache::FileAlias> getResolvedSymlinks(Diagnostics& diags);
+
+private:
+    std::set<std::string> filePaths;
+    std::map<std::string, std::string> symlinks;
+};
 
 #endif // FileUtils_h
diff --git a/dyld3/shared-cache/ImageProxy.cpp b/dyld3/shared-cache/ImageProxy.cpp
deleted file mode 100644 (file)
index 960e1b9..0000000
+++ /dev/null
@@ -1,2372 +0,0 @@
-/*
- * 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++, true);
-        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, true);
-    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));
-                            if ( replacementOffsetInImage >= 0xFFFFFFFFULL ) {
-                                diag.warning("bad interposing implementation in %s", _images[imageIndex]->runtimePath().c_str());
-                                return;
-                            }
-                            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));
-                                if ( foundInfo.value >= (1ULL << 32) ) {
-                                    diag.warning("bad weak symbol address in %s", proxy->runtimePath().c_str());
-                                    return;
-                                }
-                                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
deleted file mode 100644 (file)
index 35bf42c..0000000
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * 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
index a15100f25c0859400e929fd52ddf31a11e2851a6..824742f910ec37425ed7d5ce3070a76c80ae3664 100644 (file)
@@ -67,6 +67,20 @@ struct uuid_command {
 #ifndef CPU_TYPE_ARM64
        #define CPU_TYPE_ARM64                          ((cpu_type_t) (CPU_TYPE_ARM | CPU_ARCH_ABI64))
 #endif
+#ifndef CPU_TYPE_ARM64_32
+    #ifndef CPU_ARCH_ABI64_32
+        #define CPU_ARCH_ABI64_32            0x02000000
+    #endif
+    #define CPU_TYPE_ARM64_32            (CPU_TYPE_ARM | CPU_ARCH_ABI64_32)
+#endif
+
+#ifndef CPU_SUBTYPE_ARM64_32_V8
+    #define CPU_SUBTYPE_ARM64_32_V8    1
+#endif
+
+#ifndef CPU_SUBTYPE_ARM64_E
+       #define CPU_SUBTYPE_ARM64_E    2
+#endif
 
 #define ARM64_RELOC_UNSIGNED            0 // for pointers
 
@@ -113,6 +127,7 @@ struct uuid_command {
 #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 DYLD_CACHE_ADJ_V2_THREADED_POINTER_64   0x0D
 
 #define MH_HAS_OBJC                    0x40000000
 
@@ -952,7 +967,7 @@ inline int64_t read_sleb128(const uint8_t*& p, const uint8_t* end)
        } while (byte & 0x80);
        // sign extend negative numbers
        if ( (byte & 0x40) != 0 )
-               result |= (-1LL) << bit;
+               result |= (~0ULL) << bit;
        return result;
 }
 
index 3348cfa032f4c2163fb99e7953b94fdf4cec9336..e48e4e75a8c86aef03f235eec01c0efce82ab8c9 100644 (file)
 
 #import <Foundation/Foundation.h>
 
-#include "MachOParser.h"
 #include "DyldSharedCache.h"
 #include "Diagnostics.h"
+#include "MachOAnalyzer.h"
 
 extern std::string toolDir();
 
 namespace dyld3 {
 
+struct VIS_HIDDEN UUID {
+    UUID() {}
+    UUID(const UUID& other) { uuid_copy(_bytes, other._bytes); }
+    UUID(const uuid_t other) { uuid_copy(&_bytes[0], other); }
+    UUID(const dyld3::MachOAnalyzer* ml) { ml->getUuid(_bytes); }
+    bool operator<(const UUID& other) const { return uuid_compare(_bytes, other._bytes) < 0; }
+    bool operator==(const UUID& other) const { return uuid_compare(_bytes, other._bytes) == 0; }
+    bool operator!=(const UUID& other) const { return !(*this == other); }
+
+    size_t hash() const
+    {
+        size_t retval = 0;
+        for (size_t i = 0; i < (16 / sizeof(size_t)); ++i) {
+            retval ^= ((size_t*)(&_bytes[0]))[i];
+        }
+        return retval;
+    }
+    const unsigned char* get() const { return _bytes; };
+
+private:
+    uuid_t _bytes;
+};
+
 struct BuildQueueEntry {
     DyldSharedCache::CreateOptions            options;
     std::vector<DyldSharedCache::MappedMachO> dylibsForCache;
@@ -58,7 +81,7 @@ struct BuildQueueEntry {
 
 struct Manifest {
     struct UUIDInfo {
-        const mach_header* mh;
+        const MachOAnalyzer* mh;
         uint64_t sliceFileOffset;
         std::size_t size;
         std::string runtimePath;
@@ -66,7 +89,7 @@ struct Manifest {
         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)
+        UUIDInfo(const MachOAnalyzer* 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(), "", "", "", "") {}
     };
@@ -75,13 +98,6 @@ struct Manifest {
         std::vector<std::string> sources;
     };
 
-    struct File {
-        MachOParser* parser;
-        File(MachOParser* P)
-            : parser(P)
-        {
-        }
-    };
 
     struct SegmentInfo {
         std::string name;
@@ -116,7 +132,7 @@ struct Manifest {
         CacheInfo             developmentCache;
         CacheInfo             productionCache;
         CacheImageInfo& dylibForInstallname(const std::string& installname);
-        void exclude(MachOParser* parser, const std::string& reason);
+        void exclude(const dyld3::MachOAnalyzer* ml, const std::string& reason);
         void exclude(Manifest& manifest, const UUID& uuid, const std::string& reason);
     };
 
@@ -170,29 +186,37 @@ struct Manifest {
     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);
+    Manifest(Diagnostics& D, const std::string& path, bool onlyParseManifest = false);
+    Manifest(Diagnostics& D, const std::string& path, const std::set<std::string>& overlays, bool onlyParseManifest = false);
 
-    BuildQueueEntry makeQueueEntry(const std::string& outputPath, const std::set<std::string>& configs, const std::string& arch, bool optimizeStubs, const std::string& prefix, bool verbose);
+    BuildQueueEntry makeQueueEntry(const std::string& outputPath, const std::set<std::string>& configs, const std::string& arch, bool optimizeStubs, const std::string& prefix,
+                                   bool isLocallyBuiltCache, bool skipWrites, 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 MachOAnalyzer* machOForUUID(const UUID& uuid) const;
     const std::string buildPathForUUID(const UUID& uuid);
     const std::string runtimePathForUUID(const UUID& uuid);
+    const std::string& installNameForUUID(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);
+    std::set<std::string> resultsForConfiguration(const std::string& configName);
+
+    // These are used by MRM to support having the Manifest give us a list of files/symlinks from the BOM but we use MRM for the actual cache generation
+    void forEachMachO(std::string configuration, std::function<void(const std::string &buildPath, const std::string &runtimePath, const std::string &arch, bool shouldBeExcludedIfLeaf)> lambda);
+
+    void forEachSymlink(std::string configuration, std::function<void(const std::string &fromPath, const std::string &toPath)> lambda);
 
 private:
     NSDictionary*    _manifestDict;
     Diagnostics&      _diags;
     std::map<UUID, UUIDInfo> _uuidMap;
     std::map<std::pair<std::string, std::string>, UUID> _installNameMap;
+    std::vector<std::pair<std::string, std::string>>    _symlinks;
     static dispatch_queue_t _identifierQueue;
     uint32_t                _manifestVersion;
     std::string             _build;
@@ -203,6 +227,7 @@ private:
     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>> _metabomSymlinkTagMap;
     std::map<std::string, std::set<std::string>> _metabomExcludeTagMap;
     std::map<std::string, std::set<std::string>> _metabomRestrictedTagMap;
 
@@ -213,10 +238,8 @@ private:
     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 loadParser(const void* p, size_t sliceLength, 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);
@@ -226,4 +249,14 @@ private:
 };
 }
 
+namespace std {
+template <>
+struct hash<dyld3::UUID> {
+    size_t operator()(const dyld3::UUID& x) const
+    {
+        return x.hash();
+    }
+};
+}
+
 #endif /* Manifest_h */
index abc23bba67ffd42f68212b902e37ddb0f08952d1..ee6cb4ec3c2c666ada482eb64071f9e5a6a2d327 100644 (file)
@@ -40,6 +40,8 @@ extern "C" {
 #include "Trie.hpp"
 #include "FileUtils.h"
 #include "StringUtils.h"
+#include "MachOFile.h"
+#include "MachOAnalyzer.h"
 
 #include <mach-o/loader.h>
 #include <mach-o/fat.h>
@@ -48,6 +50,7 @@ extern "C" {
 #include <vector>
 
 #include "Manifest.h"
+#include "ClosureFileSystemPhysical.h"
 
 namespace {
 //FIXME this should be in a class
@@ -78,27 +81,24 @@ inline bool is_disjoint(const Set1& set1, const Set2& set2)
     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)
+void Manifest::Results::exclude(const dyld3::MachOAnalyzer* mh, const std::string& reason)
 {
-    auto dylibUUID = parser->uuid();
-    dylibs[dylibUUID].uuid = dylibUUID;
-    dylibs[dylibUUID].installname = parser->installName();
-    dylibs[dylibUUID].included = false;
+    UUID dylibUUID(mh);
+    dylibs[dylibUUID].uuid          = dylibUUID;
+    dylibs[dylibUUID].installname   = mh->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;
+    const MachOAnalyzer* mh = manifest.machOForUUID(uuid);
+    dylibs[uuid].uuid          = uuid;
+    dylibs[uuid].installname   = mh->installName();
+    dylibs[uuid].included      = false;
     dylibs[uuid].exclusionInfo = reason;
 }
 
@@ -262,11 +262,14 @@ 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)
+BuildQueueEntry Manifest::makeQueueEntry(const std::string& outputPath, const std::set<std::string>& configs, const std::string& arch, bool optimizeStubs,
+                                         const std::string& prefix, bool isLocallyBuiltCache, bool skipWrites, bool verbose)
 {
     dyld3::BuildQueueEntry retval;
 
     DyldSharedCache::CreateOptions options;
+    options.outputFilePath    = skipWrites ? "" : outputPath;
+    options.outputMapFilePath = skipWrites ? "" : outputPath + ".map";
     options.archName = arch;
     options.platform = platform();
     options.excludeLocalSymbols = true;
@@ -278,12 +281,13 @@ BuildQueueEntry Manifest::makeQueueEntry(const std::string& outputPath, const st
     options.inodesAreSameAsRuntime = false;
     options.cacheSupportsASLR = true;
     options.forSimulator = false;
+    options.isLocallyBuiltCache = isLocallyBuiltCache;
     options.verbose = verbose;
     options.evictLeafDylibsOnOverflow = true;
     options.loggingPrefix = prefix;
-    options.pathPrefixes = { "" };
-    options.dylibOrdering = loadOrderFile(_dylibOrderFile);
-    options.dirtyDataSegmentOrdering = loadOrderFile(_dirtyDataOrderFile);
+    options.pathPrefixes = { "./Root/" };
+    options.dylibOrdering = parseOrderFile(loadOrderFile(_dylibOrderFile));
+    options.dirtyDataSegmentOrdering = parseOrderFile(loadOrderFile(_dirtyDataOrderFile));
 
     dyld3::BuildQueueEntry queueEntry;
     retval.configNames = configs;
@@ -296,36 +300,62 @@ BuildQueueEntry Manifest::makeQueueEntry(const std::string& outputPath, const st
     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)
+bool Manifest::loadParser(const void* p, size_t sliceLength, 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)) {
+    assert(!_diags.hasError());
+
+    const MachOFile* mf = reinterpret_cast<const MachOFile*>(p);
+    const std::string archName = mf->archName();
+    if ( archName == "unknown" ) {
+        // Clear the error and punt
+        _diags.verbose("Dylib located at '%s' has unknown architecture\n", runtimePath.c_str());
         return false;
     }
+    if ( architectures.count(archName) == 0 )
+        return false;
 
-    auto parser = MachOParser(mh);
-    if (_diags.hasError()) {
+    const MachOAnalyzer* ma = reinterpret_cast<const MachOAnalyzer*>(p);
+    if ( !ma->validMachOForArchAndPlatform(_diags, sliceLength, runtimePath.c_str(), archName.c_str(), _platform) ) {
         // Clear the error and punt
-        _diags.verbose("MachoParser error: %s\n", _diags.errorMessage().c_str());
+        _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
+        _diags.clearError();
+        return false;
+    }
+
+    // if this file uses zero-fill expansion, then mapping whole file in one blob will not work
+    // remapIfZeroFill() will remap the file
+    closure::FileSystemPhysical fileSystem;
+    closure::LoadedFileInfo info;
+    info.fileContent                = p;
+    info.fileContentLen             = sliceLength;
+    info.sliceOffset                = 0;
+    info.sliceLen                   = sliceLength;
+    info.inode                      = 0;
+    info.mtime                      = 0;
+    info.unload                     = nullptr;
+    ma = ma->remapIfZeroFill(_diags, fileSystem, info);
+
+    if (ma == nullptr) {
+        _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
         _diags.clearError();
         return false;
     }
 
-    auto uuid = parser.uuid();
-    auto archName = parser.archName();
+    uuid_t uuid;
+    ma->getUuid(uuid);
 
-    if (parser.fileType() == MH_DYLIB && architectures.count(parser.archName()) != 0) {
-        std::string installName = parser.installName();
-        auto index = std::make_pair(installName, parser.archName());
+    if ( ma->isDylib() ) {
+        std::string installName = ma->installName();
+        auto index = std::make_pair(installName, 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)));
+            _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, 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)));
+            _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, installName)));
             if (installName[0] != '@' && installName != runtimePath) {
                 _diags.warning("Dylib located at '%s' has  installname '%s'", runtimePath.c_str(), installName.c_str());
             }
@@ -336,11 +366,11 @@ bool Manifest::loadParser(const void* p, size_t size, uint64_t sliceOffset, cons
             // 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)));
+                _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, installName)));
             }
         }
     } else {
-        _uuidMap.insert(std::make_pair(uuid, UUIDInfo(mh, size, sliceOffset, uuid, parser.archName(), runtimePath, buildPath, "")));
+        _uuidMap.insert(std::make_pair(uuid, UUIDInfo(ma, sliceLength, sliceOffset, uuid, archName, runtimePath, buildPath, "")));
     }
     return true;
 }
@@ -358,8 +388,8 @@ bool Manifest::loadParsers(const std::string& buildPath, const std::string& runt
         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 ( const FatFile* fh = FatFile::isFatFile(p) ) {
+        fh->forEachSlice(_diags, stat_buf.st_size, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
             if (loadParser(sliceStart, sliceSize, (uintptr_t)sliceStart-(uintptr_t)p, runtimePath, buildPath, architectures))
                 retval = true;
         });
@@ -369,6 +399,7 @@ bool Manifest::loadParsers(const std::string& buildPath, const std::string& runt
     return retval;
 }
 
+
 const Manifest::UUIDInfo& Manifest::infoForUUID(const UUID& uuid) const {
     auto i = _uuidMap.find(uuid);
     assert(i != _uuidMap.end());
@@ -387,8 +418,8 @@ const Manifest::UUIDInfo Manifest::infoForInstallNameAndarch(const std::string&
     return i->second;
 }
 
-MachOParser Manifest::parserForUUID(const UUID& uuid) const {
-    return MachOParser(infoForUUID(uuid).mh);
+const MachOAnalyzer* Manifest::machOForUUID(const UUID& uuid) const {
+    return infoForUUID(uuid).mh;
 }
 
 const std::string Manifest::buildPathForUUID(const UUID& uuid) {
@@ -398,22 +429,29 @@ const std::string Manifest::buildPathForUUID(const UUID& uuid) {
 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>())
+
+const std::string& Manifest::installNameForUUID(const UUID& uuid) {
+    return infoForUUID(uuid).installName;
+}
+
+
+Manifest::Manifest(Diagnostics& D, const std::string& path, bool onlyParseManifest)  : Manifest(D, path, std::set<std::string>(), onlyParseManifest)
 {
 }
 
-Manifest::Manifest(Diagnostics& D, const std::string& path, const std::set<std::string>& overlays) :
+Manifest::Manifest(Diagnostics& D, const std::string& path, const std::set<std::string>& overlays, bool onlyParseManifest) :
     _diags(D)
 {
-    NSMutableDictionary* manifestDict = [NSMutableDictionary dictionaryWithContentsOfFile:cppToObjStr(path)];
-    NSString*            platStr = manifestDict[@"platform"];
+    _manifestDict = [NSMutableDictionary dictionaryWithContentsOfFile:cppToObjStr(path)];
+    if (!_manifestDict)
+        return;
+    NSString*            platStr = _manifestDict[@"platform"];
     std::set<std::string> architectures;
 
     if (platStr == nullptr)
         platStr = @"ios";
     std::string platformString = [platStr UTF8String];
-    setMetabomFile([manifestDict[@"metabomFile"] UTF8String]);
+    setMetabomFile([_manifestDict[@"metabomFile"] UTF8String]);
 
     if (platformString == "ios") {
         setPlatform(dyld3::Platform::iOS);
@@ -430,25 +468,25 @@ Manifest::Manifest(Diagnostics& D, const std::string& path, const std::set<std::
         setPlatform(dyld3::Platform::iOS);
     }
 
-    for (NSString* project in manifestDict[@"projects"]) {
-        for (NSString* source in manifestDict[@"projects"][project]) {
+    for (NSString* project in _manifestDict[@"projects"]) {
+        for (NSString* source in _manifestDict[@"projects"][project]) {
             addProjectSource([project UTF8String], [source UTF8String]);
         }
     }
 
-    for (NSString* configuration in manifestDict[@"configurations"]) {
+    for (NSString* configuration in _manifestDict[@"configurations"]) {
         std::string configStr = [configuration UTF8String];
-        std::string configTag = [manifestDict[@"configurations"][configuration][@"metabomTag"] UTF8String];
+        std::string configTag = [_manifestDict[@"configurations"][configuration][@"metabomTag"] UTF8String];
 
-        if (manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) {
-            for (NSString* excludeTag in manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) {
+        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"]) {
+        if (_manifestDict[@"configurations"][configuration][@"metabomRestrictTags"]) {
+            for (NSString* restrictTag in _manifestDict[@"configurations"][configuration][@"metabomRestrictTags"]) {
                 _metabomRestrictedTagMap[configStr].insert([restrictTag UTF8String]);
                 _configurations[configStr].metabomRestrictTags.insert([restrictTag UTF8String]);
             }
@@ -457,7 +495,7 @@ Manifest::Manifest(Diagnostics& D, const std::string& path, const std::set<std::
         _configurations[configStr].metabomTag = configTag;
         _configurations[configStr].metabomTags.insert(configTag);
         _configurations[configStr].platformName =
-            [manifestDict[@"configurations"][configuration][@"platformName"] UTF8String];
+            [_manifestDict[@"configurations"][configuration][@"platformName"] UTF8String];
 
         if (endsWith(configStr, "InternalOS")) {
             _configurations[configStr].disposition = "internal";
@@ -494,7 +532,7 @@ Manifest::Manifest(Diagnostics& D, const std::string& path, const std::set<std::
             _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("OS"));
         }
 
-        for (NSString* architecutre in manifestDict[@"configurations"][configuration][@"architectures"]) {
+        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();
@@ -502,15 +540,18 @@ Manifest::Manifest(Diagnostics& D, const std::string& path, const std::set<std::
         }
     }
 
-    setVersion([manifestDict[@"manifest-version"] unsignedIntValue]);
-    setBuild([manifestDict[@"build"] UTF8String]);
-    if (manifestDict[@"dylibOrderFile"]) {
-        setDylibOrderFile([manifestDict[@"dylibOrderFile"] UTF8String]);
+    setVersion([_manifestDict[@"manifest-version"] unsignedIntValue]);
+    setBuild([_manifestDict[@"build"] UTF8String]);
+    if (_manifestDict[@"dylibOrderFile"]) {
+        setDylibOrderFile([_manifestDict[@"dylibOrderFile"] UTF8String]);
     }
-    if (manifestDict[@"dirtyDataOrderFile"]) {
-        setDirtyDataOrderFile([manifestDict[@"dirtyDataOrderFile"] UTF8String]);
+    if (_manifestDict[@"dirtyDataOrderFile"]) {
+        setDirtyDataOrderFile([_manifestDict[@"dirtyDataOrderFile"] UTF8String]);
     }
 
+    if (onlyParseManifest)
+        return;
+
     auto    metabom = MBMetabomOpen(metabomFile().c_str(), false);
     auto    metabomEnumerator = MBIteratorNewWithPath(metabom, ".", "");
     MBEntry entry;
@@ -518,7 +559,6 @@ Manifest::Manifest(Diagnostics& D, const std::string& path, const std::set<std::
     // 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);
@@ -549,7 +589,9 @@ Manifest::Manifest(Diagnostics& D, const std::string& path, const std::set<std::
 
         MBTag tag;
         auto  tagCount = MBEntryGetNumberOfProjectTags(entry);
-        if (entryType == BOMFileType && BOMFSObjectIsBinaryObject(fsObject) && MBEntryGetNumberOfProjectTags(entry) != 0 && tagCount != 0) {
+        bool isObjectFile = (entryType == BOMFileType) && BOMFSObjectIsBinaryObject(fsObject);
+        bool isSymlinkFile = entryType == BOMSymlinkType;
+        if ( (isObjectFile || isSymlinkFile) && MBEntryGetNumberOfProjectTags(entry) != 0 && tagCount != 0) {
             if (tagCount == 1) {
                 MBEntryGetProjectTags(entry, &tag);
             } else {
@@ -588,17 +630,35 @@ Manifest::Manifest(Diagnostics& D, const std::string& path, const std::set<std::
                 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 (isObjectFile) {
+                _metabomTagMap.insert(std::make_pair(entryPath, tagStrs));
+
+                bool foundParser = false;
+                for (const auto& overlay : overlays) {
+                    if (loadParsers(overlay + "/" + entryPath, entryPath, architectures)) {
+                        foundParser = true;
+                        _diags.verbose("Taking '%s' from overlay instead of dylib cache\n", entryPath.c_str());
+                        break;
+                    }
+                    if (_diags.hasError()) {
+                        _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
+                        _diags.clearError();
+                    }
                 }
-            }
 
-            if (!foundParser) {
-                (void)loadParsers(projectPath(projectName) + "/" + entryPath, entryPath, architectures);
+                if (!foundParser) {
+                    (void)loadParsers(projectPath(projectName) + "/" + entryPath, entryPath, architectures);
+                    if (_diags.hasError()) {
+                        _diags.verbose("Mach-O error: %s\n", _diags.errorMessage().c_str());
+                        _diags.clearError();
+                    } else {
+                        foundParser = true;
+                    }
+                }
+            } else if (isSymlinkFile) {
+                const char* target = BOMFSObjectSymlinkTarget(fsObject);
+                _symlinks.push_back({ entryPath, target });
+                _metabomSymlinkTagMap.insert(std::make_pair(entryPath, tagStrs));
             }
         }
     }
@@ -631,13 +691,17 @@ std::vector<DyldSharedCache::MappedMachO> Manifest::otherDylibsAndBundles(const
     const auto&                               dylibs = _configurations[configuration].architectures[architecture].results.dylibs;
     for (const auto& dylib : dylibs) {
         if (!dylib.second.included) {
-            insert(retval, dylib.second);
+            const UUIDInfo& info = infoForUUID(dylib.second.uuid);
+            if ( ((MachOAnalyzer*)(info.mh))->canHavePrecomputedDlopenClosure(info.runtimePath.c_str(), ^(const char*) {}) )
+                insert(retval, dylib.second);
         }
     }
 
     const auto& bundles = _configurations[configuration].architectures[architecture].results.bundles;
     for (const auto& bundle : bundles) {
-        insert(retval, bundle.second);
+        const UUIDInfo& info = infoForUUID(bundle.second.uuid);
+        if ( ((MachOAnalyzer*)(info.mh))->canHavePrecomputedDlopenClosure(info.runtimePath.c_str(), ^(const char*) {}) )
+            insert(retval, bundle.second);
     }
 
     return retval;
@@ -654,6 +718,8 @@ std::vector<DyldSharedCache::MappedMachO> Manifest::mainExecutables(const std::s
     return retval;
 }
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wrange-loop-analysis"
 bool Manifest::filterForConfig(const std::string& configName)
 {
     for (const auto configuration : _configurations) {
@@ -671,6 +737,21 @@ bool Manifest::filterForConfig(const std::string& configName)
     }
     return false;
 }
+#pragma clang diagnostic pop
+
+std::set<std::string> Manifest::resultsForConfiguration(const std::string& configName) {
+    std::set<std::string> results;
+    NSDictionary* configurationResults = _manifestDict[@"results"][[NSString stringWithUTF8String:configName.c_str()]];
+    for (NSString* arch in configurationResults) {
+        NSDictionary* dylibs = configurationResults[arch][@"dylibs"];
+        for (NSString* dylib in dylibs) {
+            NSDictionary* dylibDict = dylibs[dylib];
+            if ([dylibDict[@"included"] boolValue])
+                results.insert([dylib UTF8String]);
+        }
+    }
+    return results;
+}
 
 void Manifest::dedupeDispositions(void) {
     // Since this is all hacky and inference based for now only do it for iOS until XBS
@@ -719,88 +800,6 @@ void Manifest::remove(const std::string& config, const std::string& arch)
         _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)
 {
@@ -816,13 +815,12 @@ void Manifest::calculateClosure(const std::string& configuration, const std::str
         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);
-
         }
     }
 
@@ -837,41 +835,41 @@ void Manifest::calculateClosure(const std::string& configuration, const std::str
             }
             processedUuids.insert(uuid);
 
-            auto parser = parserForUUID(uuid);
+            const MachOAnalyzer* mh = machOForUUID(uuid);
             auto runtimePath = runtimePathForUUID(uuid);
-            assert(parser.header() != 0);
+            assert(mh != nullptr);
 
-            parser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+            mh->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) {
+            if (mh->isDylib()) {
                 // 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();
+                    archManifest.results.dylibs[uuid].installname = mh->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");
+                if ( (strcmp(mh->installName(), "/System/Library/Caches/com.apple.xpc/sdk.dylib") == 0)
+                    || (strcmp(mh->installName(), "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib") == 0) ) {
+                    archManifest.results.exclude(mh, "Device specific dylib");
                     continue;
                 }
 
-                std::set<std::string> reasons;
-                if (parser.canBePlacedInDyldCache(runtimePath, reasons)) {
+                __block std::set<std::string> reasons;
+                if (mh->canBePlacedInDyldCache(runtimePath.c_str(), ^(const char* reason) { reasons.insert(reason); })) {
                     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");
+                        archManifest.results.exclude(mh, "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());
+                    cachedUUIDs.insert(uuid);
                 } else {
                     // It can't be placed in the cache, print out the reasons why
                     std::string reasonString = "Rejected from cached dylibs: " + runtimePath + " " + architecture + " (\"";
@@ -882,13 +880,13 @@ void Manifest::calculateClosure(const std::string& configuration, const std::str
                         }
                     }
                     reasonString += "\")";
-                    archManifest.results.exclude(&parser, reasonString);
+                    archManifest.results.exclude(mh, reasonString);
                 }
-            } else if (parser.fileType() == MH_BUNDLE) {
+            } else if (mh->isBundle()) {
                 if (archManifest.results.bundles.count(uuid) == 0) {
                     archManifest.results.bundles[uuid].uuid = uuid;
                 }
-            } else if (parser.fileType() == MH_EXECUTE) {
+            } else if (mh->isMainExecutable()) {
                 //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"
@@ -911,8 +909,8 @@ void Manifest::calculateClosure(const std::string& configuration, const std::str
         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) {
+            const dyld3::MachOAnalyzer* mh = machOForUUID(uuid);
+            mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
                 if (isWeak)
                     return;
 
@@ -924,7 +922,7 @@ void Manifest::calculateClosure(const std::string& configuration, const std::str
                 }
 
                 if (badDependencies.size()) {
-                    std::string reasonString = "Rejected from cached dylibs: " + std::string(parser.installName()) + " " + architecture + " (\"";
+                    std::string reasonString = "Rejected from cached dylibs: " + std::string(mh->installName()) + " " + architecture + " (\"";
                     for (auto i = badDependencies.begin(); i != badDependencies.end(); ++i) {
                         reasonString += *i;
                         if (i != --badDependencies.end()) {
@@ -932,7 +930,7 @@ void Manifest::calculateClosure(const std::string& configuration, const std::str
                         }
                     }
                     reasonString += "\")";
-                    archManifest.results.exclude(&parser, reasonString);
+                    archManifest.results.exclude(mh, reasonString);
                 }
             });
         }
@@ -946,8 +944,8 @@ void Manifest::calculateClosure(const std::string& configuration, const std::str
     __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) {
+        const dyld3::MachOAnalyzer* mh = machOForUUID(uuid);
+        mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
             linkedDylibs.insert(loadPath);
         });
     }
@@ -1120,6 +1118,10 @@ void Manifest::write(const std::string& path)
         cacheDict[@"platform"] = @"macos";
         break;
     case Platform::unknown:
+    case Platform::iOSMac:
+    case Platform::iOS_simulator:
+    case Platform::tvOS_simulator:
+    case Platform::watchOS_simulator:
         cacheDict[@"platform"] = @"unknown";
         break;
     }
@@ -1131,4 +1133,38 @@ void Manifest::write(const std::string& path)
                                                                   error:&error];
     (void)[outData writeToFile:cppToObjStr(path) atomically:YES];
 }
+
+
+void Manifest::forEachMachO(std::string configuration,
+                            std::function<void(const std::string &buildPath, const std::string &runtimePath, const std::string &arch, bool shouldBeExcludedIfLeaf)> lambda) {
+    for (auto& uuidInfo : _uuidMap) {
+        auto i = _metabomTagMap.find(uuidInfo.second.runtimePath);
+        assert(i != _metabomTagMap.end());
+        auto restrictions = _metabomRestrictedTagMap.find(configuration);
+        if (restrictions != _metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, i->second)) {
+            continue;
+        }
+        auto& configManifest = _configurations[configuration];
+        auto exclusions = _metabomExcludeTagMap.find(configuration);
+        bool isExcluded = (exclusions != _metabomExcludeTagMap.end()) && !is_disjoint(exclusions->second, i->second);
+        bool isAnchor = !is_disjoint(i->second, configManifest.metabomTags);
+        bool shouldBeExcludedIfLeaf = isExcluded || !isAnchor;
+        lambda(uuidInfo.second.buildPath, uuidInfo.second.runtimePath, uuidInfo.second.arch, shouldBeExcludedIfLeaf);
+    }
+}
+
+
+void Manifest::forEachSymlink(std::string configuration,
+                            std::function<void(const std::string &fromPath, const std::string &toPath)> lambda) {
+    for (const auto& symlink : _symlinks) {
+        auto i = _metabomSymlinkTagMap.find(symlink.first);
+        assert(i != _metabomSymlinkTagMap.end());
+        auto restrictions = _metabomRestrictedTagMap.find(configuration);
+        if (restrictions != _metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, i->second)) {
+            continue;
+        }
+        lambda(symlink.first, symlink.second);
+    }
 }
+
+} //namespace dyld3
index 59aa455f497dd04acc473e1d14aef909e5250981..ec2965f71d8181553a10095935bc8fd14092b017 100644 (file)
@@ -221,13 +221,13 @@ public:
         }
     }
     
-    static void addPointers(uint8_t* methodList, std::vector<void*>& pointersToAdd) {
+    static void addPointers(uint8_t* methodList, CacheBuilder::ASLR_Tracker& aslrTracker) {
         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));
+            aslrTracker.add(&(entry.name));
+            aslrTracker.add(&(entry.types));
+            aslrTracker.add(&(entry.imp));
         }
     }
 
@@ -380,12 +380,12 @@ public:
         }
     }
 
-    static void addPointers(uint8_t* propertyList, std::vector<void*>& pointersToAdd) {
+    static void addPointers(uint8_t* propertyList, CacheBuilder::ASLR_Tracker& aslrTracker) {
         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));
+            aslrTracker.add(&(entry.name));
+            aslrTracker.add(&(entry.attributes));
         }
     }
 
@@ -474,18 +474,18 @@ public:
             P::setP(demangledName, cache->vmAddrForContent((void*)newName));
     }
 
-    void addPointers(std::vector<void*>& pointersToAdd) 
+    void addPointers(ContentAccessor* cache, CacheBuilder::ASLR_Tracker& aslrTracker)
     {
-        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);
+        aslrTracker.add(&isa);
+        aslrTracker.add(&name);
+        if (protocols)               aslrTracker.add(&protocols);
+        if (instanceMethods)         aslrTracker.add(&instanceMethods);
+        if (classMethods)            aslrTracker.add(&classMethods);
+        if (optionalInstanceMethods) aslrTracker.add(&optionalInstanceMethods);
+        if (optionalClassMethods)    aslrTracker.add(&optionalClassMethods);
+        if (instanceProperties)      aslrTracker.add(&instanceProperties);
+        if (extendedMethodTypes)     aslrTracker.add(&extendedMethodTypes);
+        if (demangledName)           aslrTracker.add(&demangledName);
     }
 };
 
@@ -531,10 +531,10 @@ public:
         }
     }
 
-     static void addPointers(uint8_t* protocolList, std::vector<void*>& pointersToAdd) {
+     static void addPointers(uint8_t* protocolList, CacheBuilder::ASLR_Tracker& aslrTracker) {
         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]);
+            aslrTracker.add(&plist->list[i]);
         }
     }
 
@@ -606,16 +606,16 @@ public:
         P::setP(baseProperties, cache->vmAddrForContent(proplist));
     }
     
-    void addMethodListPointer(std::vector<void*>& pointersToAdd) {
-        pointersToAdd.push_back(&this->baseMethods);
+    void addMethodListPointer(CacheBuilder::ASLR_Tracker& aslrTracker) {
+        aslrTracker.add(&this->baseMethods);
     }
     
-    void addPropertyListPointer(std::vector<void*>& pointersToAdd) {
-        pointersToAdd.push_back(&this->baseProperties);
+    void addPropertyListPointer(CacheBuilder::ASLR_Tracker& aslrTracker) {
+        aslrTracker.add(&this->baseProperties);
     }
     
-    void addProtocolListPointer(std::vector<void*>& pointersToAdd) {
-        pointersToAdd.push_back(&this->baseProtocols);
+    void addProtocolListPointer(CacheBuilder::ASLR_Tracker& aslrTracker) {
+        aslrTracker.add(&this->baseProtocols);
     }
 };
 
@@ -637,6 +637,8 @@ public:
 
     objc_class_t<P> *getSuperclass(ContentAccessor* cache) const { return (objc_class_t<P> *)cache->contentForVMAddr(P::getP(superclass)); }
     
+    const pint_t* getSuperClassAddress() const { return &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)); }
 
@@ -665,16 +667,16 @@ public:
         getData(cache)->setPropertyList(cache, proplist);
     }
     
-    void addMethodListPointer(ContentAccessor* cache, std::vector<void*>& pointersToAdd) {
-        getData(cache)->addMethodListPointer(pointersToAdd);
+    void addMethodListPointer(ContentAccessor* cache, CacheBuilder::ASLR_Tracker& aslrTracker) {
+        getData(cache)->addMethodListPointer(aslrTracker);
     }
     
-    void addPropertyListPointer(ContentAccessor* cache, std::vector<void*>& pointersToAdd) {
-        getData(cache)->addPropertyListPointer(pointersToAdd);
+    void addPropertyListPointer(ContentAccessor* cache, CacheBuilder::ASLR_Tracker& aslrTracker) {
+        getData(cache)->addPropertyListPointer(aslrTracker);
     }
     
-    void addProtocolListPointer(ContentAccessor* cache, std::vector<void*>& pointersToAdd) {
-        getData(cache)->addProtocolListPointer(pointersToAdd);
+    void addProtocolListPointer(ContentAccessor* cache, CacheBuilder::ASLR_Tracker& aslrTracker) {
+        getData(cache)->addProtocolListPointer(aslrTracker);
     }
     
 };
@@ -943,6 +945,8 @@ class SelectorOptimizer {
 
     V& mVisitor;
 
+    std::set<pint_t> selectorRefVMAddrs;
+
     friend class MethodListWalker<P, SelectorOptimizer<P,V> >;
     void visitMethodList(objc_method_list_t<P> *mlist)
     {
@@ -977,6 +981,7 @@ public:
             pint_t oldValue = selrefs.getVMAddress(i);
             pint_t newValue = mVisitor.visit(oldValue);
             selrefs.setVMAddress(i, newValue);
+            selectorRefVMAddrs.insert(selrefs.getSectionVMAddress() + (i * sizeof(pint_t)));
         }
 
         // message references
@@ -989,6 +994,10 @@ public:
             msg.setName(newValue);
         }
     }
+
+    bool isSelectorRefAddress(pint_t vmAddr) const {
+        return selectorRefVMAddrs.count(vmAddr);
+    }
 };
 
 
@@ -1083,7 +1092,8 @@ public:
 // Detect classes that have missing weak-import superclasses.
 template <typename P>
 class WeakClassDetector {
-    bool noMissing;
+    bool                                noMissing;
+    const std::map<void*, std::string>* missingWeakImports = nullptr;
 
     friend class ClassWalker<P, WeakClassDetector<P>>;
     void visitClass(ContentAccessor* cache, const macho_header<P>*, 
@@ -1098,18 +1108,26 @@ class WeakClassDetector {
         } 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));
+            // bad: cls's superclass is missing.
+            // See if we can find the name from the missing weak import map
+            auto it = missingWeakImports->find((void*)cls->getSuperClassAddress());
+            const char* dylibName = "unknown dylib";
+            if (it != missingWeakImports->end()) {
+                dylibName = it->second.c_str();
+            }
+            cache->diagnostics().warning("Superclass of class '%s' is weak-import and missing.  Expected in %s",
+                                         cls->getName(cache), dylibName);
             noMissing = false;
         }
     }
 
 public:
-    bool noMissingWeakSuperclasses(ContentAccessor* cache, 
+    bool noMissingWeakSuperclasses(ContentAccessor* cache,
+                                   const std::map<void*, std::string>& missingWeakImportsMap,
                                    std::vector<const macho_header<P>*> dylibs)
     {
-        noMissing = true;
+        noMissing           = true;
+        missingWeakImports  = &missingWeakImportsMap;
         ClassWalker<P, WeakClassDetector<P>> classes(*this);
         for (auto mh : dylibs) {
             classes.walk(cache, mh);
@@ -1197,7 +1215,7 @@ public:
         return nullptr;
     }
 
-    void update(ContentAccessor* cache, const macho_header<P>* mh, std::vector<void*>& pointersInData) {
+    void update(ContentAccessor* cache, const macho_header<P>* mh, CacheBuilder::ASLR_Tracker& aslrTracker) {
         InfoT* hi = new(&_hInfos[_count++]) InfoT(cache, mh);
         (void)hi;
     }
index 332e045baf7a50719c209f49ab1198df7dae7cfb..805f03346b26b0d8fa3619ab4b0ceccb1086e50c 100644 (file)
@@ -39,7 +39,7 @@
 #include "StringUtils.h"
 #include "Trie.hpp"
 #include "MachOFileAbstraction.hpp"
-#include "MachOParser.h"
+#include "MachOAnalyzer.h"
 #include "Diagnostics.h"
 #include "DyldSharedCache.h"
 #include "CacheBuilder.h"
@@ -330,7 +330,7 @@ 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 textRegionStartAddr, uint64_t poolLinkEditStartAddr, uint64_t poolLinkEditFileOffset, Diagnostics& diags);
 
     uint64_t                addr() { return _startAddr; }
     uint64_t                getForwardBranch(uint64_t finalTargetAddr, const char* name, std::vector<BranchPoolDylib<P>*>& branchIslandPools);
@@ -366,15 +366,16 @@ private:
 
 template <typename P>
 BranchPoolDylib<P>::BranchPoolDylib(DyldSharedCache* cache, uint64_t poolStartAddr,
-                                     uint64_t textRegionStartAddr, uint64_t poolLinkEditStartAddr, uint64_t poolLinkEditStartOffset, Diagnostics& diags)
+                                     uint64_t textRegionStartAddr, uint64_t poolLinkEditStartAddr, uint64_t poolLinkEditFileOffset, 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 int64_t  cacheSlide = (long)cache - cache->unslidLoadAddress();
     const uint64_t textSegSize = branchPoolTextSize(archName);
     const uint64_t linkEditSegSize = branchPoolLinkEditSize(archName);
-    const unsigned stubCount = (unsigned)((textSegSize - _firstStubOffset)/4);
+    const unsigned stubCount = (unsigned)((textSegSize - _firstStubOffset)/sizeof(uint32_t));
     const uint32_t linkeditOffsetSymbolTable = 0;
     const uint32_t linkeditOffsetIndirectSymbolTable = stubCount*sizeof(macho_nlist<P>);
     const uint32_t linkeditOffsetSymbolPoolOffset = linkeditOffsetIndirectSymbolTable + stubCount*sizeof(uint32_t);
@@ -383,8 +384,8 @@ BranchPoolDylib<P>::BranchPoolDylib(DyldSharedCache* cache, uint64_t poolStartAd
     // 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_cputype(dyld3::MachOFile::cpuTypeFromArchName(archName.c_str()));
+    mh->set_cpusubtype(dyld3::MachOFile::cpuSubtypeFromArchName(archName.c_str()));
     mh->set_filetype(MH_DYLIB);
     mh->set_ncmds(6);
     mh->set_sizeofcmds(is64 ? 0x210 : 100); // FIXME: 32-bit size
@@ -423,7 +424,7 @@ BranchPoolDylib<P>::BranchPoolDylib(DyldSharedCache* cache, uint64_t poolStartAd
     linkEditSegCmd->set_segname("__LINKEDIT");
     linkEditSegCmd->set_vmaddr(poolLinkEditStartAddr);
     linkEditSegCmd->set_vmsize(linkEditSegSize);
-    linkEditSegCmd->set_fileoff(poolLinkEditStartOffset);
+    linkEditSegCmd->set_fileoff(poolLinkEditFileOffset);
     linkEditSegCmd->set_filesize(linkEditSegSize);
     linkEditSegCmd->set_maxprot(PROT_READ);
     linkEditSegCmd->set_initprot(PROT_READ);
@@ -445,8 +446,8 @@ BranchPoolDylib<P>::BranchPoolDylib(DyldSharedCache* cache, uint64_t poolStartAd
     _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_symoff((uint32_t)(poolLinkEditFileOffset + linkeditOffsetSymbolTable));
+    _symbolTableCmd->set_stroff((uint32_t)(poolLinkEditFileOffset + linkeditOffsetSymbolPoolOffset));
     _symbolTableCmd->set_strsize((uint32_t)(linkEditSegSize - linkeditOffsetSymbolPoolOffset));
     // LC_DYSYMTAB
     cmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
@@ -465,7 +466,7 @@ BranchPoolDylib<P>::BranchPoolDylib(DyldSharedCache* cache, uint64_t poolStartAd
     _dynamicSymbolTableCmd->set_nmodtab(0);
     _dynamicSymbolTableCmd->set_extrefsymoff(0);
     _dynamicSymbolTableCmd->set_nextrefsyms(0);
-    _dynamicSymbolTableCmd->set_indirectsymoff((uint32_t)(poolLinkEditStartOffset + linkeditOffsetIndirectSymbolTable));
+    _dynamicSymbolTableCmd->set_indirectsymoff((uint32_t)(poolLinkEditFileOffset + linkeditOffsetIndirectSymbolTable));
     _dynamicSymbolTableCmd->set_nindirectsyms(stubCount);
     _dynamicSymbolTableCmd->set_extreloff(0);
     _dynamicSymbolTableCmd->set_nextrel(0);
@@ -480,15 +481,15 @@ BranchPoolDylib<P>::BranchPoolDylib(DyldSharedCache* cache, uint64_t poolStartAd
 
     // write stubs section content
     _stubInstructions = (uint32_t*)((uint8_t*)mh + _firstStubOffset);
-    for (int i=0; i < stubCount; ++i) {
+    for (unsigned i=0; i < stubCount; ++i) {
         E::set32(_stubInstructions[i], 0xD4200000);
     }
 
     // write linkedit content
-    uint8_t* linkeditBufferStart = (uint8_t*)cache + poolLinkEditStartOffset;
+    uint8_t* linkeditBufferStart = (uint8_t*)poolLinkEditStartAddr + cacheSlide;
     // write symbol table
     _symbolTable = (macho_nlist<P>*)(linkeditBufferStart);
-    for (int i=0; i < stubCount; ++i) {
+    for (unsigned 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);
@@ -497,7 +498,7 @@ BranchPoolDylib<P>::BranchPoolDylib(DyldSharedCache* cache, uint64_t poolStartAd
     }
     // write indirect symbol table
     uint32_t* indirectSymboTable = (uint32_t*)(linkeditBufferStart + linkeditOffsetIndirectSymbolTable);
-    for (int i=0; i < stubCount; ++i) {
+    for (unsigned i=0; i < stubCount; ++i) {
         P::E::set32(indirectSymboTable[i], i);
     }
     // write string pool
@@ -517,7 +518,7 @@ void BranchPoolDylib<P>::finalizeLoadCommands()
     _dynamicSymbolTableCmd->set_nundefsym(_nextIndex);
 
     uint8_t digest[CC_MD5_DIGEST_LENGTH];
-    CC_MD5(_stubInstructions, _maxStubs*sizeof(uint64_t), digest);
+    CC_MD5(_stubInstructions, _maxStubs*sizeof(uint32_t), digest);
     _uuidCmd->set_uuid(digest);
 
     if ( verbose ) {
@@ -647,13 +648,12 @@ void BranchPoolDylib<P>::printStats()
 template <typename P>
 class StubOptimizer {
 public:
-                            StubOptimizer(void* cacheBuffer, macho_header<P>* mh, Diagnostics& diags);
+                            StubOptimizer(const DyldSharedCache* cache, 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(); }
+    const uint8_t*          exportsTrie() { return &_linkeditBias[_dyldInfo->export_off()]; }
     uint32_t                exportsTrieSize() { return _dyldInfo->export_size(); }
 
     uint32_t                                _stubCount           = 0;
@@ -674,6 +674,9 @@ private:
     void                    optimizeArmCallSites();
     void                    optimizeArmStubs();
     uint64_t                lazyPointerAddrFromArm64Stub(const uint8_t* stubInstructions, uint64_t stubVMAddr);
+#if SUPPORT_ARCH_arm64e
+    uint64_t                lazyPointerAddrFromArm64eStub(const uint8_t* stubInstructions, uint64_t stubVMAddr);
+#endif
     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,
@@ -688,9 +691,10 @@ private:
 
 
     macho_header<P>*                        _mh;
-    void*                                   _cacheBuffer;
+    int64_t                                 _cacheSlide          = 0;
+    uint64_t                                _cacheUnslideAddr    = 0;
+    bool                                    _chainedFixups       = false;
     uint32_t                                _linkeditSize        = 0;
-    uint32_t                                _linkeditCacheOffset = 0;
     uint64_t                                _linkeditAddr        = 0;
     const uint8_t*                          _linkeditBias        = nullptr;
     const char*                             _installName         = nullptr;
@@ -703,18 +707,23 @@ private:
     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;
+    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)
+StubOptimizer<P>::StubOptimizer(const DyldSharedCache* cache, macho_header<P>* mh, Diagnostics& diags)
+: _mh(mh), _diagnostics(diags)
 {
-    _linkeditBias = (uint8_t*)cacheBuffer;
+    _cacheSlide = (long)cache - cache->unslidLoadAddress();
+    _cacheUnslideAddr = cache->unslidLoadAddress();
+#if SUPPORT_ARCH_arm64e
+    _chainedFixups = (strcmp(cache->archName(), "arm64e") == 0);
+#else
+    _chainedFixups = false;
+#endif
     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;
@@ -742,13 +751,12 @@ StubOptimizer<P>::StubOptimizer(void* cacheBuffer, macho_header<P>* mh, Diagnost
                 segCmd =( macho_segment_command<P>*)cmd;
                 _segCmds.push_back(segCmd);
                 if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
+                    _linkeditBias        = (uint8_t*)(segCmd->vmaddr() + _cacheSlide - segCmd->fileoff());
                     _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) {
@@ -819,15 +827,50 @@ uint64_t StubOptimizer<P>::lazyPointerAddrFromArm64Stub(const uint8_t* stubInstr
     return (stubVMAddr & (-4096)) + adrpValue*4096 + ldrValue*8;
 }
 
+#if SUPPORT_ARCH_arm64e
+template <typename P>
+uint64_t StubOptimizer<P>::lazyPointerAddrFromArm64eStub(const uint8_t* stubInstructions, uint64_t stubVMAddr)
+{
+    uint32_t stubInstr1 = E::get32(*(uint32_t*)stubInstructions);
+    // ADRP  X17, dyld_mageLoaderCache@page
+    if ( (stubInstr1 & 0x9F00001F) != 0x90000011 ) {
+        _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;
+
+    // ADD     X17, X17, dyld_mageLoaderCache@pageoff
+    uint32_t stubInstr2 = E::get32(*(uint32_t*)(stubInstructions + 4));
+    if ( (stubInstr2 & 0xFFC003FF) != 0x91000231 ) {
+        _diagnostics.warning("second instruction of stub (0x%08X) is not ADD for stub at addr 0x%0llX in %s",
+                             stubInstr2, (uint64_t)stubVMAddr, _installName);
+        return 0;
+    }
+    uint32_t addValue = ((stubInstr2 & 0x003FFC00) >> 10);
+
+    // LDR   X16, [X17]
+    uint32_t stubInstr3 = E::get32(*(uint32_t*)(stubInstructions + 8));
+    if ( stubInstr3 != 0xF9400230 ) {
+        _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;
+    }
+    return (stubVMAddr & (-4096)) + adrpValue*4096 + addValue;
+}
+#endif
+
 
 
 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_nlist<P>* symbolTable = (const macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
+    const char* symbolStrings = (char*)(&_linkeditBias[_symTabCmd->stroff()]);
+    const uint32_t* const indirectTable = (uint32_t*)(&_linkeditBias[_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;
@@ -850,6 +893,7 @@ void StubOptimizer<P>::buildStubMap(const std::unordered_set<std::string>& never
                         switch ( symbolIndex ) {
                             case INDIRECT_SYMBOL_ABS:
                             case INDIRECT_SYMBOL_LOCAL:
+                            case INDIRECT_SYMBOL_ABS | INDIRECT_SYMBOL_LOCAL:
                                 break;
                             default:
                                 if ( symbolIndex >= _symTabCmd->nsyms() ) {
@@ -866,14 +910,20 @@ void StubOptimizer<P>::buildStubMap(const std::unordered_set<std::string>& never
                                 }
                                 const char* symName = &symbolStrings[stringOffset];
                                 if ( neverStubEliminate.count(symName) ) {
-                                    //verboseLog("not bypassing stub to %s in %s because target is interposable\n", symName, _installName);
+                                    //fprintf(stderr, "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();
+                                const uint8_t* stubInstrs = (uint8_t*)(long)stubVMAddr + _cacheSlide;
                                 pint_t targetLPAddr = 0;
                                 switch ( _mh->cputype() ) {
                                     case CPU_TYPE_ARM64:
-                                        targetLPAddr = (pint_t)lazyPointerAddrFromArm64Stub(stubInstrs, stubVMAddr);
+                                    case CPU_TYPE_ARM64_32:
+#if SUPPORT_ARCH_arm64e
+                                        if (_mh->cpusubtype() == CPU_SUBTYPE_ARM64_E)
+                                            targetLPAddr = (pint_t)lazyPointerAddrFromArm64eStub(stubInstrs, stubVMAddr);
+                                        else
+#endif
+                                            targetLPAddr = (pint_t)lazyPointerAddrFromArm64Stub(stubInstrs, stubVMAddr);
                                         break;
                                     case CPU_TYPE_ARM:
                                         targetLPAddr = (pint_t)lazyPointerAddrFromArmStub(stubInstrs, (uint32_t)stubVMAddr);
@@ -885,9 +935,9 @@ void StubOptimizer<P>::buildStubMap(const std::unordered_set<std::string>& never
                         }
                     }
                 }
-                else if ( sectionType == S_LAZY_SYMBOL_POINTERS ) {
+                else if ( (sectionType == S_LAZY_SYMBOL_POINTERS) || (sectionType == S_NON_LAZY_SYMBOL_POINTERS) ) {
                     pint_t lpVMAddr;
-                    pint_t* lpContent = (pint_t*)(((uint8_t*)_cacheBuffer) + sect->offset());
+                    pint_t* lpContent = (pint_t*)(sect->addr() + _cacheSlide);
                     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();
@@ -897,9 +947,24 @@ void StubOptimizer<P>::buildStubMap(const std::unordered_set<std::string>& never
                         switch ( symbolIndex ) {
                             case INDIRECT_SYMBOL_ABS:
                             case INDIRECT_SYMBOL_LOCAL:
+                            case INDIRECT_SYMBOL_LOCAL|INDIRECT_SYMBOL_ABS:
                                 break;
                             default:
                                 lpValue = (pint_t)P::getP(lpContent[j]);
+
+                                // Fixup threaded rebase/bind
+                                if ( _chainedFixups ) {
+                                    dyld3::MachOLoaded::ChainedFixupPointerOnDisk ptr;
+                                    ptr.raw = lpValue;
+                                    assert(ptr.authRebase.bind == 0);
+                                    if ( ptr.authRebase.auth ) {
+                                        lpValue = (pint_t)(_cacheUnslideAddr + ptr.authRebase.target);
+                                    }
+                                    else {
+                                        lpValue = (pint_t)ptr.plainRebase.signExtendedTarget();
+                                    }
+                                }
+
                                 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",
@@ -915,7 +980,7 @@ void StubOptimizer<P>::buildStubMap(const std::unordered_set<std::string>& never
                                 }
                                 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);
+                                    //fprintf(stderr, "skipping lazy pointer at 0x%0lX to %s in %s because target is within dylib\n", (long)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",
@@ -948,7 +1013,7 @@ void StubOptimizer<P>::forEachCallSiteToAStub(CallSiteHandler handler)
         return;
     }
 
-    uint8_t* textSectionContent = (uint8_t*)_cacheBuffer + _textSegCacheOffset + _textSection->addr() -_textSegStartAddr;
+    uint8_t* textSectionContent = (uint8_t*)(_textSection->addr() + _cacheSlide);
 
     // Whole         :== <count> FromToSection+
     // FromToSection :== <from-sect-index> <to-sect-index> <count> ToOffset+
@@ -967,8 +1032,8 @@ void StubOptimizer<P>::forEachCallSiteToAStub(CallSiteHandler handler)
             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);
+                if ( kind > 13 ) {
+                    _diagnostics.error("bad kind (%llu) value in %s\n", kind, _installName);
                 }
                 uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd);
                 uint64_t fromSectionOffset = 0;
@@ -1127,11 +1192,12 @@ void StubOptimizer<P>::optimizeArmStubs()
         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
+        uint32_t* stubInstructions = (uint32_t*)((uint8_t*)(long)stubVMAddr + _cacheSlide);
+        assert(stubInstructions[0] == 0xe59fc004);
+        stubInstructions[0] = 0xe59fc000;  //      ldr    ip, L0
+        stubInstructions[1] = 0xe08ff00c;  //      add    pc, pc, ip
+        stubInstructions[2] = delta;       // L0:  .long  xxxx
+        stubInstructions[3] = 0xe7ffdefe;  //      trap
         _stubOptimizedCount++;
     }
 }
@@ -1172,7 +1238,7 @@ void StubOptimizer<P>::optimizeArm64CallSites(std::vector<BranchPoolDylib<P>*>&
         if ( (deltaToFinalTarget > -b128MegLimit) && (deltaToFinalTarget < b128MegLimit) ) {
             instruction= (instruction & 0xFC000000) | ((deltaToFinalTarget >> 2) & 0x03FFFFFF);
             _branchesDirectCount++;
-            return true;
+           return true;
         }
         // find closest branch island pool between instruction and target and get island
         const auto& pos3 = _targetAddrToName.find((pint_t)finalTargetAddr);
@@ -1232,6 +1298,7 @@ void StubOptimizer<P>::optimizeCallSites(std::vector<BranchPoolDylib<P>*>& branc
 
     switch ( _mh->cputype() ) {
         case CPU_TYPE_ARM64:
+        case CPU_TYPE_ARM64_32:
             optimizeArm64CallSites(branchIslandPools);
              if ( verbose ) {
                 _diagnostics.verbose("%5u branches in __text, %5u changed to direct branches, %5u changed to use islands for %s\n",
@@ -1251,14 +1318,15 @@ void StubOptimizer<P>::optimizeCallSites(std::vector<BranchPoolDylib<P>*>& branc
 
 template <typename P>
 void bypassStubs(DyldSharedCache* cache, const std::string& archName, const std::vector<uint64_t>& branchPoolStartAddrs,
-                const char* const neverStubEliminateDylibs[], Diagnostics& diags)
+                  uint64_t branchPoolsLinkEditStartAddr, uint64_t branchPoolsLinkEditStartFileOffset,
+                  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));
+        optimizers.push_back(new StubOptimizer<P>(cache, (macho_header<P>*)mh, diags));
     });
 
     // construct a BranchPoolDylib for each pool
@@ -1282,25 +1350,22 @@ void bypassStubs(DyldSharedCache* cache, const std::string& archName, const std:
         });
         __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;
+            ((dyld3::MachOFile*)mh)->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool &stop) {
+                if ( strcmp(info.segName, "__LINKEDIT") == 0 ) {
+                    if ( info.fileOffset >= lastLinkEditRegionUsedOffset )
+                        lastLinkEditRegionUsedOffset = info.fileOffset + info.vmSize;
                 }
             });
         });
-        uint64_t allPoolsLinkEditStartOffset = lastLinkEditRegionUsedOffset;
-        uint64_t allPoolsLinkEditStartAddr =  linkEditRegionStartAddr + allPoolsLinkEditStartOffset - linkEditRegionStartCacheOffset;
-        uint64_t allPoolsLinkEditSize = linkEditRegionEndAddr - allPoolsLinkEditStartAddr;
+        uint64_t allPoolsLinkEditStartAddr =  branchPoolsLinkEditStartAddr;
         if ( !branchPoolStartAddrs.empty() ) {
-            uint64_t poolLinkEditStartAddr = allPoolsLinkEditStartAddr;
-            uint64_t poolLinkEditStartOffset = allPoolsLinkEditStartOffset;
-            const uint64_t poolSize = (allPoolsLinkEditSize/branchPoolStartAddrs.size()) & (-4096);
+            uint64_t poolLinkEditStartAddr  = allPoolsLinkEditStartAddr;
+            uint64_t poolLinkEditFileOffset = branchPoolsLinkEditStartFileOffset;
+            const uint64_t poolSize = branchPoolLinkEditSize("arm64");
             for (uint64_t poolAddr : branchPoolStartAddrs) {
-                pools.push_back(new BranchPoolDylib<P>(cache, poolAddr, textRegionStartAddr, poolLinkEditStartAddr, poolLinkEditStartOffset, diags));
-                poolLinkEditStartAddr += poolSize;
-                poolLinkEditStartOffset += poolSize;
+                pools.push_back(new BranchPoolDylib<P>(cache, poolAddr, textRegionStartAddr, poolLinkEditStartAddr, poolLinkEditFileOffset, diags));
+                poolLinkEditStartAddr  += poolSize;
+                poolLinkEditFileOffset += poolSize;
             }
         }
     }
@@ -1361,13 +1426,20 @@ void bypassStubs(DyldSharedCache* cache, const std::string& archName, const std:
 
 }
 
-void bypassStubs(DyldSharedCache* cache, const std::vector<uint64_t>& branchPoolStartAddrs, const char* const neverStubEliminateDylibs[], Diagnostics& diags)
+void CacheBuilder::optimizeAwayStubs(const std::vector<uint64_t>& branchPoolStartAddrs, uint64_t branchPoolsLinkEditStartAddr)
 {
-    std::string archName = cache->archName();
+    DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    uint64_t branchPoolsLinkEditStartFileOffset = _readOnlyRegion.cacheFileOffset + branchPoolsLinkEditStartAddr - _readOnlyRegion.unslidLoadAddress;
+    std::string archName = dyldCache->archName();
+#if SUPPORT_ARCH_arm64_32
+    if ( startsWith(archName, "arm64_32") )
+        bypassStubs<Pointer32<LittleEndian> >(dyldCache, archName, branchPoolStartAddrs, branchPoolsLinkEditStartAddr, branchPoolsLinkEditStartFileOffset, _s_neverStubEliminate, _diagnostics);
+    else
+#endif
     if ( startsWith(archName, "arm64") )
-        bypassStubs<Pointer64<LittleEndian>>(cache, archName, branchPoolStartAddrs, neverStubEliminateDylibs, diags);
+        bypassStubs<Pointer64<LittleEndian> >(dyldCache, archName, branchPoolStartAddrs, branchPoolsLinkEditStartAddr, branchPoolsLinkEditStartFileOffset, _s_neverStubEliminate, _diagnostics);
     else if ( archName == "armv7k" )
-        bypassStubs<Pointer32<LittleEndian>>(cache, archName, branchPoolStartAddrs, neverStubEliminateDylibs, diags);
+        bypassStubs<Pointer32<LittleEndian>>(dyldCache, archName, branchPoolStartAddrs, branchPoolsLinkEditStartAddr, branchPoolsLinkEditStartFileOffset, _s_neverStubEliminate, _diagnostics);
     // no stub optimization done for other arches
 }
 
index b8ac19f0f2ba3a49711636f1b79d2f8738362189..ef5db27153db2e353f7eb5f329d3971bf53bd5d2 100644 (file)
@@ -40,7 +40,7 @@
 #include "Trie.hpp"
 #include "DyldSharedCache.h"
 #include "CacheBuilder.h"
-
+#include "MachOLoaded.h"
 
 #define ALIGN_AS_TYPE(value, type) \
         ((value + alignof(type) - 1) & (-alignof(type)))
@@ -58,21 +58,15 @@ public:
 
     // 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) {
+        for (auto& entry : _map) {
+            const std::string& symName = entry.first;
             // 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]) {
+            for (uint32_t symbolIndex : entry.second) {
                 symbolTable[symbolIndex].set_n_strx(poolOffset);
             }
             poolOffset += symName.size() + 1;
@@ -91,10 +85,11 @@ public:
 
 
 private:
-    std::unordered_map<std::string, std::vector<uint32_t>> _map;
+    std::map<std::string, std::vector<uint32_t>> _map;
 };
 
 
+} // anonymous namespace
 
 
 struct LocalSymbolInfo
@@ -111,7 +106,6 @@ 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);
@@ -144,6 +138,9 @@ public:
     const std::vector<macho_segment_command<P>*>&  segCmds() { return _segCmds; }
 
 
+    static void optimizeLinkedit(CacheBuilder& builder);
+    static void mergeLinkedits(CacheBuilder& builder, std::vector<LinkeditOptimizer<P>*>& optimizers);
+
 private:
 
     typedef typename P::uint_t pint_t;
@@ -153,7 +150,6 @@ private:
     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;
@@ -421,6 +417,7 @@ AcceleratorTables<P>::AcceleratorTables(DyldSharedCache* cache, uint64_t linkedi
         }
     }
 
+
     // 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];
@@ -527,10 +524,11 @@ 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;
+    uint64_t    textSegAddr = 0;
+    int64_t     slide = 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;
@@ -579,10 +577,14 @@ LinkeditOptimizer<P>::LinkeditOptimizer(void* cacheBuffer, macho_header<P>* mh,
             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();
+                if ( strcmp(segCmd->segname(), "__TEXT") == 0 ) {
+                    textSegAddr = segCmd->vmaddr();
+                    slide = (uint64_t)mh - textSegAddr;
+                }
+                else if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
                     _linkeditAddr        = segCmd->vmaddr();
+                    _linkeditBias        = (uint8_t*)mh + (_linkeditAddr - textSegAddr) - segCmd->fileoff();
+                    _linkeditSize        = (uint32_t)segCmd->vmsize();
                 }
                 else if ( segCmd->nsects() > 0 ) {
                     macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
@@ -590,7 +592,7 @@ LinkeditOptimizer<P>::LinkeditOptimizer(void* cacheBuffer, macho_header<P>* mh,
                     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 pint_t* inits = (pint_t*)(sect->addr()+slide);
                             const size_t count = sect->size() / sizeof(pint_t);
                             for (size_t j=0; j < count; ++j) {
                                 uint64_t func = P::getP(inits[j]);
@@ -947,7 +949,7 @@ void LinkeditOptimizer<P>::copyIndirectSymbolTable(uint8_t* newLinkEditContent,
     _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) {
+    for (uint32_t 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);
@@ -958,63 +960,59 @@ void LinkeditOptimizer<P>::copyIndirectSymbolTable(uint8_t* newLinkEditContent,
 }
 
 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)
+void LinkeditOptimizer<P>::mergeLinkedits(CacheBuilder& builder, 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;
+    uint64_t totalUnoptLinkeditsSize = builder._readOnlyRegion.sizeInUse - builder._nonLinkEditReadOnlySize;
     uint8_t* newLinkEdit = (uint8_t*)calloc(totalUnoptLinkeditsSize, 1);
     SortedStringPool<P> stringPool;
     uint32_t offset = 0;
 
-    diagnostics.verbose("Merged LINKEDIT:\n");
+    builder._diagnostics.verbose("Merged LINKEDIT:\n");
 
     // copy weak binding info
     uint32_t startWeakBindInfosOffset = offset;
     for (LinkeditOptimizer<P>* op : optimizers) {
-        op->copyWeakBindingInfo(newLinkEdit, offset);
+        // Skip chained fixups as the in-place linked list isn't valid any more
+        const dyld3::MachOFile* mf = (dyld3::MachOFile*)op->machHeader();
+        if (!mf->hasChainedFixups())
+            op->copyWeakBindingInfo(newLinkEdit, offset);
     }
-    diagnostics.verbose("  weak bindings size:      %5uKB\n", (uint32_t)(offset-startWeakBindInfosOffset)/1024);
+    builder._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);
+    builder._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);
+            // Skip chained fixups as the in-place linked list isn't valid any more
+            const dyld3::MachOFile* mf = (dyld3::MachOFile*)op->machHeader();
+            if (!mf->hasChainedFixups())
+                op->copyBindingInfo(newLinkEdit, offset);
         }
-        diagnostics.verbose("  bindings size:           %5uKB\n", (uint32_t)(offset-startBindingsInfosOffset)/1024);
+        builder._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);
+            // Skip chained fixups as the in-place linked list isn't valid any more
+            const dyld3::MachOFile* mf = (dyld3::MachOFile*)op->machHeader();
+            if (!mf->hasChainedFixups())
+                op->copyLazyBindingInfo(newLinkEdit, offset);
         }
-        diagnostics.verbose("  lazy bindings size:      %5uKB\n", (offset-startLazyBindingsInfosOffset)/1024);
+        builder._diagnostics.verbose("  lazy bindings size:      %5uKB\n", (offset-startLazyBindingsInfosOffset)/1024);
     }
 
     // copy symbol table entries
     std::vector<macho_nlist<P>> unmappedLocalSymbols;
-    if ( dontMapLocalSymbols )
+    if ( builder._options.excludeLocalSymbols )
         unmappedLocalSymbols.reserve(0x01000000);
     std::vector<LocalSymbolInfo> localSymbolInfos;
         localSymbolInfos.reserve(optimizers.size());
@@ -1024,7 +1022,7 @@ uint64_t mergeLinkedits(DyldSharedCache* cache, bool dontMapLocalSymbols, bool a
     uint32_t sharedSymbolTableExportsCount = 0;
     uint32_t sharedSymbolTableImportsCount = 0;
     for (LinkeditOptimizer<P>* op : optimizers) {
-         op->copyLocalSymbols(newLinkEdit, stringPool, offset, symbolIndex, dontMapLocalSymbols,
+         op->copyLocalSymbols(newLinkEdit, stringPool, offset, symbolIndex, builder._options.excludeLocalSymbols,
                              localSymbolInfos, unmappedLocalSymbols, localSymbolsStringPool);
         uint32_t x = symbolIndex;
         op->copyExportedSymbols(newLinkEdit, stringPool, offset, symbolIndex);
@@ -1041,14 +1039,14 @@ uint64_t mergeLinkedits(DyldSharedCache* cache, bool dontMapLocalSymbols, bool a
     for (LinkeditOptimizer<P>* op : optimizers) {
         op->copyFunctionStarts(newLinkEdit, offset);
     }
-    diagnostics.verbose("  function starts size:    %5uKB\n", (offset-startFunctionStartsOffset)/1024);
+    builder._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);
+    builder._diagnostics.verbose("  data in code size:       %5uKB\n", (offset-startDataInCodeOffset)/1024);
 
     // copy indirect symbol tables
     for (LinkeditOptimizer<P>* op : optimizers) {
@@ -1063,41 +1061,38 @@ uint64_t mergeLinkedits(DyldSharedCache* cache, bool dontMapLocalSymbols, bool a
     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);
+    uint64_t newLinkeditAlignedSize = align(offset, 14);
+    builder._diagnostics.verbose("  symbol table size:       %5uKB (%d exports, %d imports)\n", (sharedSymbolTableEndOffset-sharedSymbolTableStartOffset)/1024, sharedSymbolTableExportsCount, sharedSymbolTableImportsCount);
+    builder._diagnostics.verbose("  symbol string pool size: %5uKB\n", sharedSymbolStringsSize/1024);
+    builder._sharedStringsPoolVmOffset = (uint32_t)((builder._readOnlyRegion.unslidLoadAddress - builder._readExecuteRegion.unslidLoadAddress) + builder._nonLinkEditReadOnlySize + sharedSymbolStringsOffset);
 
     // 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);
+    builder._diagnostics.verbose("LINKEDITS optimized from %uMB to %uMB\n", (uint32_t)totalUnoptLinkeditsSize/(1024*1024), (uint32_t)newLinkeditUnalignedSize/(1024*1024));
+    ::memcpy(builder._readOnlyRegion.buffer+builder._nonLinkEditReadOnlySize, newLinkEdit, newLinkeditAlignedSize);
     ::free(newLinkEdit);
+    builder._readOnlyRegion.sizeInUse = builder._nonLinkEditReadOnlySize + newLinkeditAlignedSize;
 
     // If making cache for customers, add extra accelerator tables for dyld
-    if ( addAcceleratorTables ) {
-        AcceleratorTables<P> tables(cache, linkeditStartAddr, diagnostics, optimizers);
+    DyldSharedCache* cacheHeader = (DyldSharedCache*)builder._readExecuteRegion.buffer;
+    if ( builder._options.optimizeStubs ) {
+        uint64_t addrWhereAccTablesWillBe     = builder._readOnlyRegion.unslidLoadAddress+builder._readOnlyRegion.sizeInUse;
+        uint64_t addrWhereMergedLinkWillStart = builder._readOnlyRegion.unslidLoadAddress+builder._nonLinkEditReadOnlySize;
+        AcceleratorTables<P> tables(cacheHeader, addrWhereMergedLinkWillStart, builder._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));
+        if ( tablesSize < (builder._readOnlyRegion.bufferSize - builder._readOnlyRegion.sizeInUse) ) {
+            tables.copyTo(builder._readOnlyRegion.buffer+builder._readOnlyRegion.sizeInUse);
+            cacheHeader->header.accelerateInfoAddr = addrWhereAccTablesWillBe;
+            cacheHeader->header.accelerateInfoSize = tablesSize;
+            builder._readOnlyRegion.sizeInUse += align(tablesSize, 14);
+            builder._diagnostics.verbose("Accelerator tables %uMB\n", (uint32_t)tablesSize/(1024*1024));
        }
         else {
-            diagnostics.warning("not enough room to add dyld accelerator tables");
+            builder._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;
+    if ( builder._options.excludeLocalSymbols ) {
         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
@@ -1106,49 +1101,59 @@ uint64_t mergeLinkedits(DyldSharedCache* cache, bool dontMapLocalSymbols, bool a
         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;
+        vm_address_t localsBuffer;
+        if ( ::vm_allocate(mach_task_self(), &localsBuffer, localsBufferSize, VM_FLAGS_ANYWHERE) == 0 ) {
+            dyld_cache_local_symbols_info* infoHeader = (dyld_cache_local_symbols_info*)localsBuffer;
+            // 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 (uint32_t 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>*)(localsBuffer+nlistOffset);
+            ::memcpy(newLocalsSymbolTable, &unmappedLocalSymbols[0], nlistCount*sizeof(macho_nlist<P>));
+            // copy string pool
+            localSymbolsStringPool.copyPoolAndUpdateOffsets(((char*)infoHeader)+stringsOffset, newLocalsSymbolTable);
+            // update cache header
+            cacheHeader->header.localSymbolsSize    = localsBufferSize;
+            // return buffer of local symbols, caller to free() it
+            builder._localSymbolsRegion.buffer      = (uint8_t*)localsBuffer;
+            builder._localSymbolsRegion.bufferSize  = localsBufferSize;
+            builder._localSymbolsRegion.sizeInUse   = localsBufferSize;
+        }
+        else {
+            builder._diagnostics.warning("could not allocate local symbols");
         }
-        // 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
+    uint64_t linkeditsUnslidStartAddr = builder._readOnlyRegion.unslidLoadAddress + builder._nonLinkEditReadOnlySize;
+    uint32_t linkeditsCacheFileOffset = (uint32_t)(builder._readOnlyRegion.cacheFileOffset + builder._nonLinkEditReadOnlySize);
     for (LinkeditOptimizer<P>* op : optimizers) {
-        op->updateLoadCommands(linkeditStartOffset, linkeditStartAddr, newLinkeditEnd-linkeditStartOffset,
+        op->updateLoadCommands(linkeditsCacheFileOffset, linkeditsUnslidStartAddr, newLinkeditUnalignedSize,
                                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)
+void LinkeditOptimizer<P>::optimizeLinkedit(CacheBuilder& builder)
 {
+    DyldSharedCache* cache = (DyldSharedCache*)builder._readExecuteRegion.buffer;
     // 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));
+        optimizers.push_back(new LinkeditOptimizer<P>(cache, (macho_header<P>*)mh, builder._diagnostics));
     });
 #if 0
     // add optimizer for each branch pool
@@ -1158,22 +1163,20 @@ uint64_t optimizeLinkedit(DyldSharedCache* cache, bool dontMapLocalSymbols, bool
     }
 #endif
     // merge linkedit info
-    uint64_t newFileSize = mergeLinkedits(cache, dontMapLocalSymbols, addAcceleratorTables, optimizers, diag, localsInfo);
+    mergeLinkedits(builder, optimizers);
 
     // 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)
+void CacheBuilder::optimizeLinkedit(const std::vector<uint64_t>& branchPoolOffsets)
 {
-    if ( is64) {
-        return optimizeLinkedit<Pointer64<LittleEndian>>(cache, dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets, diag, localsInfo);
+    if ( _archLayout->is64 ) {
+        return LinkeditOptimizer<Pointer64<LittleEndian>>::optimizeLinkedit(*this);
     }
     else {
-        return optimizeLinkedit<Pointer32<LittleEndian>>(cache, dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets, diag, localsInfo);
+         return LinkeditOptimizer<Pointer32<LittleEndian>>::optimizeLinkedit(*this);
     }
 }
 
index 71fe9dc7ab59a02c5844836a998642b3b87bf81c..3ea4ad2f176481e4f590cbb72bd14401774067b7 100644 (file)
@@ -35,7 +35,7 @@
 #include "CacheBuilder.h"
 #include "FileAbstraction.hpp"
 #include "MachOFileAbstraction.hpp"
-
+#include "MachOLoaded.h"
 
 // Scan a C++ or Swift length-mangled field.
 static bool scanMangledField(const char *&string, const char *end, 
@@ -109,37 +109,57 @@ 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 };
-        });
+        _cacheStart         = (uint8_t*)cache;
+        _cacheUnslideAddr   = cache->unslidLoadAddress();
+        _slide              = (uint64_t)cache - _cacheUnslideAddr;
+#if SUPPORT_ARCH_arm64e
+        _chainedFixups      = (strcmp(cache->archName(), "arm64e") == 0);
+#else
+        _chainedFixups      = false;
+#endif
+     }
+
+    // Converts from an on disk vmAddr to the real vmAddr
+    // That is, for a chained fixup, decodes the chain, for a non-chained fixup, does nothing.
+    uint64_t vmAddrForOnDiskVMAddr(uint64_t vmaddr) {
+        if ( _chainedFixups ) {
+            dyld3::MachOLoaded::ChainedFixupPointerOnDisk ptr;
+            ptr.raw = vmaddr;
+            assert(ptr.authRebase.bind == 0);
+            if ( ptr.authRebase.auth ) {
+                vmaddr = _cacheUnslideAddr + ptr.authRebase.target;
+            }
+            else {
+                vmaddr = ptr.plainRebase.signExtendedTarget();
+            }
+        }
+        return vmaddr;
     }
 
     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;
+        vmaddr = vmAddrForOnDiskVMAddr(vmaddr);
+        if ( vmaddr != 0 ) {
+            uint64_t offset = vmaddr - _cacheUnslideAddr;
+            return _cacheStart + offset;
+        } else
+            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;
+        if ( content != nullptr )
+            return _cacheUnslideAddr + ((uint8_t*)content - _cacheStart);
+        else
+            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];
+    Diagnostics&                    _diagnostics;
+    uint64_t                        _slide;
+    uint64_t                        _cacheUnslideAddr;
+    uint8_t*                        _cacheStart;
+    bool                            _chainedFixups;
 };
 
 
@@ -167,6 +187,10 @@ public:
         return (pint_t)P::getP(_base[index]);
     }
 
+    pint_t getSectionVMAddress() const {
+        return (pint_t)_section->addr();
+    }
+
     T get(pint_t index) const {
         return (T)_cache->contentForVMAddr(getVMAddress(index));
     }
@@ -253,6 +277,7 @@ public:
     {
         _count++;
         const char *s = (const char *)_cache->contentForVMAddr(oldValue);
+        oldValue = (pint_t)_cache->vmAddrForOnDiskVMAddr(oldValue);
         objc_opt::string_map::iterator element = 
             _selectorStrings.insert(objc_opt::string_map::value_type(s, oldValue)).first;
         return (pint_t)element->second;
@@ -366,7 +391,7 @@ public:
     const char *writeProtocols(ContentAccessor* cache,
                                uint8_t *& rwdest, size_t& rwremaining,
                                uint8_t *& rodest, size_t& roremaining, 
-                               std::vector<void*>& pointersInData, 
+                               CacheBuilder::ASLR_Tracker& aslrTracker,
                                pint_t protocolClassVMAddr)
     {
         if (_protocolCount == 0) return NULL;
@@ -430,7 +455,7 @@ public:
             iter->second = cache->vmAddrForContent(proto);
 
             // Add new rebase entries.
-            proto->addPointers(pointersInData);
+            proto->addPointers(cache, aslrTracker);
         }
         
         return NULL;
@@ -464,7 +489,9 @@ static int percent(size_t num, size_t denom) {
 
 
 template <typename P>
-void optimizeObjC(DyldSharedCache* cache, bool forProduction, std::vector<void*>& pointersForASLR, Diagnostics& diag)
+void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::ASLR_Tracker& aslrTracker,
+                    CacheBuilder::LOH_Tracker& lohTracker,
+                    const std::map<void*, std::string>& missingWeakImports, Diagnostics& diag)
 {
     typedef typename P::E           E;
     typedef typename P::uint_t      pint_t;
@@ -563,7 +590,7 @@ void optimizeObjC(DyldSharedCache* cache, bool forProduction, std::vector<void*>
     }
     else {
         for (const macho_header<P>* mh : addressSortedDylibs) {
-            hinfoROOptimizer.update(&cacheAccessor, mh, pointersForASLR);
+            hinfoROOptimizer.update(&cacheAccessor, mh, aslrTracker);
         }
     }
 
@@ -578,7 +605,7 @@ void optimizeObjC(DyldSharedCache* cache, bool forProduction, std::vector<void*>
     }
     else {
         for (const macho_header<P>* mh : addressSortedDylibs) {
-            hinfoRWOptimizer.update(&cacheAccessor, mh, pointersForASLR);
+            hinfoRWOptimizer.update(&cacheAccessor, mh, aslrTracker);
         }
     }
 
@@ -640,7 +667,7 @@ void optimizeObjC(DyldSharedCache* cache, bool forProduction, std::vector<void*>
     if (forProduction) {
         WeakClassDetector<P> weakopt;
         noMissingWeakSuperclasses = 
-            weakopt.noMissingWeakSuperclasses(&cacheAccessor, sizeSortedDylibs);
+            weakopt.noMissingWeakSuperclasses(&cacheAccessor, missingWeakImports, sizeSortedDylibs);
 
         // Shared cache does not currently support unbound weak references. 
         // Here we assert that there are none. If support is added later then 
@@ -717,7 +744,7 @@ void optimizeObjC(DyldSharedCache* cache, bool forProduction, std::vector<void*>
     err = protocolOptimizer.writeProtocols(&cacheAccessor,
                                            optRWData, optRWRemaining,
                                            optROData, optRORemaining,
-                                           pointersForASLR, protocolClassVMAddr);
+                                           aslrTracker, protocolClassVMAddr);
     if (err) {
         diag.warning("%s", err);
         return;
@@ -804,17 +831,120 @@ void optimizeObjC(DyldSharedCache* cache, bool forProduction, std::vector<void*>
     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);
+
+    // Now that objc has uniqued the selector references, we can apply the LOHs so that ADRP/LDR -> ADRP/ADD
+    if (forProduction) {
+        uint64_t lohADRPCount = 0;
+        uint64_t lohLDRCount = 0;
+
+        for (auto& targetAndInstructions : lohTracker) {
+            uint64_t targetVMAddr = targetAndInstructions.first;
+            if (!selOptimizer.isSelectorRefAddress((pint_t)targetVMAddr))
+                continue;
+
+            std::set<void*>& instructions = targetAndInstructions.second;
+            // We do 2 passes over the instructions.  The first to validate them and the second
+            // to actually update them.
+            for (unsigned pass = 0; pass != 2; ++pass) {
+                uint32_t adrpCount = 0;
+                uint32_t ldrCount = 0;
+                for (void* instructionAddress : instructions) {
+                    uint32_t& instruction = *(uint32_t*)instructionAddress;
+                    uint64_t instructionVMAddr = cacheAccessor.vmAddrForContent(&instruction);
+                    uint64_t selRefContent = *(uint64_t*)cacheAccessor.contentForVMAddr(targetVMAddr);
+                    const char* selectorString = (const char*)cacheAccessor.contentForVMAddr(selRefContent);
+                    uint64_t selectorStringVMAddr = cacheAccessor.vmAddrForContent(selectorString);
+
+                    if ( (instruction & 0x9F000000) == 0x90000000 ) {
+                        // ADRP
+                        int64_t pageDistance = ((selectorStringVMAddr & ~0xFFF) - (instructionVMAddr & ~0xFFF));
+                        int64_t newPage21 = pageDistance >> 12;
+
+                        if (pass == 0) {
+                            if ( (newPage21 > 2097151) || (newPage21 < -2097151) ) {
+                                diag.verbose("Out of bounds ADRP selector reference target\n");
+                                instructions.clear();
+                                break;
+                            }
+                            ++adrpCount;
+                        }
+
+                        if (pass == 1) {
+                            instruction = (instruction & 0x9F00001F) | ((newPage21 << 29) & 0x60000000) | ((newPage21 << 3) & 0x00FFFFE0);
+                            ++lohADRPCount;
+                        }
+                        continue;
+                    }
+
+                    if ( (instruction & 0x3B000000) == 0x39000000 ) {
+                        // LDR/STR.  STR shouldn't be possible as this is a selref!
+                        if (pass == 0) {
+                            if ( (instruction & 0xC0C00000) != 0xC0400000 ) {
+                                // Not a load, or dest reg isn't xN, or uses sign extension
+                                diag.verbose("Bad LDR for selector reference optimisation\n");
+                                instructions.clear();
+                                break;
+                            }
+                            if ( (instruction & 0x04000000) != 0 ) {
+                                // Loading a float
+                                diag.verbose("Bad LDR for selector reference optimisation\n");
+                                instructions.clear();
+                                break;
+                            }
+                            ++ldrCount;
+                        }
+
+                        if (pass == 1) {
+                            uint32_t ldrDestReg = (instruction & 0x1F);
+                            uint32_t ldrBaseReg = ((instruction >> 5) & 0x1F);
+
+                            // Convert the LDR to an ADD
+                            instruction = 0x91000000;
+                            instruction |= ldrDestReg;
+                            instruction |= ldrBaseReg << 5;
+                            instruction |= (selectorStringVMAddr & 0xFFF) << 10;
+
+                            ++lohLDRCount;
+                        }
+                        continue;
+                    }
+
+                    if ( (instruction & 0xFFC00000) == 0x91000000 ) {
+                        // ADD imm12
+                        // We don't support ADDs.
+                        diag.verbose("Bad ADD for selector reference optimisation\n");
+                        instructions.clear();
+                        break;
+                    }
+
+                    diag.verbose("Unknown instruction for selref optimisation\n");
+                    instructions.clear();
+                    break;
+                }
+                if (pass == 0) {
+                    // If we didn't see at least one ADRP/LDR in pass one then don't optimize this location
+                    if ((adrpCount == 0) || (ldrCount == 0)) {
+                        instructions.clear();
+                        break;
+                    }
+                }
+            }
+        }
+
+        diag.verbose("  Optimized %lld ADRP LOHs\n", lohADRPCount);
+        diag.verbose("  Optimized %lld LDR LOHs\n", lohLDRCount);
+    }
 }
 
 
 } // anon namespace
 
-void optimizeObjC(DyldSharedCache* cache, bool is64, bool customerCache, std::vector<void*>& pointersForASLR, Diagnostics& diag)
+void CacheBuilder::optimizeObjC()
 {
-    if ( is64 )
-        optimizeObjC<Pointer64<LittleEndian>>(cache, customerCache, pointersForASLR, diag);
+    if ( _archLayout->is64 )
+        doOptimizeObjC<Pointer64<LittleEndian>>((DyldSharedCache*)_readExecuteRegion.buffer, _options.optimizeStubs, _aslrTracker, _lohTracker, _missingWeakImports, _diagnostics);
     else
-        optimizeObjC<Pointer32<LittleEndian>>(cache, customerCache, pointersForASLR, diag);
+        doOptimizeObjC<Pointer32<LittleEndian>>((DyldSharedCache*)_readExecuteRegion.buffer, _options.optimizeStubs, _aslrTracker, _lohTracker, _missingWeakImports, _diagnostics);
 }
 
 
index 701e5fd8d0d32359203d3b3bf29a81c2d0cd98e3..d8541a1b1fb4331d6d1f4b03cd29aabeb28b5e66 100644 (file)
@@ -32,6 +32,11 @@ 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 startsWith(const std::string_view& str, const std::string_view& 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());
@@ -55,7 +60,7 @@ inline char hexDigit(uint8_t value)
 inline void bytesToHex(const uint8_t* bytes, size_t byteCount, char buffer[])
 {
     char* p = buffer;
-    for (int i=0; i < byteCount; ++i) {
+    for (size_t i=0; i < byteCount; ++i) {
         *p++ = hexDigit(bytes[i] >> 4);
         *p++ = hexDigit(bytes[i] & 0x0F);
     }
index de63bf9e0086171a9cf3f6bb9ed15a7a54b75fc4..da9861b175f7f6beafb9d453663230bce3d90f02 100644 (file)
@@ -59,12 +59,22 @@ struct dyld_cache_header
     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
+    uint32_t    formatVersion        : 8,  // dyld3::closure::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
+                locallyBuiltCache    : 1,  // 0 for B&I built cache, 1 for locally built cache
+                padding              : 21; // TBD
     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
+    uint64_t    dylibsImageArrayAddr;   // (unslid) address of ImageArray for dylibs in this cache
+    uint64_t    dylibsImageArraySize;   // size of ImageArray for dylibs in this cache
+    uint64_t    dylibsTrieAddr;         // (unslid) address of trie of indexes of all cached dylibs
+    uint64_t    dylibsTrieSize;         // size of trie of cached dylib paths
+    uint64_t    otherImageArrayAddr;    // (unslid) address of ImageArray for dylibs and bundles with dlopen closures
+    uint64_t    otherImageArraySize;    // size of ImageArray for dylibs and bundles with dlopen closures
+    uint64_t    otherTrieAddr;          // (unslid) address of trie of indexes of all dylibs and bundles with dlopen closures
+    uint64_t    otherTrieSize;          // size of trie of dylibs and bundles with dlopen closures
 };
 
 
@@ -238,10 +248,176 @@ struct dyld_cache_slide_info2
     //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
+#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
+
+
+
+// The version 3 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
+//
+// There are two cases:
+//
+// 1) pageStarts[pageIndex] == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE
+//    The page contains no values that need rebasing.
+//
+// 2) otherwise...
+//    All rebase locations are in one linked list. The offset of the first
+//    rebase location in the page is pageStarts[pageIndex].
+//
+// A pointer is one of of the variants in dyld_cache_slide_pointer3
+//
+// The code for processing a linked list (chain) is:
+//
+//    uint32_t delta = pageStarts[pageIndex];
+//    dyld_cache_slide_pointer3* loc = pageStart;
+//    do {
+//        loc += delta;
+//        delta = loc->offsetToNextPointer;
+//        if ( loc->auth.authenticated ) {
+//            newValue = loc->offsetFromSharedCacheBase  + results->slide + auth_value_add;
+//            newValue = sign_using_the_various_bits(newValue);
+//        }
+//        else {
+//            uint64_t value51      = loc->pointerValue;
+//            uint64_t top8Bits     = value51 & 0x0007F80000000000ULL;
+//            uint64_t bottom43Bits = value51 & 0x000007FFFFFFFFFFULL;
+//            uint64_t targetValue  = ( top8Bits << 13 ) | bottom43Bits;
+//            newValue = targetValue + results->slide;
+//        }
+//        loc->raw = newValue;
+//    } while (delta != 0);
+//
+//
+struct dyld_cache_slide_info3
+{
+    uint32_t    version;            // currently 3
+    uint32_t    page_size;          // currently 4096 (may also be 16384)
+    uint32_t    page_starts_count;
+    uint64_t    auth_value_add;
+    uint16_t    page_starts[/* page_starts_count */];
+};
+
+#define DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE    0xFFFF    // page has no rebasing
+
+union dyld_cache_slide_pointer3
+{
+    uint64_t  raw;
+    struct {
+        uint64_t    pointerValue        : 51,
+                    offsetToNextPointer : 11,
+                    unused              :  2;
+    }         plain;
+
+    struct {
+        uint64_t    offsetFromSharedCacheBase : 32,
+                    diversityData             : 16,
+                    hasAddressDiversity       :  1,
+                    key                       :  2,
+                    offsetToNextPointer       : 11,
+                    unused                    :  1,
+                    authenticated             :  1; // = 1;
+    }         auth;
+};
+
+
+
+// The version 4 of the slide info is optimized for 32-bit caches up to 1GB.
+// Since only interior pointers (pointers that point within the cache) are rebased
+// (slid), we know the possible range of the pointers takes 30 bits.  That
+// gives us two bits to use to chain to the next rebase.
+//
+// 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_SLIDE4_PAGE_NO_REBASE
+//    The page contains no values that need rebasing.
+//
+// 2) (pageStarts[pageIndex] & DYLD_CACHE_SLIDE4_PAGE_USE_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_SLIDE4_PAGE_USE_EXTRA
+//    Multiple chains 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 chain in the page. The first is at:
+//       extrasStartIndex = (pageStarts[pageIndex] & DYLD_CACHE_SLIDE4_PAGE_INDEX)
+//    The next is at extrasStartIndex+1.  The last is denoted by
+//    having the high bit (DYLD_CACHE_SLIDE4_PAGE_EXTRA_END) of the pageExtras[].
+//
+// For 32-bit architectures, there are only two bits free (the two 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 12 bytes.
+// To reduce the number or chains needed, an optimization was added.  Turns
+// most of the non-rebased data are small values and can be co-opt'ed into
+// being used in the chain. The can be done because nothing
+// in the shared cache should point to the first 64KB which are in the shared
+// cache header information. So if the resulting pointer points to the
+// start of the cache +/-32KB, then it is actually a small number that should
+// not be rebased, but just reconstituted.
+//
+// The code for processing a linked list (chain) is:
+//
+//    uint32_t delta = 1;
+//    while ( delta != 0 ) {
+//        uint8_t* loc = pageStart + pageOffset;
+//        uint32_t rawValue = *((uint32_t*)loc);
+//        delta = ((rawValue & deltaMask) >> deltaShift);
+//        uintptr_t newValue = (rawValue & valueMask);
+//        if ( (newValue & 0xFFFF8000) == 0 ) {
+//           // small positive non-pointer, use as-is
+//        }
+//        else if ( (newValue & 0x3FFF8000) == 0x3FFF8000 ) {
+//           // small negative non-pointer
+//           newValue |= 0xC0000000;
+//        }
+//        else  {
+//            // pointer that needs rebasing
+//            newValue += valueAdd;
+//            newValue += slideAmount;
+//        }
+//        *((uint32_t*)loc) = newValue;
+//        pageOffset += delta;
+//    }
+//
+//
+struct dyld_cache_slide_info4
+{
+    uint32_t    version;            // currently 4
+    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 (0xC0000000)
+    uint64_t    value_add;          // base address of cache
+    //uint16_t    page_starts[page_starts_count];
+    //uint16_t    page_extras[page_extras_count];
+};
+#define DYLD_CACHE_SLIDE4_PAGE_NO_REBASE           0xFFFF  // page has no rebasing
+#define DYLD_CACHE_SLIDE4_PAGE_INDEX               0x7FFF  // mask of page_starts[] values
+#define DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA           0x8000  // index is into extras array (not a chain start offset)
+#define DYLD_CACHE_SLIDE4_PAGE_EXTRA_END           0x8000  // last chain entry for page
+
+
 
 
 struct dyld_cache_local_symbols_info
index db01f5fbdd01a9e66b28989e0958ddb77086b5cb..a5f2973507454ac08f7d734eae404582cc639303 100644 (file)
 #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"
+#include "ClosureBuilder.h"
+#include "ClosurePrinter.h"
+#include "ClosureFileSystemPhysical.h"
+
+using dyld3::closure::ImageArray;
+using dyld3::closure::Image;
+using dyld3::closure::ImageNum;
+using dyld3::closure::ClosureBuilder;
+using dyld3::closure::LaunchClosure;
+using dyld3::closure::DlopenClosure;
+using dyld3::closure::PathOverrides;
+using dyld3::Array;
 
-extern "C" {
-    #include "closuredProtocol.h"
-}
 
+// mmap() an shared cache file read/only but laid out like it would be at runtime
 static const DyldSharedCache* mapCacheFile(const char* path)
 {
     struct stat statbuf;
-    if (stat(path, &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);
+    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;
+    uint8_t  firstPage[4096];
+    if ( ::pread(cache_fd, firstPage, 4096, 0) != 4096 ) {
+        fprintf(stderr, "Error: failed to read shared cache file at %s\n", path);
+        return nullptr;
     }
-    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);
+    const dyld_cache_header*       header   = (dyld_cache_header*)firstPage;
+       const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(firstPage + header->mappingOffset);
+
+    size_t vmSize = (size_t)(mappings[2].address + mappings[2].size - mappings[0].address);
+    vm_address_t result;
+    kern_return_t r = ::vm_allocate(mach_task_self(), &result, vmSize, VM_FLAGS_ANYWHERE);
+    if ( r != KERN_SUCCESS ) {
+        fprintf(stderr, "Error: failed to allocate space to load shared cache file at %s\n", path);
+        return nullptr;
+       }
+    for (int i=0; i < 3; ++i) {
+        void* mapped_cache = ::mmap((void*)(result + mappings[i].address - mappings[0].address), (size_t)mappings[i].size,
+                                    PROT_READ, MAP_FIXED | MAP_PRIVATE, cache_fd, mappings[i].fileOffset);
+        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);
 
-    //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;
+    return (DyldSharedCache*)result;
 }
-*/
 
 static void usage()
 {
-    printf("dyld_closure_util program to create of view dyld3 closures\n");
+    printf("dyld_closure_util program to create or 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("    -list_dyld_cache_closures              # list all launch closures in the dyld shared cache with size\n");
+    printf("    -list_dyld_cache_dlopen_closures       # list all dlopen closures in the dyld shared cache with size\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("    -print_dyld_cache_dylib <dylib-path>   # print specified cached dylib as JSON\n");
+    printf("    -print_dyld_cache_dylibs               # print all cached dylibs as JSON\n");
+    printf("    -print_dyld_cache_dlopen <path>        # print specified dlopen closure as JSON\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("    -dlopen <path>                         # for use with -create_closure to simulate that program calling dlopen\n");
     printf("    -verbose_fixups                        # for use with -print* options to force printing fixups\n");
+    printf("    -no_at_paths                           # when building a closure, simulate security not allowing @path expansion\n");
+    printf("    -no_fallback_paths                     # when building a closure, simulate security not allowing default fallback paths\n");
+    printf("    -allow_insertion_failures              # when building a closure, simulate security allowing unloadable DYLD_INSERT_LIBRARIES to be ignored\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                      listCacheDlopenClosures = false;
     bool                      printCachedDylibs = false;
-    bool                      printOtherDylibs = false;
-    bool                      printPatchTable = false;
-    bool                      useClosured = false;
     bool                      verboseFixups = false;
+    bool                      allowAtPaths = true;
+    bool                      allowFallbackPaths = true;
+    bool                      allowInsertionFailures = false;
     std::vector<std::string>  buildtimePrefixes;
-    std::vector<std::string>  envArgs;
+    std::vector<const char*>  envArgs;
     std::vector<const char*>  dlopens;
 
     if ( argc == 1 ) {
@@ -212,14 +163,7 @@ int main(int argc, const char* argv[])
                 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 ) {
+        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");
@@ -227,9 +171,18 @@ int main(int argc, const char* argv[])
             }
             dlopens.push_back(path);
         }
-       else if ( strcmp(arg, "-verbose_fixups") == 0 ) {
+        else if ( strcmp(arg, "-verbose_fixups") == 0 ) {
            verboseFixups = true;
         }
+        else if ( strcmp(arg, "-no_at_paths") == 0 ) {
+            allowAtPaths = false;
+        }
+        else if ( strcmp(arg, "-no_fallback_paths") == 0 ) {
+            allowFallbackPaths = false;
+        }
+        else if ( strcmp(arg, "-allow_insertion_failures") == 0 ) {
+            allowInsertionFailures = true;
+        }
         else if ( strcmp(arg, "-build_root") == 0 ) {
             const char* buildRootPath = argv[++i];
             if ( buildRootPath == nullptr ) {
@@ -238,32 +191,11 @@ int main(int argc, const char* argv[])
             }
             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, "-list_dyld_cache_dlopen_closures") == 0 ) {
+            listCacheDlopenClosures = true;
         }
         else if ( strcmp(arg, "-print_dyld_cache_closure") == 0 ) {
             printCacheClosure = argv[++i];
@@ -272,15 +204,9 @@ int main(int argc, const char* argv[])
                 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 ) {
@@ -288,19 +214,13 @@ int main(int argc, const char* argv[])
                 return 1;
             }
         }
-        else if ( strcmp(arg, "-print_dyld_cache_other") == 0 ) {
+        else if ( strcmp(arg, "-print_dyld_cache_dlopen") == 0 ) {
             printOtherDylib = argv[++i];
             if ( printOtherDylib == nullptr ) {
-                fprintf(stderr, "-print_dyld_cache_other option requires a path \n");
+                fprintf(stderr, "-print_dyld_cache_dlopen 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) ) {
@@ -309,306 +229,151 @@ int main(int argc, const char* argv[])
             }
             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;
-    }
+    envArgs.push_back(nullptr);
+
 
     const DyldSharedCache* dyldCache = nullptr;
-    bool dyldCacheIsRaw = false;
+    bool dyldCacheIsLive = true;
     if ( cacheFilePath != nullptr ) {
         dyldCache = mapCacheFile(cacheFilePath);
-        dyldCacheIsRaw = true;
+        dyldCacheIsLive = false;
     }
     else {
-#if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101300)
+#if __MAC_OS_X_VERSION_MIN_REQUIRED && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101300)
+        fprintf(stderr, "this tool needs to run on macOS 10.13 or later\n");
+        return 1;
+#else
         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("");
+    dyld3::Platform platform = dyldCache->platform();
+    const char*     archName = dyldCache->archName();
 
-    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());
+        PathOverrides pathOverrides;
+        pathOverrides.setFallbackPathHandling(allowFallbackPaths ? dyld3::closure::PathOverrides::FallbackPathMode::classic : dyld3::closure::PathOverrides::FallbackPathMode::none);
+        pathOverrides.setEnvVars(&envArgs[0], nullptr, nullptr);
+        const char* prefix = ( buildtimePrefixes.empty() ? nullptr : buildtimePrefixes.front().c_str());
+        //dyld3::PathOverrides pathStuff(envArgs);
+        STACK_ALLOC_ARRAY(const ImageArray*,    imagesArrays, 3+dlopens.size());
+        STACK_ALLOC_ARRAY(dyld3::LoadedImage,   loadedArray,  1024);
+        imagesArrays.push_back(dyldCache->cachedDylibsImageArray());
+        imagesArrays.push_back(dyldCache->otherOSImageArray());
+
+        dyld3::closure::FileSystemPhysical fileSystem(prefix);
+        ClosureBuilder::AtPath atPathHanding = allowAtPaths ? ClosureBuilder::AtPath::all : ClosureBuilder::AtPath::none;
+        ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, fileSystem, dyldCache, dyldCacheIsLive, pathOverrides, atPathHanding, nullptr, archName, platform, nullptr);
+        const LaunchClosure* mainClosure = builder.makeLaunchClosure(inputMainExecutablePath, allowInsertionFailures);
+        if ( builder.diagnostics().hasError() ) {
+            fprintf(stderr, "dyld_closure_util: %s\n", builder.diagnostics().errorMessage());
             return 1;
         }
-        for (const std::string& warn : closureDiag.warnings() )
-            fprintf(stderr, "dyld_closure_util: warning: %s\n", warn.c_str());
+        ImageNum nextNum = builder.nextFreeImageNum();
 
-        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);
+        if ( !dlopens.empty() )
+            printf("[\n");
+        imagesArrays.push_back(mainClosure->images());
+        dyld3::closure::printClosureAsJSON(mainClosure, imagesArrays, verboseFixups);
+        ClosureBuilder::buildLoadOrder(loadedArray, imagesArrays, mainClosure);
 
-            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");
-        }
+        for (const char* path : dlopens) {
+            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;
+            // map unloaded mach-o files for use during closure building
+            for (dyld3::LoadedImage& li : loadedArray) {
+                if ( li.loadedAddress() != nullptr )
+                    continue;
+                if ( li.image()->inDyldCache() ) {
+                    li.setLoadedAddress((dyld3::MachOLoaded*)((uint8_t*)dyldCache + li.image()->cacheOffset()));
+                }
+                else {
+                    Diagnostics diag;
+                    dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, li.image()->path(), archName, platform);
+                    li.setLoadedAddress((const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent);
+                }
             }
-        }
 
-        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);
+            ClosureBuilder::AtPath atPathHandingDlopen = allowAtPaths ? ClosureBuilder::AtPath::all : ClosureBuilder::AtPath::onlyInRPaths;
+            ClosureBuilder dlopenBuilder(nextNum, fileSystem, dyldCache, dyldCacheIsLive, pathOverrides, atPathHandingDlopen, nullptr, archName, platform, nullptr);
+            ImageNum topImageNum;
+            const DlopenClosure* dlopenClosure = dlopenBuilder.makeDlopenClosure(path, mainClosure, loadedArray, 0, false, false, &topImageNum);
+            if ( dlopenBuilder.diagnostics().hasError() ) {
+                fprintf(stderr, "dyld_closure_util: %s\n", dlopenBuilder.diagnostics().errorMessage());
                 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();
+            if ( dlopenClosure == nullptr ) {
+                if ( topImageNum == 0 ) {
+                    fprintf(stderr, "dyld_closure_util: failed to dlopen %s\n", path);
+                    return 1;
+                }
+                printf("{\n   \"dyld-cache-image-num\":  \"0x%04X\"\n}\n", topImageNum);
+            }
+            else {
+                nextNum += dlopenClosure->images()->imageCount();
+                imagesArrays.push_back(dlopenClosure->images());
+                dyld3::closure::printClosureAsJSON(dlopenClosure, imagesArrays, verboseFixups);
+                ClosureBuilder::buildLoadOrder(loadedArray, imagesArrays, dlopenClosure);
             }
         }
-        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);
+        if ( !dlopens.empty() )
+            printf("]\n");
     }
     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);
+        dyldCache->forEachLaunchClosure(^(const char* runtimePath, const dyld3::closure::LaunchClosure* closure) {
+            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 ( listCacheDlopenClosures ) {
+        dyldCache->forEachDlopenImage(^(const char* runtimePath, const dyld3::closure::Image* image) {
+            printf("%6lu  %s\n", image->size(), runtimePath);
+        });
     }
     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);
+        const dyld3::closure::LaunchClosure* closure = dyldCache->findClosure(printCacheClosure);
+        if ( closure != nullptr ) {
+            STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 3);
+            imagesArrays.push_back(dyldCache->cachedDylibsImageArray());
+            imagesArrays.push_back(dyldCache->otherOSImageArray());
+            imagesArrays.push_back(closure->images());
+            dyld3::closure::printClosureAsJSON(closure, imagesArrays, 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);
+        dyld3::closure::printDyldCacheImagesAsJSON(dyldCache, 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);
+        const dyld3::closure::ImageArray* dylibs = dyldCache->cachedDylibsImageArray();
+        STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 2);
+        imagesArrays.push_back(dylibs);
+        ImageNum num;
+        if ( dylibs->hasPath(printCachedDylib, num) ) {
+            dyld3::closure::printImageAsJSON(dylibs->imageForNum(num), imagesArrays, verboseFixups);
         }
         else {
-            fprintf(stderr, "no such other image found\n");
+            fprintf(stderr, "no such 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);
+        if ( const dyld3::closure::Image* image = dyldCache->findDlopenOtherImage(printOtherDylib) ) {
+            STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 2);
+            imagesArrays.push_back(dyldCache->cachedDylibsImageArray());
+            imagesArrays.push_back(dyldCache->otherOSImageArray());
+            dyld3::closure::printImageAsJSON(image, imagesArrays, verboseFixups);
         }
         else {
-            fprintf(stderr, "no such other image found\n");
+            fprintf(stderr, "no such 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;
 }
index cbedce283849380b2339a29f6403dc787bbc700e..b3a12628b356344659bc2ce7c0a304380aa739c5 100644 (file)
@@ -63,8 +63,9 @@
 #include "DyldSharedCache.h"
 #include "BuilderUtils.h"
 #include "FileUtils.h"
+#include "JSONWriter.h"
 #include "StringUtils.h"
-#include "MachOParser.h"
+#include "mrm_shared_cache_builder.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
@@ -74,9 +75,6 @@ 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;
@@ -99,7 +97,7 @@ int runCommandAndWait(Diagnostics& diags, const char* args[])
     return res;
 }
 
-void processRoots(Diagnostics& diags, std::set<std::string>& roots)
+void processRoots(Diagnostics& diags, std::set<std::string>& roots, const char *tempRootsDir)
 {
     std::set<std::string> processedRoots;
     struct stat           sb;
@@ -110,9 +108,16 @@ void processRoots(Diagnostics& diags, std::set<std::string>& 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")) {
+            processedRoots.insert(root);
+            continue;
+        }
+
+        char tempRootDir[MAXPATHLEN];
+        strlcpy(tempRootDir, tempRootsDir, MAXPATHLEN);
+        strlcat(tempRootDir, "/XXXXXXXX", MAXPATHLEN);
+        mkdtemp(tempRootDir);
+
+        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();
@@ -176,6 +181,7 @@ void processRoots(Diagnostics& diags, std::set<std::string>& roots)
 
 bool writeRootList(const std::string& dstRoot, const std::set<std::string>& roots)
 {
+    mkpath_np(dstRoot.c_str(), 0755);
     if (roots.size() == 0)
         return false;
 
@@ -192,35 +198,109 @@ bool writeRootList(const std::string& dstRoot, const std::set<std::string>& root
     return true;
 }
 
-std::set<std::string> cachePaths;
-
-BOMCopierCopyOperation filteredCopy(BOMCopier copier, const char* path, BOMFSObjType type, off_t size)
+BOMCopierCopyOperation filteredCopyExcludingPaths(BOMCopier copier, const char* path, BOMFSObjType type, off_t size)
 {
     std::string absolutePath = &path[1];
-    if (cachePaths.count(absolutePath)) {
+    void *userData = BOMCopierUserData(copier);
+    std::set<std::string> *cachePaths = (std::set<std::string>*)userData;
+    if (cachePaths->count(absolutePath)) {
         return BOMCopierSkipFile;
     }
     return BOMCopierContinue;
 }
 
+BOMCopierCopyOperation filteredCopyIncludingPaths(BOMCopier copier, const char* path, BOMFSObjType type, off_t size)
+{
+    std::string absolutePath = &path[1];
+    void *userData = BOMCopierUserData(copier);
+    std::set<std::string> *cachePaths = (std::set<std::string>*)userData;
+    for (const std::string& cachePath : *cachePaths) {
+        if (startsWith(cachePath, absolutePath))
+            return BOMCopierContinue;
+    }
+    if (cachePaths->count(absolutePath)) {
+        return BOMCopierContinue;
+    }
+    return BOMCopierSkipFile;
+}
+
+static std::string dispositionToString(Disposition disposition) {
+    switch (disposition) {
+        case Unknown:
+            return "Unknown";
+        case InternalDevelopment:
+            return "InternalDevelopment";
+        case Customer:
+            return "Customer";
+        case InternalMinDevelopment:
+            return "InternalMinDevelopment";
+    }
+}
+
+static std::string platformToString(Platform platform) {
+    switch (platform) {
+        case unknown:
+            return "unknown";
+        case macOS:
+            return "macOS";
+        case iOS:
+            return "iOS";
+        case tvOS:
+            return "tvOS";
+        case watchOS:
+            return "watchOS";
+        case bridgeOS:
+            return "bridgeOS";
+        case iOSMac:
+            return "iOSMac";
+        case iOS_simulator:
+            return "iOS_simulator";
+        case tvOS_simulator:
+            return "tvOS_simulator";
+        case watchOS_simulator:
+            return "watchOS_simulator";
+    }
+}
+
+static dyld3::json::Node getBuildOptionsNode(BuildOptions_v1 buildOptions) {
+    dyld3::json::Node buildOptionsNode;
+    buildOptionsNode.map["version"].value             = dyld3::json::decimal(buildOptions.version);
+    buildOptionsNode.map["updateName"].value          = buildOptions.updateName;
+    buildOptionsNode.map["deviceName"].value          = buildOptions.deviceName;
+    buildOptionsNode.map["disposition"].value         = dispositionToString(buildOptions.disposition);
+    buildOptionsNode.map["platform"].value            = platformToString(buildOptions.platform);
+    for (unsigned i = 0; i != buildOptions.numArchs; ++i) {
+        dyld3::json::Node archNode;
+        archNode.value = buildOptions.archs[i];
+        buildOptionsNode.map["archs"].array.push_back(archNode);
+    }
+    buildOptionsNode.map["verboseDiagnostics"].value  = buildOptions.verboseDiagnostics ? "true" : "false";
+    return buildOptionsNode;
+}
+
 int main(int argc, const char* argv[])
 {
     @autoreleasepool {
         __block Diagnostics   diags;
         std::set<std::string> roots;
         std::string           dylibCacheDir;
+        std::string           artifactDir;
         std::string           release;
         bool                  emitDevCaches = true;
         bool                  emitElidedDylibs = true;
         bool                  listConfigs = false;
         bool                  copyRoots = false;
         bool                  debug = false;
+        bool                  useMRM = false;
         std::string           dstRoot;
+        std::string           emitJSONPath;
         std::string           configuration;
         std::string           resultPath;
+        std::string           baselineDifferenceResultPath;
+        bool                  baselineCopyRoots = false;
+        char* tempRootsDir = strdup("/tmp/dyld_shared_cache_builder.XXXXXX");
 
-        tempRootDir = strdup(tempRootDirTemplate);
-        mkdtemp(tempRootDir);
+        mkdtemp(tempRootsDir);
 
         for (int i = 1; i < argc; ++i) {
             const char* arg = argv[i];
@@ -236,6 +316,8 @@ int main(int argc, const char* argv[])
                     copyRoots = true;
                 } else if (strcmp(arg, "-dylib_cache") == 0) {
                     dylibCacheDir = realPath(argv[++i]);
+                } else if (strcmp(arg, "-artifact") == 0) {
+                    artifactDir = realPath(argv[++i]);
                 } else if (strcmp(arg, "-no_development_cache") == 0) {
                     emitDevCaches = false;
                 } else if (strcmp(arg, "-no_overflow_dylibs") == 0) {
@@ -244,19 +326,29 @@ int main(int argc, const char* argv[])
                     emitDevCaches = true;
                 } else if (strcmp(arg, "-overflow_dylibs") == 0) {
                     emitElidedDylibs = true;
+                } else if (strcmp(arg, "-mrm") == 0) {
+                    useMRM = true;
+                } else if (strcmp(arg, "-emit_json") == 0) {
+                    emitJSONPath = realPath(argv[++i]);
                 } 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 if (strcmp(arg, "-baseline_diff_results") == 0) {
+                    baselineDifferenceResultPath = realPath(argv[++i]);
+                } else if (strcmp(arg, "-baseline_copy_roots") == 0) {
+                    baselineCopyRoots = true;
                 } else {
                     //usage();
-                    diags.error("unknown option: %s\n", arg);
+                    fprintf(stderr, "unknown option: %s\n", arg);
+                    exit(-1);
                 }
             } else {
                 if (!configuration.empty()) {
-                    diags.error("You may only specify one configuration");
+                    fprintf(stderr, "You may only specify one configuration\n");
+                    exit(-1);
                 }
                 configuration = argv[i];
             }
@@ -264,16 +356,20 @@ int main(int argc, const char* argv[])
 
         time_t mytime = time(0);
         fprintf(stderr, "Started: %s", asctime(localtime(&mytime)));
-        processRoots(diags, roots);
+        writeRootList(dstRoot, roots);
+        processRoots(diags, roots, tempRootsDir);
 
         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");
+        if (dylibCacheDir.empty() && artifactDir.empty() && release.empty()) {
+            fprintf(stderr, "you must specify either -dylib_cache, -artifact or -release\n");
             exit(-1);
         } else if (!dylibCacheDir.empty() && !release.empty()) {
-            fprintf(stderr, "you may not use -dylib_cache and -release at the same time");
+            fprintf(stderr, "you may not use -dylib_cache and -release at the same time\n");
+            exit(-1);
+        } else if (!dylibCacheDir.empty() && !artifactDir.empty()) {
+            fprintf(stderr, "you may not use -dylib_cache and -artifact at the same time\n");
             exit(-1);
         }
 
@@ -282,6 +378,49 @@ int main(int argc, const char* argv[])
             exit(-1);
         }
 
+        if (!baselineDifferenceResultPath.empty() && (roots.size() > 1)) {
+            fprintf(stderr, "Cannot use -baseline_diff_results with more that one -root\n");
+            exit(-1);
+        }
+
+        if (!artifactDir.empty()) {
+            // Find the dylib cache dir from inside the artifact dir
+            struct stat stat_buf;
+            if (stat(artifactDir.c_str(), &stat_buf) != 0) {
+                fprintf(stderr, "Could not find artifact path '%s'\n", artifactDir.c_str());
+                exit(-1);
+            }
+            std::string dir = artifactDir + "/AppleInternal/Developer/DylibCaches";
+            if (stat(dir.c_str(), &stat_buf) != 0) {
+                fprintf(stderr, "Could not find artifact path '%s'\n", dir.c_str());
+                exit(-1);
+            }
+
+            if (!release.empty()) {
+                // Use the given release
+                dylibCacheDir = dir + "/" + release + ".dlc";
+            } else {
+                // Find a release directory
+                __block std::vector<std::string> subDirectories;
+                iterateDirectoryTree("", dir, ^(const std::string& dirPath) {
+                    subDirectories.push_back(dirPath);
+                    return false;
+                }, nullptr, false, false);
+
+                if (subDirectories.empty()) {
+                    fprintf(stderr, "Could not find dlc subdirectories inside '%s'\n", dir.c_str());
+                    exit(-1);
+                }
+
+                if (subDirectories.size() > 1) {
+                    fprintf(stderr, "Found too many subdirectories inside artifact path '%s'.  Use -release to select one\n", dir.c_str());
+                    exit(-1);
+                }
+
+                dylibCacheDir = subDirectories.front();
+            }
+        }
+
         if (dylibCacheDir.empty()) {
             dylibCacheDir = std::string("/AppleInternal/Developer/DylibCaches/") + release + ".dlc";
         }
@@ -290,7 +429,10 @@ int main(int argc, const char* argv[])
         chdir(dylibCacheDir.c_str());
 
         dispatch_async(dispatch_get_main_queue(), ^{
-            auto manifest = dyld3::Manifest(diags, dylibCacheDir + "/Manifest.plist", roots);
+            // If we only want a list of configuations, then tell the manifest to only parse the data and not
+            // actually get all the macho's.
+            bool onlyParseManifest = listConfigs && configuration.empty();
+            auto manifest = dyld3::Manifest(diags, dylibCacheDir + "/Manifest.plist", roots, onlyParseManifest);
 
             if (manifest.build().empty()) {
                 fprintf(stderr, "No manifest found at '%s/Manifest.plist'\n", dylibCacheDir.c_str());
@@ -302,6 +444,9 @@ int main(int argc, const char* argv[])
                 manifest.forEachConfiguration([](const std::string& configName) {
                     printf("%s\n", configName.c_str());
                 });
+                // If we weren't passed a configuration then exit
+                if (configuration.empty())
+                    exit(0);
             }
 
             if (!manifest.filterForConfig(configuration)) {
@@ -309,51 +454,327 @@ int main(int argc, const char* argv[])
                     configuration.c_str(), manifest.build().c_str());
                 exit(-1);
             }
-            manifest.calculateClosure();
 
-            std::vector<dyld3::BuildQueueEntry> buildQueue;
+            (void)mkpath_np((dstRoot + "/System/Library/Caches/com.apple.dyld/").c_str(), 0755);
+            bool cacheBuildSuccess = false;
+            if (useMRM) {
+
+                FILE* jsonFile = nullptr;
+                if (!emitJSONPath.empty()) {
+                    jsonFile = fopen(emitJSONPath.c_str(), "w");
+                    if (!jsonFile) {
+                        diags.verbose("can't open file '%s', errno=%d\n", emitJSONPath.c_str(), errno);
+                        return;
+                    }
+                }
+                dyld3::json::Node buildInvocationNode;
+
+                // Find the archs for the configuration we want.
+                __block std::set<std::string> validArchs;
+                manifest.configuration(configuration).forEachArchitecture(^(const std::string& path) {
+                    validArchs.insert(path);
+                });
+
+                if (validArchs.size() != 1) {
+                    fprintf(stderr, "MRM doesn't support more than one arch per configuration: %s\n",
+                            configuration.c_str());
+                    exit(-1);
+                }
+
+                const char* archs[validArchs.size()];
+                uint64_t archIndex = 0;
+                for (const std::string& arch : validArchs) {
+                    archs[archIndex++] = arch.c_str();
+                }
+
+                BuildOptions_v1 buildOptions;
+                buildOptions.version                            = 1;
+                buildOptions.updateName                         = manifest.build().c_str();
+                buildOptions.deviceName                         = configuration.c_str();
+                buildOptions.disposition                        = Disposition::Unknown;
+                buildOptions.platform                           = (Platform)manifest.platform();
+                buildOptions.archs                              = archs;
+                buildOptions.numArchs                           = validArchs.size();
+                buildOptions.verboseDiagnostics                 = debug;
+                buildOptions.isLocallyBuiltCache                = true;
+
+                __block struct SharedCacheBuilder* sharedCacheBuilder = createSharedCacheBuilder(&buildOptions);
+                buildInvocationNode.map["build-options"] = getBuildOptionsNode(buildOptions);
+
+                std::set<std::string> requiredBinaries =  {
+                    "/usr/lib/libSystem.B.dylib"
+                };
+
+                // Get the file data for every MachO in the BOM.
+                __block dyld3::json::Node filesNode;
+                __block std::vector<std::pair<const void*, size_t>> mappedFiles;
+                manifest.forEachMachO(configuration, ^(const std::string &buildPath, const std::string &runtimePath, const std::string &arch, bool shouldBeExcludedIfLeaf) {
+
+                    // Filter based on arch as the Manifest adds the file once for each UUID.
+                    if (!validArchs.count(arch))
+                        return;
+
+                    struct stat stat_buf;
+                    int fd = ::open(buildPath.c_str(), O_RDONLY, 0);
+                    if (fd == -1) {
+                        diags.verbose("can't open file '%s', errno=%d\n", buildPath.c_str(), errno);
+                        return;
+                    }
+
+                    if (fstat(fd, &stat_buf) == -1) {
+                        diags.verbose("can't stat open file '%s', errno=%d\n", buildPath.c_str(), errno);
+                        ::close(fd);
+                        return;
+                    }
+
+                    const void* buffer = mmap(NULL, (size_t)stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+                    if (buffer == MAP_FAILED) {
+                        diags.verbose("mmap() for file at %s failed, errno=%d\n", buildPath.c_str(), errno);
+                        ::close(fd);
+                    }
+                    ::close(fd);
+
+                    mappedFiles.emplace_back(buffer, (size_t)stat_buf.st_size);
+                    FileFlags fileFlags = FileFlags::NoFlags;
+                    if (requiredBinaries.count(runtimePath))
+                        fileFlags = FileFlags::RequiredClosure;
+                    addFile(sharedCacheBuilder, runtimePath.c_str(), (uint8_t*)buffer, (size_t)stat_buf.st_size, fileFlags);
+
+                    dyld3::json::Node fileNode;
+                    fileNode.map["path"].value  = runtimePath;
+                    fileNode.map["flags"].value = "NoFlags";
+                    filesNode.array.push_back(fileNode);
+                });
+
+                __block dyld3::json::Node symlinksNode;
+                manifest.forEachSymlink(configuration, ^(const std::string &fromPath, const std::string &toPath) {
+                    addSymlink(sharedCacheBuilder, fromPath.c_str(), toPath.c_str());
+
+                    dyld3::json::Node symlinkNode;
+                    symlinkNode.map["from-path"].value  = fromPath;
+                    symlinkNode.map["to-path"].value    = toPath;
+                    symlinksNode.array.push_back(symlinkNode);
+                });
+
+                buildInvocationNode.map["symlinks"] = symlinksNode;
+
+                std::string orderFileData;
+                if (!manifest.dylibOrderFile().empty()) {
+                    orderFileData = loadOrderFile(manifest.dylibOrderFile());
+                    if (!orderFileData.empty()) {
+                        addFile(sharedCacheBuilder, "*order file data*", (uint8_t*)orderFileData.data(), orderFileData.size(), FileFlags::DylibOrderFile);
+                        dyld3::json::Node fileNode;
+                        fileNode.map["path"].value  = manifest.dylibOrderFile();
+                        fileNode.map["flags"].value = "DylibOrderFile";
+                        filesNode.array.push_back(fileNode);
+                    }
+                }
+
+                std::string dirtyDataOrderFileData;
+                if (!manifest.dirtyDataOrderFile().empty()) {
+                    dirtyDataOrderFileData = loadOrderFile(manifest.dirtyDataOrderFile());
+                    if (!dirtyDataOrderFileData.empty()) {
+                        addFile(sharedCacheBuilder, "*dirty data order file data*", (uint8_t*)dirtyDataOrderFileData.data(), dirtyDataOrderFileData.size(), FileFlags::DirtyDataOrderFile);
+                        dyld3::json::Node fileNode;
+                        fileNode.map["path"].value  = manifest.dirtyDataOrderFile();
+                        fileNode.map["flags"].value = "DirtyDataOrderFile";
+                        filesNode.array.push_back(fileNode);
+                    }
+                }
+
+                buildInvocationNode.map["files"] = filesNode;
+
+                if (jsonFile) {
+                    dyld3::json::printJSON(buildInvocationNode, 0, jsonFile);
+                    fclose(jsonFile);
+                    jsonFile = nullptr;
+                }
+
+                cacheBuildSuccess = runSharedCacheBuilder(sharedCacheBuilder);
+
+                if (!cacheBuildSuccess) {
+                    for (uint64 i = 0, e = getErrorCount(sharedCacheBuilder); i != e; ++i) {
+                        const char* errorMessage = getError(sharedCacheBuilder, i);
+                        fprintf(stderr, "ERROR: %s\n", errorMessage);
+                    }
+                }
+
+                // Now emit each cache we generated, or the errors for them.
+                for (uint64 i = 0, e = getCacheResultCount(sharedCacheBuilder); i != e; ++i) {
+                    BuildResult result;
+                    getCacheResult(sharedCacheBuilder, i, &result);
+                    if (result.numErrors) {
+                        for (uint64_t errorIndex = 0; errorIndex != result.numErrors; ++errorIndex) {
+                            fprintf(stderr, "[%s] ERROR: %s\n", result.loggingPrefix, result.errors[errorIndex]);
+                        }
+                        cacheBuildSuccess = false;
+                        continue;
+                    }
+                    if (result.numWarnings) {
+                        for (uint64_t warningIndex = 0; warningIndex != result.numWarnings; ++warningIndex) {
+                            fprintf(stderr, "[%s] WARNING: %s\n", result.loggingPrefix, result.warnings[warningIndex]);
+                        }
+                    }
+                }
+
+                // If we built caches, then write everything out.
+                // TODO: Decide if we should we write any good caches anyway?
+                if (cacheBuildSuccess) {
+                    for (uint64 i = 0, e = getFileResultCount(sharedCacheBuilder); i != e; ++i) {
+                        FileResult result;
+                        getFileResult(sharedCacheBuilder, i, &result);
+
+                        if (!result.data)
+                            continue;
+
+                        const std::string path = dstRoot + result.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 ) {
+                            ::ftruncate(fd, result.size);
+                            uint64_t writtenSize = pwrite(fd, result.data, result.size, 0);
+                            if ( writtenSize == result.size ) {
+                                ::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);
+                                    continue; // success
+                                }
+                            }
+                            else {
+                                fprintf(stderr, "ERROR: could not write file %s\n", pathTemplateSpace);
+                                cacheBuildSuccess = false;
+                            }
+                            ::close(fd);
+                            ::unlink(pathTemplateSpace);
+                        }
+                        else {
+                            fprintf(stderr, "ERROR: could not open file %s\n", pathTemplateSpace);
+                            cacheBuildSuccess = false;
+                        }
+                    }
+                }
+
+                destroySharedCacheBuilder(sharedCacheBuilder);
 
-            bool cacheBuildSuccess = build(diags, manifest, dstRoot, false, debug, false, false);
+                for (auto mappedFile : mappedFiles)
+                    ::munmap((void*)mappedFile.first, mappedFile.second);
+            } else {
+                manifest.calculateClosure();
+
+                cacheBuildSuccess = build(diags, manifest, dstRoot, false, debug, false, false, emitDevCaches, true);
+            }
 
             if (!cacheBuildSuccess) {
                 exit(-1);
             }
 
-            writeRootList(dstRoot, roots);
+            // Compare this cache to the baseline cache and see if we have any roots to copy over
+            if (!baselineDifferenceResultPath.empty() || baselineCopyRoots) {
+                std::set<std::string> baselineDylibs = manifest.resultsForConfiguration(configuration);
+
+                std::set<std::string> newDylibs;
+                manifest.forEachConfiguration([&manifest, &newDylibs](const std::string& configName) {
+                    for (auto& arch : manifest.configuration(configName).architectures) {
+                        for (auto& dylib : arch.second.results.dylibs) {
+                            if (dylib.second.included) {
+                                newDylibs.insert(manifest.installNameForUUID(dylib.first));
+                            }
+                        }
+                    }
+                });
+
+                if (baselineCopyRoots) {
+                    // Work out the set of dylibs in the old cache but not the new one
+                    std::set<std::string> dylibsMissingFromNewCache;
+                    for (const std::string& baselineDylib : baselineDylibs) {
+                        if (!newDylibs.count(baselineDylib))
+                            dylibsMissingFromNewCache.insert(baselineDylib);
+                    }
+
+                    if (!dylibsMissingFromNewCache.empty()) {
+                        BOMCopier copier = BOMCopierNewWithSys(BomSys_default());
+                        BOMCopierSetUserData(copier, (void*)&dylibsMissingFromNewCache);
+                        BOMCopierSetCopyFileStartedHandler(copier, filteredCopyIncludingPaths);
+                        std::string dylibCacheRootDir = realFilePath(dylibCacheDir + "/Root");
+                        if (dylibCacheRootDir == "") {
+                            fprintf(stderr, "Could not find dylib Root directory to copy baseline roots from\n");
+                            exit(1);
+                        }
+                        BOMCopierCopy(copier, dylibCacheRootDir.c_str(), dstRoot.c_str());
+                        BOMCopierFree(copier);
+
+                        for (const std::string& dylibMissingFromNewCache : dylibsMissingFromNewCache) {
+                            diags.verbose("Dylib missing from new cache: '%s'\n", dylibMissingFromNewCache.c_str());
+                        }
+                    }
+                }
+
+                if (!baselineDifferenceResultPath.empty()) {
+                    auto cppToObjStr = [](const std::string& str) {
+                        return [NSString stringWithUTF8String:str.c_str()];
+                    };
+
+                    // Work out the set of dylibs in the cache and taken from the -root
+                    NSMutableArray<NSString*>* dylibsFromRoots = [NSMutableArray array];
+                    for (auto& root : roots) {
+                        for (const std::string& dylibInstallName : newDylibs) {
+                            struct stat sb;
+                            std::string filePath = root + "/" + dylibInstallName;
+                            if (!stat(filePath.c_str(), &sb)) {
+                                [dylibsFromRoots addObject:cppToObjStr(dylibInstallName)];
+                            }
+                        }
+                    }
+
+                    // Work out the set of dylibs in the new cache but not in the baseline cache.
+                    NSMutableArray<NSString*>* dylibsMissingFromBaselineCache = [NSMutableArray array];
+                    for (const std::string& newDylib : newDylibs) {
+                        if (!baselineDylibs.count(newDylib))
+                            [dylibsMissingFromBaselineCache addObject:cppToObjStr(newDylib)];
+                    }
+
+                    NSMutableDictionary* cacheDict = [[NSMutableDictionary alloc] init];
+                    cacheDict[@"root-paths-in-cache"] = dylibsFromRoots;
+                    cacheDict[@"device-paths-to-delete"] = dylibsMissingFromBaselineCache;
+
+                    NSError* error = nil;
+                    NSData*  outData = [NSPropertyListSerialization dataWithPropertyList:cacheDict
+                                                                                  format:NSPropertyListBinaryFormat_v1_0
+                                                                                 options:0
+                                                                                   error:&error];
+                    (void)[outData writeToFile:cppToObjStr(baselineDifferenceResultPath) atomically:YES];
+                }
+            }
 
             if (copyRoots) {
-                manifest.forEachConfiguration([&manifest](const std::string& configName) {
+                std::set<std::string> cachePaths;
+                manifest.forEachConfiguration([&manifest, &cachePaths](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());
+                                cachePaths.insert(manifest.installNameForUUID(dylib.first));
                             }
                         }
                     }
                 });
 
                 BOMCopier copier = BOMCopierNewWithSys(BomSys_default());
-                BOMCopierSetCopyFileStartedHandler(copier, filteredCopy);
+                BOMCopierSetUserData(copier, (void*)&cachePaths);
+                BOMCopierSetCopyFileStartedHandler(copier, filteredCopyExcludingPaths);
                 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.
 
@@ -364,7 +785,7 @@ int main(int argc, const char* argv[])
             const char* args[8];
             args[0] = (char*)"/bin/rm";
             args[1] = (char*)"-rf";
-            args[2] = (char*)tempRootDir;
+            args[2] = (char*)tempRootsDir;
             args[3] = nullptr;
             (void)runCommandAndWait(diags, args);
 
index 588d7d87251ccfc9684cc48e6d60b450435c3072..8bc1632ad64891240dc47bbbb5668a3e6cc1604c 100644 (file)
@@ -55,7 +55,7 @@
 #include <iostream>
 #include <fstream>
 
-#include "MachOParser.h"
+#include "MachOFile.h"
 #include "FileUtils.h"
 #include "StringUtils.h"
 #include "DyldSharedCache.h"
@@ -86,21 +86,24 @@ static bool addIfMachO(const std::string& buildRootPath, const std::string& runt
         Diagnostics diag;
         bool usedWholeFile = false;
         for (MappedMachOsByCategory& file : files) {
-            size_t sliceOffset;
-            size_t sliceLength;
+            uint64_t sliceOffset = 0;
+            uint64_t sliceLength = statBuf.st_size;
             bool fatButMissingSlice;
             const void* slice = MAP_FAILED;
-            if ( dyld3::FatUtil::isFatFileWithSlice(diag, wholeFile, statBuf.st_size, file.archName, sliceOffset, sliceLength, fatButMissingSlice) ) {
+            const dyld3::FatFile* fh = (dyld3::FatFile*)wholeFile;
+            const dyld3::MachOFile* mh = (dyld3::MachOFile*)wholeFile;
+            if ( fh->isFatFileWithSlice(diag, statBuf.st_size, file.archName.c_str(), 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) ) {
+                    mh = (dyld3::MachOFile*)slice;
+                    if ( !mh->isMachO(diag, sliceLength) ) {
                         ::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) ) {
+            else if ( !fatButMissingSlice && mh->isMachO(diag, sliceLength) ) {
                 slice           = wholeFile;
                 sliceLength     = statBuf.st_size;
                 sliceOffset     = 0;
@@ -108,15 +111,14 @@ static bool addIfMachO(const std::string& buildRootPath, const std::string& runt
                 //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 ) {
+                mh = (dyld3::MachOFile*)slice;
+                if ( mh->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() ) {
+                    if ( mh->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);
                     }
@@ -124,9 +126,6 @@ static bool addIfMachO(const std::string& buildRootPath, const std::string& runt
                         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;
                 }
@@ -275,6 +274,7 @@ int main(int argc, const char* argv[])
                 break;
             case dyld3::Platform::watchOS:
                 archStrs.insert("armv7k");
+                archStrs.insert("arm64_32");
                 break;
              case dyld3::Platform::unknown:
              case dyld3::Platform::macOS:
@@ -289,6 +289,8 @@ int main(int argc, const char* argv[])
     std::vector<MappedMachOsByCategory> allFileSets;
     if ( archStrs.count("arm64") )
         allFileSets.push_back({"arm64"});
+    if ( archStrs.count("arm64_32") )
+        allFileSets.push_back({"arm64_32"});
     if ( archStrs.count("armv7k") )
         allFileSets.push_back({"armv7k"});
     std::vector<std::string> paths;
@@ -320,6 +322,7 @@ int main(int argc, const char* argv[])
         options.inodesAreSameAsRuntime       = false;
         options.cacheSupportsASLR            = true;
         options.forSimulator                 = false;
+        options.isLocallyBuiltCache          = true;
         options.verbose                      = verbose;
         options.evictLeafDylibsOnOverflow    = false;
         options.pathPrefixes                 = { rootPath };
diff --git a/dyld3/shared-cache/mrm_shared_cache_builder.cpp b/dyld3/shared-cache/mrm_shared_cache_builder.cpp
new file mode 100644 (file)
index 0000000..7bb7654
--- /dev/null
@@ -0,0 +1,647 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * 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 "mrm_shared_cache_builder.h"
+#include "CacheBuilder.h"
+#include "ClosureFileSystem.h"
+#include "FileUtils.h"
+#include <pthread.h>
+#include <memory>
+#include <vector>
+#include <map>
+
+static const uint64_t kMinBuildVersion = 1; //The minimum version BuildOptions struct we can support
+static const uint64_t kMaxBuildVersion = 1; //The maximum version BuildOptions struct we can support
+
+namespace dyld3 {
+namespace closure {
+
+struct FileInfo {
+    const char*     path;
+    const uint8_t*  data;
+    const uint64_t  length;
+    FileFlags       flags;
+    uint64_t        mtime;
+    uint64_t        inode;
+};
+
+class FileSystemMRM : public FileSystem {
+public:
+    FileSystemMRM() : FileSystem() { }
+
+    bool getRealPath(const char possiblePath[MAXPATHLEN], char realPath[MAXPATHLEN]) const override {
+        Diagnostics diag;
+        std::string resolvedPath = symlinkResolver.realPath(diag, possiblePath);
+        if (diag.hasError()) {
+            diag.verbose("MRM error: %s\n", diag.errorMessage().c_str());
+            diag.clearError();
+            return false;
+        }
+
+        // FIXME: Should we only return real paths of files which point to macho's?  For now that is what we are doing
+        auto it = fileMap.find(resolvedPath);
+        if (it == fileMap.end())
+            return false;
+
+        memcpy(realPath, resolvedPath.c_str(), std::min((size_t)MAXPATHLEN, resolvedPath.size() + 1));
+        return true;
+    }
+
+    bool loadFile(const char* path, LoadedFileInfo& info, char realerPath[MAXPATHLEN], void (^error)(const char* format, ...)) const override {
+        Diagnostics diag;
+        std::string resolvedPath = symlinkResolver.realPath(diag, path);
+        if (diag.hasError()) {
+            diag.verbose("MRM error: %s\n", diag.errorMessage().c_str());
+            diag.clearError();
+            return false;
+        }
+
+        auto it = fileMap.find(resolvedPath);
+        if (it == fileMap.end())
+            return false;
+
+        if (resolvedPath == path)
+            realerPath[0] = '\0';
+        else
+            memcpy(realerPath, resolvedPath.c_str(), std::min((size_t)MAXPATHLEN, resolvedPath.size() + 1));
+
+        // The file exists at this exact path.  Lets use it!
+        const FileInfo& fileInfo = files[it->second];
+
+        info.fileContent                = fileInfo.data;
+        info.fileContentLen             = fileInfo.length;
+        info.sliceOffset                = 0;
+        info.sliceLen                   = fileInfo.length;
+        info.inode                      = fileInfo.inode;
+        info.mtime                      = fileInfo.mtime;
+        info.unload                     = nullptr;
+        info.path                       = path;
+        return true;
+    }
+
+    void unloadFile(const LoadedFileInfo& info) const override {
+        if (info.unload)
+            info.unload(info);
+    }
+
+    void unloadPartialFile(LoadedFileInfo& info, uint64_t keepStartOffset, uint64_t keepLength) const override {
+        // Note we don't actually unload the data here, but we do want to update the offsets for other data structures to track where we are
+        info.fileContent = (const void*)((char*)info.fileContent + keepStartOffset);
+        info.fileContentLen = keepLength;
+    }
+
+    bool fileExists(const char* path, uint64_t* inode=nullptr, uint64_t* mtime=nullptr, bool* issetuid=nullptr) const override {
+        Diagnostics diag;
+        std::string resolvedPath = symlinkResolver.realPath(diag, path);
+        if (diag.hasError()) {
+            diag.verbose("MRM error: %s\n", diag.errorMessage().c_str());
+            diag.clearError();
+            return false;
+        }
+
+        auto it = fileMap.find(resolvedPath);
+        if (it == fileMap.end())
+            return false;
+
+        // The file exists at this exact path.  Lets use it!
+        const FileInfo& fileInfo = files[it->second];
+        if (inode)
+            *inode = fileInfo.inode;
+        if (mtime)
+            *mtime = fileInfo.mtime;
+        if (issetuid)
+            *issetuid = false;
+        return true;
+    }
+
+    // MRM file APIs
+    bool addFile(const char* path, uint8_t* data, uint64_t size, Diagnostics& diag, FileFlags fileFlags) {
+        auto iteratorAndInserted = fileMap.insert(std::make_pair(path, files.size()));
+        if (!iteratorAndInserted.second) {
+            diag.error("Already have content for path: '%s'", path);
+            return false;
+        }
+
+        symlinkResolver.addFile(diag, path);
+        if (diag.hasError())
+            return false;
+
+        // on iOS, inode is used to hold hash of path
+        uint64_t hash = 0;
+        for (const char* s = path; *s != '\0'; ++s)
+            hash += hash*4 + *s;
+        uint64_t inode = hash;
+        uint64_t mtime = 0;
+
+        files.push_back((FileInfo){ path, data, size, fileFlags, mtime, inode });
+        return true;
+    }
+
+    bool addSymlink(const char* fromPath, const char* toPath, Diagnostics& diag) {
+        symlinkResolver.addSymlink(diag, fromPath, toPath);
+        return !diag.hasError();
+    }
+
+    void forEachFileInfo(std::function<void(const char* path, FileFlags fileFlags)> lambda) {
+        for (const FileInfo& fileInfo : files)
+            lambda(fileInfo.path, fileInfo.flags);
+    }
+
+    size_t fileCount() const {
+        return files.size();
+    }
+
+    std::vector<DyldSharedCache::FileAlias> getResolvedSymlinks(Diagnostics& diag) {
+        return symlinkResolver.getResolvedSymlinks(diag);
+    }
+
+private:
+    std::vector<FileInfo> files;
+    std::map<std::string, uint64_t> fileMap;
+    SymlinkResolver symlinkResolver;
+};
+
+} // namespace closure
+} // namespace dyld3
+
+struct BuildInstance {
+    std::unique_ptr<DyldSharedCache::CreateOptions> options;
+    std::unique_ptr<CacheBuilder>                   builder;
+    std::vector<CacheBuilder::InputFile>            inputFiles;
+    std::vector<const char*>                        errors;
+    std::vector<const char*>                        warnings;
+    std::vector<std::string>                        errorStrings;   // Owns the data for the errors
+    std::vector<std::string>                        warningStrings; // Owns the data for the warnings
+    uint8_t*                                        cacheData       = nullptr;
+    uint64_t                                        cacheSize       = 0;
+    uint8_t*                                        cacheMapData    = nullptr;
+    uint64_t                                        cacheMapSize    = 0;
+    std::string                                     cdHash;         // Owns the data for the cdHash
+};
+
+struct BuildFileResult {
+    std::string                                 path;
+    const uint8_t*                              data;
+    uint64_t                                    size;
+};
+
+struct SharedCacheBuilder {
+    SharedCacheBuilder(const BuildOptions_v1* options);
+    const BuildOptions_v1*          options;
+    dyld3::closure::FileSystemMRM   fileSystem;
+
+    std::string dylibOrderFileData;
+    std::string dirtyDataOrderFileData;
+
+    // An array of builders and their options as we may have more than one builder for a given device variant.
+    std::vector<BuildInstance> builders;
+
+    // The results from all of the builders
+    // We keep this in a vector to own the data.
+    std::vector<BuildFileResult> fileResults;
+
+    std::vector<std::string> errors;
+    pthread_mutex_t lock;
+
+    enum State {
+        AcceptingFiles,
+        Building,
+        FinishedBuilding
+    };
+
+    State state = AcceptingFiles;
+
+    void runSync(void (^block)()) {
+        pthread_mutex_lock(&lock);
+        block();
+        pthread_mutex_unlock(&lock);
+    }
+
+    __attribute__((format(printf, 2, 3)))
+    void error(const char* format, ...) {
+        va_list list;
+        va_start(list, format);
+        Diagnostics diag;
+        diag.error(format, list);
+        va_end(list);
+
+        errors.push_back(diag.errorMessage());
+    }
+};
+
+SharedCacheBuilder::SharedCacheBuilder(const BuildOptions_v1* options) : options(options), lock(PTHREAD_MUTEX_INITIALIZER) {
+
+}
+
+void validiateBuildOptions(const BuildOptions_v1* options, SharedCacheBuilder& builder) {
+    if (options->version < kMinBuildVersion) {
+        builder.error("Builder version %llu is less than minimum supported version of %llu", options->version, kMinBuildVersion);
+    }
+    if (options->version > kMaxBuildVersion) {
+        builder.error("Builder version %llu is greater than maximum supported version of %llu", options->version, kMaxBuildVersion);
+    }
+    if (!options->updateName) {
+        builder.error("updateName must not be null");
+    }
+    if (!options->deviceName) {
+        builder.error("deviceName must not be null");
+    }
+    switch (options->disposition) {
+        case Disposition::Unknown:
+        case Disposition::InternalDevelopment:
+        case Disposition::Customer:
+            break;
+        default:
+            builder.error("unknown disposition value");
+            break;
+    }
+    switch (options->platform) {
+        case Platform::unknown:
+            builder.error("platform must not be unknown");
+            break;
+        case Platform::macOS:
+        case Platform::iOS:
+        case Platform::tvOS:
+        case Platform::watchOS:
+        case Platform::bridgeOS:
+        case Platform::iOSMac:
+        case Platform::iOS_simulator:
+        case Platform::tvOS_simulator:
+        case Platform::watchOS_simulator:
+            break;
+        default:
+            builder.error("unknown platform value");
+            break;
+    }
+    if (!options->archs) {
+        builder.error("archs must not be null");
+    }
+    if (!options->numArchs) {
+        builder.error("numArchs must not be 0");
+    }
+}
+
+struct SharedCacheBuilder* createSharedCacheBuilder(const BuildOptions_v1* options) {
+    SharedCacheBuilder* builder = new SharedCacheBuilder(options);
+
+    // Check the option struct values are valid
+    validiateBuildOptions(options, *builder);
+
+    return builder;
+}
+
+bool addFile(struct SharedCacheBuilder* builder, const char* path, uint8_t* data, uint64_t size, FileFlags fileFlags) {
+    __block bool success = false;
+    builder->runSync(^() {
+        if (builder->state != SharedCacheBuilder::AcceptingFiles) {
+            builder->error("Cannot add file: '%s' as we have already started building", path);
+            return;
+        }
+        size_t pathLength = strlen(path);
+        if (pathLength == 0) {
+            builder->error("Empty path");
+            return;
+        }
+        if (pathLength >= MAXPATHLEN) {
+            builder->error("Path is too long: '%s'", path);
+            return;
+        }
+        if (data == nullptr) {
+            builder->error("Data cannot be null for file: '%s'", path);
+            return;
+        }
+        switch (fileFlags) {
+            case NoFlags:
+            case MustBeInCache:
+            case ShouldBeExcludedFromCacheIfUnusedLeaf:
+            case RequiredClosure:
+                break;
+            case DylibOrderFile:
+                builder->dylibOrderFileData = std::string((char*)data, size);
+                success = true;
+                return;
+            case DirtyDataOrderFile:
+                builder->dirtyDataOrderFileData = std::string((char*)data, size);
+                success = true;
+                return;
+            default:
+                builder->error("unknown file flags value");
+                break;
+        }
+        Diagnostics diag;
+        if (!builder->fileSystem.addFile(path, data, size, diag, fileFlags)) {
+            builder->errors.push_back(diag.errorMessage());
+            return;
+        }
+        success = true;
+    });
+    return success;
+}
+
+bool addSymlink(struct SharedCacheBuilder* builder, const char* fromPath, const char* toPath) {
+    __block bool success = false;
+    builder->runSync(^() {
+        if (builder->state != SharedCacheBuilder::AcceptingFiles) {
+            builder->error("Cannot add file: '%s' as we have already started building", fromPath);
+            return;
+        }
+        size_t pathLength = strlen(fromPath);
+        if (pathLength == 0) {
+            builder->error("Empty path");
+            return;
+        }
+        if (pathLength >= MAXPATHLEN) {
+            builder->error("Path is too long: '%s'", fromPath);
+            return;
+        }
+        Diagnostics diag;
+        if (!builder->fileSystem.addSymlink(fromPath, toPath, diag)) {
+            builder->errors.push_back(diag.errorMessage());
+            return;
+        }
+        success = true;
+    });
+    return success;
+}
+
+static bool platformExcludeLocalSymbols(Platform platform) {
+    switch (platform) {
+        case Platform::unknown:
+        case Platform::macOS:
+            return false;
+        case Platform::iOS:
+        case Platform::tvOS:
+        case Platform::watchOS:
+        case Platform::bridgeOS:
+            return true;
+        case Platform::iOSMac:
+        case Platform::iOS_simulator:
+        case Platform::tvOS_simulator:
+        case Platform::watchOS_simulator:
+            return false;
+    }
+}
+
+static DyldSharedCache::CodeSigningDigestMode platformCodeSigningDigestMode(Platform platform) {
+    switch (platform) {
+        case Platform::unknown:
+        case Platform::macOS:
+        case Platform::iOS:
+        case Platform::tvOS:
+            return DyldSharedCache::SHA256only;
+        case Platform::watchOS:
+            return DyldSharedCache::Agile;
+        case Platform::bridgeOS:
+        case Platform::iOSMac:
+        case Platform::iOS_simulator:
+        case Platform::tvOS_simulator:
+        case Platform::watchOS_simulator:
+            return DyldSharedCache::SHA256only;
+    }
+}
+
+static bool platformIsForSimulator(Platform platform) {
+    switch (platform) {
+        case Platform::unknown:
+        case Platform::macOS:
+        case Platform::iOS:
+        case Platform::tvOS:
+        case Platform::watchOS:
+        case Platform::bridgeOS:
+        case Platform::iOSMac:
+            return false;
+        case Platform::iOS_simulator:
+        case Platform::tvOS_simulator:
+        case Platform::watchOS_simulator:
+            return true;
+    }
+}
+
+static const char* dispositionName(Disposition disposition) {
+    switch (disposition) {
+        case Disposition::Unknown:
+            return "";
+        case Disposition::InternalDevelopment:
+            return "Internal";
+        case Disposition::Customer:
+            return "Customer";
+        case Disposition::InternalMinDevelopment:
+            return "InternalMinDevelopment";
+    }
+}
+
+bool runSharedCacheBuilder(struct SharedCacheBuilder* builder) {
+    __block bool success = false;
+    builder->runSync(^() {
+        if (builder->state != SharedCacheBuilder::AcceptingFiles) {
+            builder->error("Builder has already been run");
+            return;
+        }
+        builder->state = SharedCacheBuilder::Building;
+        if (builder->fileSystem.fileCount() == 0) {
+            builder->error("Cannot run builder with no files");
+        }
+
+        Diagnostics diag;
+        std::vector<DyldSharedCache::FileAlias> aliases = builder->fileSystem.getResolvedSymlinks(diag);
+        if (diag.hasError()) {
+            diag.verbose("Symlink resolver error: %s\n", diag.errorMessage().c_str());
+        }
+
+        if (!builder->errors.empty()) {
+            builder->error("Skipping running shared cache builder due to previous errors");
+            return;
+        }
+
+        __block std::vector<CacheBuilder::InputFile> inputFiles;
+        builder->fileSystem.forEachFileInfo(^(const char* path, FileFlags fileFlags) {
+            CacheBuilder::InputFile::State state = CacheBuilder::InputFile::Unset;
+            switch (fileFlags) {
+                case FileFlags::NoFlags:
+                    state = CacheBuilder::InputFile::Unset;
+                    break;
+                case FileFlags::MustBeInCache:
+                    state = CacheBuilder::InputFile::MustBeIncluded;
+                    break;
+                case FileFlags::ShouldBeExcludedFromCacheIfUnusedLeaf:
+                    state = CacheBuilder::InputFile::MustBeExcludedIfUnused;
+                    break;
+                case FileFlags::RequiredClosure:
+                    state = CacheBuilder::InputFile::MustBeIncluded;
+                    break;
+                case FileFlags::DylibOrderFile:
+                case FileFlags::DirtyDataOrderFile:
+                    builder->error("Order files should not be in the file system");
+                    return;
+            }
+            inputFiles.emplace_back((CacheBuilder::InputFile){ path, state });
+        });
+
+        auto addCacheConfiguration = ^(bool isOptimized) {
+            for (uint64_t i = 0; i != builder->options->numArchs; ++i) {
+                auto options = std::make_unique<DyldSharedCache::CreateOptions>((DyldSharedCache::CreateOptions){});
+                const char *cacheSuffix = (isOptimized ? "" : ".development");
+                std::string runtimePath = (builder->options->platform == Platform::macOS) ? "/private/var/db/dyld/" : "/System/Library/Caches/com.apple.dyld/";
+                options->outputFilePath = runtimePath + "dyld_shared_cache_" + builder->options->archs[i] + cacheSuffix;
+                options->outputMapFilePath = options->outputFilePath + ".map";
+                options->archName = builder->options->archs[i];
+                options->platform = (dyld3::Platform)builder->options->platform;
+                options->excludeLocalSymbols = platformExcludeLocalSymbols(builder->options->platform);
+                options->optimizeStubs = isOptimized;
+                options->optimizeObjC = true;
+                options->codeSigningDigestMode = platformCodeSigningDigestMode(builder->options->platform);
+                options->dylibsRemovedDuringMastering = true;
+                options->inodesAreSameAsRuntime = false;
+                options->cacheSupportsASLR = true;
+                options->forSimulator = platformIsForSimulator(builder->options->platform);
+                options->isLocallyBuiltCache = builder->options->isLocallyBuiltCache;
+                options->verbose = builder->options->verboseDiagnostics;
+                options->evictLeafDylibsOnOverflow = true;
+                options->loggingPrefix = std::string(builder->options->deviceName) + dispositionName(builder->options->disposition) + "." + builder->options->archs[i] + cacheSuffix;
+                options->pathPrefixes = { "" };
+                options->dylibOrdering = parseOrderFile(builder->dylibOrderFileData);
+                options->dirtyDataSegmentOrdering = parseOrderFile(builder->dirtyDataOrderFileData);
+
+                auto cacheBuilder = std::make_unique<CacheBuilder>(*options.get(), builder->fileSystem);
+                builder->builders.emplace_back((BuildInstance) { std::move(options), std::move(cacheBuilder), inputFiles });
+            }
+        };
+
+        // Enqueue a cache for each configuration
+        switch (builder->options->disposition) {
+            case Disposition::Unknown:
+            case Disposition::InternalDevelopment:
+                addCacheConfiguration(false);
+                addCacheConfiguration(true);
+                break;
+            case Disposition::Customer:
+                addCacheConfiguration(true);
+            case Disposition::InternalMinDevelopment:
+                addCacheConfiguration(false);
+        }
+
+        // FIXME: This step can run in parallel.
+        for (auto& buildInstance : builder->builders) {
+            CacheBuilder* builder = buildInstance.builder.get();
+            builder->build(buildInstance.inputFiles, aliases);
+
+            // First put the warnings in to a vector to own them.
+            buildInstance.warningStrings.reserve(builder->warnings().size());
+            for (const std::string& warning : builder->warnings())
+                buildInstance.warningStrings.push_back(warning);
+
+            // Then copy to a vector to reference the owner
+            buildInstance.warnings.reserve(buildInstance.warningStrings.size());
+            for (const std::string& warning : buildInstance.warningStrings)
+                buildInstance.warnings.push_back(warning.c_str());
+
+            if (!builder->errorMessage().empty()) {
+                // First put the errors in to a vector to own them.
+                buildInstance.errorStrings.push_back(builder->errorMessage());
+
+                // Then copy to a vector to reference the owner
+                buildInstance.errors.reserve(buildInstance.errorStrings.size());
+                for (const std::string& error : buildInstance.errorStrings)
+                    buildInstance.errors.push_back(error.c_str());
+            }
+
+            if (builder->errorMessage().empty()) {
+                builder->writeBuffer(buildInstance.cacheData, buildInstance.cacheSize);
+                builder->writeMapFileBuffer(buildInstance.cacheMapData, buildInstance.cacheMapSize);
+                buildInstance.cdHash = builder->cdHashFirst();
+            }
+        }
+
+        // Now that we have run all of the builds, collect the results
+        // First push file results for each of the shared caches we built
+        __block std::map<std::string, uint32_t> dylibsInCaches;
+        for (auto& buildInstance : builder->builders) {
+            CacheBuilder* cacheBuilder = buildInstance.builder.get();
+            if (!cacheBuilder->errorMessage().empty())
+                continue;
+            builder->fileResults.push_back((BuildFileResult) { buildInstance.options->outputFilePath, buildInstance.cacheData, buildInstance.cacheSize });
+            builder->fileResults.push_back((BuildFileResult) { buildInstance.options->outputMapFilePath, buildInstance.cacheMapData, buildInstance.cacheMapSize });
+
+            cacheBuilder->forEachCacheDylib(^(const std::string &path) {
+                ++dylibsInCaches[path];
+            });
+        }
+
+        // Add entries to tell us to remove all of the dylibs from disk which are in every cache.
+        const size_t numCaches = builder->builders.size();
+        for (const auto& dylibAndCount : dylibsInCaches) {
+            if (dylibAndCount.second == numCaches)
+                builder->fileResults.push_back((BuildFileResult) { dylibAndCount.first, nullptr, 0 });
+        }
+
+        builder->state = SharedCacheBuilder::FinishedBuilding;
+        success = true;
+    });
+    return success;
+}
+
+uint64_t getErrorCount(const struct SharedCacheBuilder* builder) {
+    return builder->errors.size();
+}
+
+const char* getError(const struct SharedCacheBuilder* builder, uint64_t errorIndex) {
+    if (errorIndex >= builder->errors.size())
+        return nullptr;
+    return builder->errors[errorIndex].c_str();
+}
+
+uint64_t getCacheResultCount(const struct SharedCacheBuilder* builder) {
+    return builder->builders.size();
+}
+
+void getCacheResult(struct SharedCacheBuilder* builder, uint64_t cacheIndex, BuildResult* result) {
+    if (cacheIndex >= builder->builders.size())
+        return;
+
+    BuildInstance& buildInstance = builder->builders[cacheIndex];
+
+    result->version         = 1;
+    result->loggingPrefix   = buildInstance.options->loggingPrefix.c_str();
+    result->warnings        = buildInstance.warnings.empty() ? nullptr : buildInstance.warnings.data();
+    result->numWarnings     = buildInstance.warnings.size();
+    result->errors          = buildInstance.errors.empty() ? nullptr : buildInstance.errors.data();
+    result->numErrors       = buildInstance.errors.size();
+    result->sharedCachePath = buildInstance.options->outputFilePath.c_str();
+    result->cdHash          = buildInstance.cdHash.c_str();
+}
+
+uint64_t getFileResultCount(const struct SharedCacheBuilder* builder) {
+    return builder->fileResults.size();
+}
+
+void getFileResult(struct SharedCacheBuilder* builder, uint64_t fileIndex, FileResult* result) {
+    if (fileIndex >= builder->fileResults.size())
+        return;
+    const BuildFileResult& buildFileResult = builder->fileResults[fileIndex];
+    *result = (FileResult) { buildFileResult.path.c_str(), buildFileResult.data, buildFileResult.size };
+}
+
+void destroySharedCacheBuilder(struct SharedCacheBuilder* builder) {
+    delete builder;
+}
diff --git a/dyld3/shared-cache/mrm_shared_cache_builder.h b/dyld3/shared-cache/mrm_shared_cache_builder.h
new file mode 100644 (file)
index 0000000..352255d
--- /dev/null
@@ -0,0 +1,141 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+ *
+ * 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 mrm_shared_cache_builder_hpp
+#define mrm_shared_cache_builder_hpp
+
+#include <Availability.h>
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Note, this should match PLATFORM_* values in <mach-o/loader.h>
+enum Platform {
+    unknown             = 0,
+    macOS               = 1,    // PLATFORM_MACOS
+    iOS                 = 2,    // PLATFORM_IOS
+    tvOS                = 3,    // PLATFORM_TVOS
+    watchOS             = 4,    // PLATFORM_WATCHOS
+    bridgeOS            = 5,    // PLATFORM_BRIDGEOS
+    iOSMac              = 6,    // PLATFORM_IOSMAC
+    iOS_simulator       = 7,    // PLATFORM_IOSIMULATOR
+    tvOS_simulator      = 8,    // PLATFORM_TVOSSIMULATOR
+    watchOS_simulator   = 9     // PLATFORM_WATCHOSSIMULATOR
+};
+
+enum Disposition
+{
+    Unknown                 = 0,
+    InternalDevelopment     = 1,
+    Customer                = 2,
+    InternalMinDevelopment  = 3
+};
+
+enum FileFlags
+{
+    // Note these are for macho inputs
+    NoFlags                                     = 0,
+    MustBeInCache                               = 1,
+    ShouldBeExcludedFromCacheIfUnusedLeaf       = 2,
+    RequiredClosure                             = 3,
+
+    // These are for the order files
+    DylibOrderFile                              = 100,
+    DirtyDataOrderFile                          = 101
+};
+
+struct BuildOptions_v1
+{
+    uint64_t                                    version;                        // Future proofing, set to 1
+    const char *                                updateName;                     // BuildTrain+UpdateNumber
+    const char *                                deviceName;
+    enum Disposition                            disposition;                    // Internal, Customer, etc.
+    enum Platform                               platform;                       // Enum: unknown, macOS, iOS, ...
+    const char **                               archs;
+    uint64_t                                    numArchs;
+    bool                                        verboseDiagnostics;
+    bool                                        isLocallyBuiltCache;
+};
+
+struct BuildResult {
+    uint64_t                                    version;            // Future proofing, set to 1
+    const char*                                 loggingPrefix;
+    const char **                               warnings;
+    uint64_t                                    numWarnings;
+    const char **                               errors;
+    uint64_t                                    numErrors;
+    const char*                                 sharedCachePath;
+    const char*                                 cdHash;
+};
+
+struct FileResult {
+    const char*                                 path;
+    const uint8_t*                              data;               // Owned by the cache builder.  Destroyed by destroySharedCacheBuilder
+    uint64_t                                    size;
+};
+
+struct SharedCacheBuilder;
+
+__API_AVAILABLE(macos(10.12))
+struct SharedCacheBuilder* createSharedCacheBuilder(const struct BuildOptions_v1* options);
+
+// Add a file.  Returns true on success.
+__API_AVAILABLE(macos(10.12))
+bool addFile(struct SharedCacheBuilder* builder, const char* path, uint8_t* data, uint64_t size, enum FileFlags fileFlags);
+
+__API_AVAILABLE(macos(10.12))
+bool addSymlink(struct SharedCacheBuilder* builder, const char* fromPath, const char* toPath);
+
+__API_AVAILABLE(macos(10.12))
+bool runSharedCacheBuilder(struct SharedCacheBuilder* builder);
+
+__API_AVAILABLE(macos(10.12))
+uint64_t getErrorCount(const struct SharedCacheBuilder* builder);
+
+__API_AVAILABLE(macos(10.12))
+const char* getError(const struct SharedCacheBuilder* builder, uint64_t errorIndex);
+
+__API_AVAILABLE(macos(10.12))
+uint64_t getCacheResultCount(const struct SharedCacheBuilder* builder);
+
+__API_AVAILABLE(macos(10.12))
+void getCacheResult(struct SharedCacheBuilder* builder, uint64_t cacheIndex, struct BuildResult* result);
+
+__API_AVAILABLE(macos(10.12))
+uint64_t getFileResultCount(const struct SharedCacheBuilder* builder);
+
+__API_AVAILABLE(macos(10.12))
+void getFileResult(struct SharedCacheBuilder* builder, uint64_t fileIndex, struct FileResult* result);
+
+__API_AVAILABLE(macos(10.12))
+void destroySharedCacheBuilder(struct SharedCacheBuilder* builder);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* mrm_shared_cache_builder_hpp */
index 8d106d1895abfffa9d6702d859b42eec040a9e09..49e58a2e4c6320fc4cb19d1ca04cb3f0c9d7c32e 100644 (file)
@@ -246,7 +246,7 @@ int main(int argc, const char* argv[])
                     makeBoms(manifest, masterDstRoot);
                 });
                 allBuildsSucceeded = build(diags, manifest, masterDstRoot, true, verbose, skipWrites,
-                                           agileChooseSHA256CdHash);
+                                           agileChooseSHA256CdHash, true, false);
             }
 
             manifest.write(resultPath);
index 8fca51c983019adf55e22b80d406d988b556c287..cf9e21a1b2af20a958d9b8c48e7bc2c7f312845f 100644 (file)
@@ -25,9 +25,9 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
-#include <sys/xattr.h>
 #include <mach/mach.h>
 #include <mach/mach_time.h>
+#include <mach-o/dyld.h>
 #include <limits.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <iostream>
 #include <fstream>
 
-#include "MachOParser.h"
 #include "FileUtils.h"
 #include "StringUtils.h"
 #include "DyldSharedCache.h"
+#include "MachOFile.h"
+#include "MachOAnalyzer.h"
+#include "ClosureFileSystemPhysical.h"
 
 struct MappedMachOsByCategory
 {
@@ -136,6 +138,7 @@ static const char* sDontUsePrefixes[] = {
     "/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
+    "/System/Library/Developer/CoreSimulator/",  // ignore Marzipan
 };
 
 
@@ -143,94 +146,58 @@ 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)
+static bool addIfMachO(const dyld3::closure::FileSystem& fileSystem, 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 )
+    if ( startsWith(runtimePath, "/usr/lib/system/introspection/") )
         return false;
+
+    auto warningHandler = ^(const char* msg) {
+        if ( verbose )
+            fprintf(stderr, "update_dyld_shared_cache: warning: cannot build dlopen closure for '%s' because %s\n", runtimePath.c_str(), msg);
+    };
+
     bool result = false;
-    const void* wholeFile = ::mmap(NULL, statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
-    if ( wholeFile != MAP_FAILED ) {
+    for (MappedMachOsByCategory& file : files) {
         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;
-                    }
+        dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, runtimePath.c_str(), file.archName.c_str(), dyld3::Platform::macOS);
+        const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
+        if ( ma != nullptr ) {
+            bool sipProtected = false; // isProtectedBySIP(fd);
+            bool issetuid     = false;
+            if ( ma->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, ma, loadedFileInfo.sliceLen, issetuid, sipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino);
                 }
             }
-            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);
+            else if ( ma->canBePlacedInDyldCache(runtimePath.c_str(), ^(const char* msg) {}) ) {
+                // when SIP is enabled, only dylib protected by SIP can go in cache
+                if ( !requireSIP || sipProtected )
+                    file.dylibsForCache.emplace_back(runtimePath, ma, loadedFileInfo.sliceLen, issetuid, sipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+                else if ( ma->canHavePrecomputedDlopenClosure(runtimePath.c_str(), warningHandler) )
+                    file.otherDylibsAndBundles.emplace_back(runtimePath, ma, loadedFileInfo.sliceLen, issetuid, sipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino);
             }
-            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 ( ma->isDylib() ) {
+                    std::string installName = ma->installName();
+                    if ( startsWith(installName, "@") && !contains(runtimePath, ".app/") && !contains(runtimePath, ".xpc/") ) {
+                        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());
                     }
                 }
-                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);
+                else if ( ma->canHavePrecomputedDlopenClosure(runtimePath.c_str(), warningHandler) ) {
+                    file.otherDylibsAndBundles.emplace_back(runtimePath, ma, loadedFileInfo.sliceLen, issetuid, sipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino);
                 }
-                result = true;
             }
+            result = true;
         }
-        if ( !usedWholeFile )
-            ::munmap((void*)wholeFile, statBuf.st_size);
     }
-    ::close(fd);
+
     return result;
 }
 
@@ -244,6 +211,7 @@ static void findAllFiles(const std::vector<std::string>& pathPrefixes, bool requ
     bool multiplePrefixes = (pathPrefixes.size() > 1);
     for (const std::string& prefix : pathPrefixes) {
         // get all files from overlay for this search dir
+        dyld3::closure::FileSystemPhysical fileSystem(prefix.c_str());
         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)
@@ -260,7 +228,7 @@ static void findAllFiles(const std::vector<std::string>& pathPrefixes, bool requ
                     return;
 
                 // if the file is mach-o, add to list
-                if ( addIfMachO(prefix, path, statBuf, requireSIP, files) ) {
+                if ( addIfMachO(fileSystem, path, statBuf, requireSIP, files) ) {
                     if ( multiplePrefixes )
                         alreadyUsed.insert(path);
                 }
@@ -322,7 +290,8 @@ static void findOSFilesViaBOMS(const std::vector<std::string>& pathPrefixes, boo
                                     struct stat statBuf2;
                                     std::string fullPath2 = prefix2 + runPath;
                                     if ( stat(fullPath2.c_str(), &statBuf2) == 0 ) {
-                                        addIfMachO(prefix2, runPath, statBuf2, requireSIP, files);
+                                        dyld3::closure::FileSystemPhysical fileSystem(prefix2.c_str());
+                                        addIfMachO(fileSystem, runPath, statBuf2, requireSIP, files);
                                         runtimePathsFound.insert(runPath);
                                         break;
                                     }
@@ -379,8 +348,7 @@ static bool dontCache(const std::string& volumePrefix, const std::string& archNa
         return true;
     }
 
-    dyld3::MachOParser parser(aFile.mh);
-    const char* installName = parser.installName();
+    const char* installName = aFile.mh->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;
@@ -399,6 +367,17 @@ static bool dontCache(const std::string& volumePrefix, const std::string& archNa
                 return false;
             }
         }
+        // <rdar://problem/38000411> also if runtime path is a symlink to install name
+        std::string fullRuntime = volumePrefix + aFile.runtimePath;
+        if ( realpath(fullRuntime.c_str(), resolvedPath) != NULL ) {
+            std::string resolvedSymlink = resolvedPath;
+            if ( !volumePrefix.empty() ) {
+                resolvedSymlink = resolvedSymlink.substr(volumePrefix.size());
+            }
+            if ( resolvedSymlink == installName ) {
+                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;
     }
@@ -411,8 +390,7 @@ static void pruneCachedDylibs(const std::string& volumePrefix, const std::unorde
 
     std::unordered_map<std::string, std::string> installNameToFirstPath;
     for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) {
-        dyld3::MachOParser parser(aFile.mh);
-        const char* installName = parser.installName();
+        const char* installName = aFile.mh->installName();
         auto pos = installNameToFirstPath.find(installName);
         if ( pos == installNameToFirstPath.end() ) {
             installNameToFirstPath[installName] = aFile.runtimePath;
@@ -436,11 +414,9 @@ static void pruneOtherDylibs(const std::string& volumePrefix, MappedMachOsByCate
 {
     // 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());
+                                                       [&](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
@@ -448,9 +424,8 @@ static void pruneExecutables(const std::string& volumePrefix, MappedMachOsByCate
         [&](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) {
+            aFile.mh->forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t, uint32_t, bool &stop) {
                 if ( strcmp(loadPath, "/usr/lib/libxcselect.dylib") == 0 )
                     isXcodeShim = true;
             });
@@ -539,7 +514,7 @@ static bool runningOnHaswell()
         }                               \
     } while ( 0 )
 
-int main(int argc, const char* argv[])
+int main(int argc, const char* argv[], const char* envp[])
 {
     std::string                     rootPath;
     std::string                     overlayPath;
@@ -632,6 +607,31 @@ int main(int argc, const char* argv[])
             overlayPath = resolvedPath;
         }
     }
+
+    // <rdar://problem/36362221> update_dyld_shared_cache should re-exec() itself to a newer version
+    std::string newTool;
+    if ( !rootPath.empty() )
+        newTool = rootPath + "/usr/bin/update_dyld_shared_cache";
+    else if ( !overlayPath.empty() )
+        newTool = overlayPath + "/usr/bin/update_dyld_shared_cache";
+    if ( !newTool.empty() ) {
+        struct stat newToolStatBuf;
+        if ( stat(newTool.c_str(), &newToolStatBuf) == 0 ) {
+            char curToolPath[PATH_MAX];
+            uint32_t curToolPathsize = PATH_MAX;
+            int result = _NSGetExecutablePath(curToolPath, &curToolPathsize);
+            if ( result == 0 ) {
+                char resolvedCurToolPath[PATH_MAX];
+                if ( realpath(curToolPath, resolvedCurToolPath) != NULL ) {
+                    // don't re-exec if we are already running that tool
+                    if ( newTool != resolvedCurToolPath ) {
+                        execve(newTool.c_str(), (char**)argv, (char**)envp);
+                    }
+                }
+            }
+        }
+    }
+
     //
     // pathPrefixes for three modes:
     //   1) no options: { "" }           // search only boot volume
@@ -645,6 +645,12 @@ int main(int argc, const char* argv[])
 
 
     if ( cacheDir.empty() ) {
+        // if -cache_dir is not specified, then write() will eventually fail if we are not running as root
+        if ( geteuid() != 0 ) {
+            fprintf(stderr, "update_dyld_shared_cache: must be run as root (sudo)\n");
+            return 1;
+        }
+
         // write cache file into -root or -overlay directory, if used
         if ( rootPath != "/" )
             cacheDir = rootPath +  MACOSX_DYLD_SHARED_CACHE_DIR;
@@ -652,11 +658,11 @@ int main(int argc, const char* argv[])
             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);
+        fprintf(stderr, "update_dyld_shared_cache: could not access cache dir: mkpath_np(%s) failed errno=%d\n", cacheDir.c_str(), err);
         return 1;
     }
 
@@ -732,11 +738,33 @@ int main(int argc, const char* argv[])
                 std::string fullPath = prefix + runtimePath;
                 struct stat statBuf;
                 if ( stat(fullPath.c_str(), &statBuf) == 0 ) {
+                    dyld3::closure::FileSystemPhysical fileSystem(prefix.c_str());
+                    char resolvedPath[PATH_MAX];
+                    if ( realpath(fullPath.c_str(), resolvedPath) != NULL ) {
+                        std::string resolvedSymlink = resolvedPath;
+                        if ( !rootPath.empty() ) {
+                            resolvedSymlink = resolvedSymlink.substr(rootPath.size());
+                        }
+                        if ( (runtimePath != resolvedSymlink) && !contains(runtimePath, "InputContext") ) {  //HACK remove InputContext when fixed
+                           // path requested is a symlink path, check if real path already loaded
+                            for (const DyldSharedCache::MappedMachO& aDylibMapping : fileSet.dylibsForCache) {
+                                if ( aDylibMapping.runtimePath == resolvedSymlink ) {
+                                    if ( verbose )
+                                        fprintf(stderr, "verifySelfContained, redirect %s to %s\n", runtimePath.c_str(), aDylibMapping.runtimePath.c_str());
+                                    return aDylibMapping;
+                                }
+                            }
+                        }
+                    }
+
                     std::vector<MappedMachOsByCategory> mappedFiles;
                     mappedFiles.push_back({fileSet.archName});
-                    if ( addIfMachO(prefix, runtimePath, statBuf, requireDylibsBeRootlessProtected, mappedFiles) ) {
-                        if ( !mappedFiles.back().dylibsForCache.empty() )
+                   if ( addIfMachO(fileSystem, runtimePath, statBuf, requireDylibsBeRootlessProtected, mappedFiles) ) {
+                        if ( !mappedFiles.back().dylibsForCache.empty() ) {
+                            if ( verbose )
+                                fprintf(stderr, "verifySelfContained, add %s\n", mappedFiles.back().dylibsForCache.back().runtimePath.c_str());
                             return mappedFiles.back().dylibsForCache.back();
+                        }
                     }
                 }
             }
@@ -768,19 +796,17 @@ int main(int argc, const char* argv[])
         }
 
          // 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, "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.otherDylibsAndBundles) {
         //    fprintf(stderr, "  %s\n", aFile.runtimePath.c_str());
         //}
 
-        // Clear the UUID xattr for the existing cache.
-        // This prevents the existing cache from being used by dyld3 as roots are probably involved
-        if (removexattr(outFile.c_str(), "cacheUUID", 0) != 0) {
-            fprintf(stderr, "update_dyld_shared_cache: warning: failure to remove UUID xattr on shared cache file %s with error %s\n", outFile.c_str(), strerror(errno));
-        }
 
         // build cache new cache file
         DyldSharedCache::CreateOptions options;
+        options.outputFilePath               = outFile;
+        options.outputMapFilePath            = cacheDir + "/dyld_shared_cache_" + fileSet.archName + ".map";
         options.archName                     = fileSet.archName;
         options.platform                     = dyld3::Platform::macOS;
         options.excludeLocalSymbols          = false;
@@ -791,6 +817,7 @@ int main(int argc, const char* argv[])
         options.inodesAreSameAsRuntime       = true;
         options.cacheSupportsASLR            = (fileSet.archName != "i386");
         options.forSimulator                 = false;
+        options.isLocallyBuiltCache          = true;
         options.verbose                      = verbose;
         options.evictLeafDylibsOnOverflow    = true;
         options.pathPrefixes                 = pathPrefixes;
@@ -800,31 +827,12 @@ int main(int argc, const char* argv[])
         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;
+        if ( results.errorMessage.empty() ) {
+            wroteSomeCacheFile = true;
         }
         else {
-            // save new cache file to disk and write new .map file
-            assert(results.cacheContent != nullptr);
-            if ( !safeSave(results.cacheContent, results.cacheLength, outFile) ) {
-                fprintf(stderr, "update_dyld_shared_cache: could not write dyld cache file %s\n", outFile.c_str());
-                cacheBuildFailure = true;
-            }
-            if ( !cacheBuildFailure ) {
-                uuid_t cacheUUID;
-                results.cacheContent->getUUID(cacheUUID);
-                if (setxattr(outFile.c_str(), "cacheUUID", (const void*)&cacheUUID, sizeof(cacheUUID), 0, XATTR_CREATE) != 0) {
-                    fprintf(stderr, "update_dyld_shared_cache: warning: failure to set UUID xattr on shared cache file %s with error %s\n", outFile.c_str(), strerror(errno));
-                }
-                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);
+            fprintf(stderr, "update_dyld_shared_cache: %s\n", results.errorMessage.c_str());
+            cacheBuildFailure = true;
         }
     });
 
index 404bdc4d7a70d336634fa58cd18c57a4c259e799..31797ecbf2b76e1719a7332f2c90923390e0d327 100644 (file)
 #include <iostream>
 #include <fstream>
 
-#include "MachOParser.h"
+#include "MachOFile.h"
 #include "FileUtils.h"
 #include "StringUtils.h"
 #include "DyldSharedCache.h"
+#include "MachOAnalyzer.h"
+#include "ClosureFileSystemPhysical.h"
 
 
 
@@ -92,69 +94,28 @@ static const char* sMacOsAdditions[] = {
 
 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)
+static bool addIfMachO(const dyld3::closure::FileSystem& fileSystem, 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 ) {
+    for (MappedMachOsByCategory& file : files) {
         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;
-                }
+        dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, runtimePath.c_str(), file.archName.c_str(), dyld3::Platform::macOS);
+        const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
+        if ( ma != nullptr ) {
+            bool sipProtected = false; // isProtectedBySIP(fd);
+            bool issetuid     = false;
+            if ( ma->isDynamicExecutable() ) {
+                //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, ma, loadedFileInfo.sliceLen, issetuid, sipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+                result = true;
             }
+            else if ( ma->canBePlacedInDyldCache(runtimePath.c_str(), ^(const char* msg) {}) ) {
+                file.dylibsForCache.emplace_back(runtimePath, ma, loadedFileInfo.sliceLen, issetuid, sipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino);
+                result = true;
+           }
         }
-        if ( !usedWholeFile )
-            ::munmap((void*)wholeFile, statBuf.st_size);
-    }
-    ::close(fd);
+       }
     return result;
 }
 
@@ -165,6 +126,7 @@ static void findAllFiles(const std::string& simRuntimeRootPath, dyld3::Platform
         skipDirs.insert(s);
 
     for (const char* searchDir : sSearchDirs ) {
+        dyld3::closure::FileSystemPhysical fileSystem(simRuntimeRootPath.c_str());
         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);
@@ -176,17 +138,19 @@ static void findAllFiles(const std::string& simRuntimeRootPath, dyld3::Platform
                 return;
 
             // if the file is mach-o add to list
-            addIfMachO(simRuntimeRootPath, path, statBuf, platform, files);
+            addIfMachO(fileSystem, path, statBuf, platform, files);
          });
     }
 }
 
 static void addMacOSAdditions(std::vector<MappedMachOsByCategory>& allFileSets)
 {
+    dyld3::closure::FileSystemPhysical fileSystem;
     for (const char* addPath : sMacOsAdditions) {
         struct stat statBuf;
-        if ( stat(addPath, &statBuf) == 0 )
-            addIfMachO("", addPath, statBuf, dyld3::Platform::macOS, allFileSets);
+        if ( stat(addPath, &statBuf) == 0 ) {
+            addIfMachO(fileSystem, addPath, statBuf, dyld3::Platform::macOS, allFileSets);
+        }
     }
 }
 
@@ -208,8 +172,7 @@ static bool dontCache(const std::string& simRuntimeRootPath, const std::string&
         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();
+    const char* installName = aFile.mh->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;
@@ -242,8 +205,7 @@ static void pruneCachedDylibs(const std::string& simRuntimeRootPath, MappedMachO
     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();
+        const char* installName = aFile.mh->installName();
         auto pos = installNameToFirstPath.find(installName);
         if ( pos == installNameToFirstPath.end() ) {
             installNameToFirstPath[installName] = aFile.runtimePath;
@@ -351,13 +313,13 @@ int main(int argc, const char* argv[])
             verbose = true;
         }
         else if (strcmp(arg, "-tvOS") == 0) {
-            platform = dyld3::Platform::tvOS;
+            platform = dyld3::Platform::tvOS_simulator;
         }
         else if (strcmp(arg, "-iOS") == 0) {
-            platform = dyld3::Platform::iOS;
+            platform = dyld3::Platform::iOS_simulator;
         }
         else if (strcmp(arg, "-watchOS") == 0) {
-            platform = dyld3::Platform::watchOS;
+            platform = dyld3::Platform::watchOS_simulator;
         }
         else if ( strcmp(arg, "-runtime_dir") == 0 ) {
             TERMINATE_IF_LAST_ARG("-runtime_dir missing path argument\n");
@@ -406,22 +368,28 @@ int main(int argc, const char* argv[])
 
     if ( archStrs.empty() ) {
         switch ( platform ) {
-            case dyld3::Platform::iOS:
+            case dyld3::Platform::iOS_simulator:
                 archStrs.insert("x86_64");
                 break;
-            case dyld3::Platform::tvOS:
+            case dyld3::Platform::tvOS_simulator:
                 archStrs.insert("x86_64");
                 break;
-            case dyld3::Platform::watchOS:
+            case dyld3::Platform::watchOS_simulator:
                 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;
+             case dyld3::Platform::iOS:
+             case dyld3::Platform::tvOS:
+             case dyld3::Platform::watchOS:
+             case dyld3::Platform::iOSMac:
+             case dyld3::Platform::unknown:
+                assert(0 && "invalid platform");
+                break;
        }
     }
 
@@ -456,7 +424,8 @@ int main(int argc, const char* argv[])
             if ( stat(fullPath.c_str(), &statBuf) == 0 ) {
                 std::vector<MappedMachOsByCategory> mappedFiles;
                 mappedFiles.push_back({fileSet.archName});
-                if ( addIfMachO(rootPath, runtimePath, statBuf, platform, mappedFiles) ) {
+                dyld3::closure::FileSystemPhysical fileSystem(rootPath.c_str());
+                if ( addIfMachO(fileSystem, runtimePath, statBuf, platform, mappedFiles) ) {
                     if ( !mappedFiles.back().dylibsForCache.empty() )
                         return mappedFiles.back().dylibsForCache.back();
                 }
@@ -495,6 +464,8 @@ int main(int argc, const char* argv[])
 
         // build cache new cache file
         DyldSharedCache::CreateOptions options;
+        options.outputFilePath               = outFile;
+        options.outputMapFilePath            = cacheDir + "/dyld_shared_cache_" + fileSet.archName + ".map";
         options.archName                     = fileSet.archName;
         options.platform                     = platform;
         options.excludeLocalSymbols          = false;
@@ -505,6 +476,7 @@ int main(int argc, const char* argv[])
         options.inodesAreSameAsRuntime       = true;
         options.cacheSupportsASLR            = false;
         options.forSimulator                 = true;
+        options.isLocallyBuiltCache          = true;
         options.verbose                      = verbose;
         options.evictLeafDylibsOnOverflow    = true;
         options.pathPrefixes                 = { rootPath };
@@ -512,26 +484,12 @@ int main(int argc, const char* argv[])
 
         // 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());
+            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_sim_shared_cache: %s\n", results.errorMessage.c_str());
+            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);
-            }
-            // 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
index 770abd497fe3fd893141d5e05518f198352ee30f..6ceb38b62e3e12bbe625f64704c1affb9cbe7e97 100644 (file)
@@ -104,17 +104,7 @@ extern void _tlv_atexit(void (*termFunc)(void* objAddr), void* objAddr)      __O
  * 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
+extern void _tlv_bootstrap(void)                                             __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0);
 
 /*
  * The following dyld API's are deprecated as of Mac OS X 10.5.  They are either  
@@ -152,27 +142,27 @@ typedef enum {
     NSObjectFileImageAccess
 } NSObjectFileImageReturnCode;
 
-typedef struct __NSObjectFileImage*  NSObjectFileImage;
+typedef struct __NSObjectFileImage* NSObjectFileImage;
 
 
 
 /* NSObjectFileImage can only be used with MH_BUNDLE files */
-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 NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage)               __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "dlopen()");
+extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void *address, size_t size, NSObjectFileImage *objectFileImage) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "");
+extern bool                        NSDestroyObjectFileImage(NSObjectFileImage objectFileImage)                                             __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __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()");
+extern uint32_t     NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage)                   __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "");
+extern const char*  NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal)  __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "");
+extern uint32_t     NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage)                    __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "");
+extern const char*  NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, bool *tentative_definition) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "");
+extern bool         NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern void*        NSGetSectionDataInObjectFileImage(NSObjectFileImage objectFileImage, const char* segmentName, const char* sectionName, size_t *size) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "getsectiondata()");
 
 typedef struct __NSModule* NSModule;
-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 const char*  NSNameOfModule(NSModule m)         __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "");
+extern const char*  NSLibraryNameForModule(NSModule m) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "");
 
-extern NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlopen()");
+extern NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "dlopen()");
 #define NSLINKMODULE_OPTION_NONE                         0x0
 #define NSLINKMODULE_OPTION_BINDNOW                      0x1
 #define NSLINKMODULE_OPTION_PRIVATE                      0x2
@@ -180,27 +170,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) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
+extern bool NSUnLinkModule(NSModule module, uint32_t options) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __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)                                                    __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()");
+extern bool     NSIsSymbolNameDefined(const char* symbolName)                                                    __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern bool     NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint)               __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern bool     NSIsSymbolNameDefinedInImage(const struct mach_header* image, const char* symbolName)            __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern NSSymbol NSLookupAndBindSymbol(const char* symbolName)                                                    __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint)               __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName)                                  __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "dlsym()");
+extern NSSymbol NSLookupSymbolInImage(const struct mach_header* image, const char* symbolName, uint32_t options) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __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)    __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()");
+extern const char*  NSNameOfSymbol(NSSymbol symbol)    __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "");
+extern void *       NSAddressOfSymbol(NSSymbol symbol) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "dlsym()");
+extern NSModule     NSModuleForSymbol(NSSymbol symbol) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "dladdr()");
 
 /* error handling API */
 typedef enum {
@@ -228,7 +218,7 @@ typedef enum {
     NSOtherErrorInvalidArgs
 } NSOtherErrorNumbers;
 
-extern void NSLinkEditError(NSLinkEditErrors *c, int *errorNumber, const char** fileName, const char** errorString) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlerror()");
+extern void NSLinkEditError(NSLinkEditErrors *c, int *errorNumber, const char** fileName, const char** errorString) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "dlerror()");
 
 typedef struct {
      void     (*undefined)(const char* symbolName);
@@ -237,27 +227,27 @@ typedef struct {
                           const char* fileName, const char* errorString);
 } NSLinkEditErrorHandlers;
 
-extern void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers *handlers) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "");
+extern void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers *handlers) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "");
 
-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()");
+extern bool                      NSAddLibrary(const char* pathName)                   __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.4, "dlopen()");
+extern bool                      NSAddLibraryWithSearching(const char* pathName)      __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.4, "dlopen()");
+extern const struct mach_header* NSAddImage(const char* image_name, uint32_t options) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __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)                                                              __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 bool _dyld_present(void)                                                              __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "always true");
+extern bool _dyld_launched_prebound(void)                                                    __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "moot");
+extern bool _dyld_all_twolevel_modules_prebound(void)                                        __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.3, 10.5, "moot");
+extern bool _dyld_bind_fully_image_containing_address(const void* address)                   __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.5, "dlopen(RTLD_NOW)");
+extern bool _dyld_image_containing_address(const void* address)                              __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.3, 10.5, "dladdr()");
+extern void _dyld_lookup_and_bind(const char* symbol_name, void **address, NSModule* module) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __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) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.1, 10.4, "dlsym()");
+extern void _dyld_lookup_and_bind_fully(const char* symbol_name, void** address, NSModule* module) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __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()");
+extern const struct mach_header*  _dyld_get_image_header_containing_address(const void* address) __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos) __OSX_DEPRECATED(10.3, 10.5, "dladdr()");
 
 
 #if __cplusplus
index 4b2da8f13b5a738f4d4c8626f2f9a306353b3233..6adb8ab7670d4f56ad29d18be9f6449119a7e9af 100644 (file)
 #include <stdbool.h>
 #include <unistd.h>
 #include <mach/mach.h>
+#include <uuid/uuid.h>
+
+#if defined(__cplusplus) && (BUILDING_LIBDYLD || BUILDING_DYLD)
+#include <atomic>
+#endif
 
 #ifdef __cplusplus
 extern "C" {
@@ -95,7 +100,11 @@ enum {      dyld_error_kind_none=0,
 struct dyld_all_image_infos {
        uint32_t                                                version;                /* 1 in Mac OS X 10.4 and 10.5 */
        uint32_t                                                infoArrayCount;
-       const struct dyld_image_info*   infoArray;
+#if defined(__cplusplus) && (BUILDING_LIBDYLD || BUILDING_DYLD)
+    std::atomic<const struct dyld_image_info*> infoArray;
+#else
+    const struct dyld_image_info*    infoArray;
+#endif
        dyld_image_notifier                             notification;           
        bool                                                    processDetachedFromSharedRegion;
        /* the following fields are only in version 2 (Mac OS X 10.6, iPhoneOS 2.0) and later */
@@ -129,7 +138,12 @@ struct dyld_all_image_infos {
        uint8_t                                                 sharedCacheUUID[16];
        /* the following field is only in version 15 (macOS 10.12, iOS 10.0) and later */
        uintptr_t                                               sharedCacheBaseAddress;
+#if defined(__cplusplus) && (BUILDING_LIBDYLD || BUILDING_DYLD)
+    // We want this to be atomic in libdyld so that we can see updates when we map it shared
+    std::atomic<uint64_t>           infoArrayChangeTimestamp;
+#else
        uint64_t                                                infoArrayChangeTimestamp;
+#endif
        const char*                                             dyldPath;
        mach_port_t                                             notifyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
 #if __LP64__
index 93f1b90ffab3dca98f24d865e39851743ab86a48..0c3f74ecf7c7202e5bde8d649bcb1b69e9c81281 100644 (file)
 #ifndef _MACH_O_DYLD_PRIV_H_
 #define _MACH_O_DYLD_PRIV_H_
 
+#include <assert.h>
 #include <stdbool.h>
 #include <Availability.h>
 #include <TargetConditionals.h>
 #include <mach-o/dyld.h>
 #include <mach-o/dyld_images.h>
+#include <uuid/uuid.h>
 
 #if __cplusplus
 extern "C" {
@@ -39,7 +41,7 @@ extern "C" {
 //
 // private interface between libSystem.dylib and dyld
 //
-extern void _dyld_fork_child();
+extern void _dyld_fork_child(void);
 
 
 // DEPRECATED
@@ -171,7 +173,53 @@ extern const char* dyld_image_path_containing_address(const void* addr);
 // Exists in Mac OS X 10.11 and later
 extern const struct mach_header* dyld_image_header_containing_address(const void* addr);
 
+typedef uint32_t dyld_platform_t;
 
+typedef struct {
+    dyld_platform_t platform;
+    uint32_t        version;
+} dyld_build_version_t;
+
+// Returns the active platform of the process
+extern dyld_platform_t dyld_get_active_platform(void) __API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0), bridgeos(3.0));
+
+// Base platforms are platforms that have version numbers (macOS, iOS, watchos, tvOS, bridgeOS)
+// All other platforms are mapped to a base platform for version checks
+extern dyld_platform_t dyld_get_base_platform(dyld_platform_t platform) __API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0), bridgeos(3.0));
+
+// SPI to ask if a platform is a simulation platform
+extern bool dyld_is_simulator_platform(dyld_platform_t platform) __API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0), bridgeos(3.0));
+
+// Takes a version and returns if the image was built againt that SDK or newer
+// In the case of multi_plaform mach-o's it tests against the active platform
+extern bool dyld_sdk_at_least(const struct mach_header* mh, dyld_build_version_t version) __API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0), bridgeos(3.0));
+
+// Takes a version and returns if the image was built with that minos version or newer
+// In the case of multi_plaform mach-o's it tests against the active platform
+extern bool dyld_minos_at_least(const struct mach_header* mh, dyld_build_version_t version) __API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0), bridgeos(3.0));
+
+// Convenience versions of the previous two functions that run against the the main executable
+extern bool dyld_program_sdk_at_least(dyld_build_version_t version) __API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0), bridgeos(3.0));
+extern bool dyld_program_minos_at_least(dyld_build_version_t version) __API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0), bridgeos(3.0));
+
+// Function that walks through the load commands and calls the internal block for every version found
+// Intended as a fallback for very complex (and rare) version checks, or for tools that need to
+// print our everything for diagnostic reasons
+extern void dyld_get_image_versions(const struct mach_header* mh, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version)) __API_AVAILABLE(macos(10.14), ios(12.0), watchos(5.0), tvos(12.0), bridgeos(3.0));
+
+// Convienence constants for dyld version SPIs.
+
+//@VERSION_SET_DEFS@
+
+//@MACOS_PLATFORM_VERSION_DEFS@
+
+//@IOS_PLATFORM_VERSION_DEFS@
+
+//@WATCHOS_PLATFORM_VERSION_DEFS@
+
+//@TVOS_PLATFORM_VERSION_DEFS@
+
+//@BRIDGEOS_PLATFORM_VERSION_DEFS@
 
 // Convienence constants for return values from dyld_get_sdk_version() and friends.
 
@@ -181,7 +229,6 @@ extern const struct mach_header* dyld_image_header_containing_address(const void
 
 //@WATCHOS_VERSION_DEFS@
 
-
 //
 // This finds the SDK version a binary was built against.
 // Returns zero on error, or if SDK version could not be determined.
@@ -201,14 +248,13 @@ extern uint32_t dyld_get_sdk_version(const struct mach_header* mh);
 //
 // Exists in Mac OS X 10.8 and later 
 // Exists in iOS 6.0 and later
-extern uint32_t dyld_get_program_sdk_version();
-
+extern uint32_t dyld_get_program_sdk_version(void);
 
-#if __WATCH_OS_VERSION_MIN_REQUIRED
+#if TARGET_OS_WATCH
 // 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() __IOS_UNAVAILABLE __OSX_UNAVAILABLE __WATCHOS_AVAILABLE(2.0);
+extern uint32_t dyld_get_program_sdk_watch_os_version(void) __API_AVAILABLE(watchos(2.0));
 
 
 // watchOS only.
@@ -216,22 +262,21 @@ extern uint32_t dyld_get_program_sdk_watch_os_version() __IOS_UNAVAILABLE __OSX_
 // 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);
+extern uint32_t dyld_get_program_min_watch_os_version(void) __API_AVAILABLE(watchos(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();
+extern uint32_t dyld_get_program_sdk_bridge_os_version(void) __API_AVAILABLE(bridgeos(2.0));
 
 // 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();
+extern uint32_t dyld_get_program_min_bridge_os_version(void) __API_AVAILABLE(bridgeos(2.0));
 #endif
 
 //
@@ -249,7 +294,7 @@ extern uint32_t dyld_get_min_os_version(const struct mach_header* mh);
 //
 // Exists in Mac OS X 10.8 and later 
 // Exists in iOS 6.0 and later
-extern uint32_t dyld_get_program_min_os_version();
+extern uint32_t dyld_get_program_min_os_version(void);
 
 
 
@@ -259,7 +304,7 @@ extern uint32_t dyld_get_program_min_os_version();
 //
 // Exists in iPhoneOS 3.1 and later 
 // Exists in Mac OS X 10.10 and later
-extern bool dyld_shared_cache_some_image_overridden();
+extern bool dyld_shared_cache_some_image_overridden(void);
 
 
        
@@ -267,7 +312,7 @@ extern bool dyld_shared_cache_some_image_overridden();
 // Returns if the process is setuid or is code signed with entitlements.
 //
 // Exists in Mac OS X 10.9 and later
-extern bool dyld_process_is_restricted();
+extern bool dyld_process_is_restricted(void);
 
 
 
@@ -275,7 +320,7 @@ extern bool dyld_process_is_restricted();
 // Returns path used by dyld for standard dyld shared cache file for the current arch.
 //
 // Exists in Mac OS X 10.11 and later
-extern const char* dyld_shared_cache_file_path();
+extern const char* dyld_shared_cache_file_path(void);
 
 
 
@@ -371,6 +416,36 @@ extern const void* _dyld_get_shared_cache_range(size_t* length);
 
 
 
+
+struct dyld_image_uuid_offset {
+    uuid_t                      uuid;
+       uint64_t                    offsetInImage;
+    const struct mach_header*   image;
+};
+
+//
+// Given an array of addresses, returns info about each address.
+// Common usage is the array or addresses was produced by a stack backtrace.
+// For each address, returns the where that image was loaded, the offset
+// of the address in the image, and the image's uuid.  If a specified
+// address is unknown to dyld, all fields will be returned a zeros.
+//
+// Exists in macOS 10.14 and later
+// Exists in iOS 12.0 and later
+extern void _dyld_images_for_addresses(unsigned count, const void* addresses[], struct dyld_image_uuid_offset infos[]);
+
+
+//
+// Lets you register a callback which is called each time an image is loaded and provides the mach_header*, path, and
+// whether the image may be unloaded later.  During the call to _dyld_register_for_image_loads(), the callback is called
+// once for each image currently loaded.
+//
+// Exists in macOS 10.14 and later
+// Exists in iOS 12.0 and later
+extern void _dyld_register_for_image_loads(void (*func)(const struct mach_header* mh, const char* path, bool unloadable));
+
+
+
 //
 // When dyld must terminate a process because of a required dependent dylib
 // could not be loaded or a symbol is missing, dyld calls abort_with_reason()
@@ -411,15 +486,15 @@ extern const char*  __progname;
 
 
 // called by libSystem_initializer only
-extern void _dyld_initializer();
+extern void _dyld_initializer(void);
 
 // never called from source code. Used by static linker to implement lazy binding
-extern void dyld_stub_binder() __asm__("dyld_stub_binder");
+extern void dyld_stub_binder(void) __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();
+extern void _tlv_exit(void);
 
 
 // temp exports to keep tapi happy, until ASan stops using dyldVersionNumber
index 7db413f8075893481ac36d17ee1c80ab522d13e8..fd91ba9b60b6dc506675db0765bbb16a770a6c67 100644 (file)
@@ -27,6 +27,7 @@
 #include <unistd.h>
 #include <mach/mach.h>
 #include <dispatch/dispatch.h>
+#include <uuid/uuid.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -121,11 +122,11 @@ typedef const struct dyld_process_info_notify_base* dyld_process_info_notify;
 //
 extern dyld_process_info_notify _dyld_process_info_notify(task_t task, dispatch_queue_t queue,
                                                           void (^notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path),
-                                                          void (^notifyExit)(),
+                                                          void (^notifyExit)(void),
                                                           kern_return_t* kernelError);
 // add block to call right before main() is entered.
 // does nothing if process is already in main().
-extern void _dyld_process_info_notify_main(dyld_process_info_notify objc, void (^notifyMain)());
+extern void _dyld_process_info_notify_main(dyld_process_info_notify objc, void (^notifyMain)(void));
 
 
 // stop notifications and invalid dyld_process_info_notify object
index 6f2923009cc6d185ddd411d6a734ca5e040cd948..254008ddb652bd233d7e2593247c5c4e0449ef42 100644 (file)
@@ -581,7 +581,7 @@ struct objc_protocolopt_t : objc_stringhash_t {
         objc_stringhash_offset_t *o;
         
         o = protocolOffsets();
-        for (objc_stringhash_offset_t i = 0; i < capacity; i++) {
+        for (objc_stringhash_offset_t i = 0; i < (int)capacity; i++) {
             S32(o[i]);
         }
 
@@ -1038,13 +1038,13 @@ static void initnorm(key *keys, ub4 nkeys, ub4 alen, ub4 blen, ub4 smax, ub8 sal
 // gencode  *final;                          /* output, code for the final hash */
 {
   ub4 loga = log2u(alen);                            /* log based 2 of blen */
-  ub4 i;
-  for (i = 0; i < nkeys; i++) {
+  dispatch_apply(nkeys, DISPATCH_APPLY_AUTO, ^(size_t index) {
+    ub4 i = (ub4)index;
     key *mykey = keys+i;
     ub8 hash = lookup8(mykey->name_k, mykey->len_k, salt);
     mykey->a_k = (loga > 0) ? (ub4)(hash >> (UB8BITS-loga)) : 0;
     mykey->b_k = (blen > 1) ? (hash & (blen-1)) : 0;
-  }
+  });
 }
 
 
@@ -1431,7 +1431,7 @@ make_perfect(const string_map& strings)
   ub8       salt;                       /* a parameter to the hash function */
   ub4       scramble[SCRAMBLE_LEN];           /* used in final hash function */
   int ok;
-  int i;
+  uint32_t i;
   perfect_hash result;
 
   /* read in the list of keywords */
index fe3eea443f3969d41ebd781503a70d3a3730b67d..2da76e5a233427bca0c065db14d327d4885fded9 100644 (file)
@@ -54,6 +54,12 @@ struct arm64
 
 };
 
+struct arm64_32
+{
+       typedef Pointer32<LittleEndian>         P;
+
+};
+
 
 
 
index b1ac76c5ee7d62e3900904e7058cc060eabf7c10..e229bb3e56e3ab7cf60a28ff187f46d50c6ffa0c 100644 (file)
@@ -367,6 +367,30 @@ private:
 };
 
 
+template <typename E>
+class dyldCacheSlideInfo3 {
+public:
+    uint32_t        version() const                                INLINE { return E::get32(fields.version); }
+    void            set_version(uint32_t value)                    INLINE { E::set32(fields.version, value); }
+
+    uint32_t        page_starts_count() const                    INLINE { return E::get32(fields.page_starts_count); }
+    void            set_page_starts_count(uint32_t value)        INLINE { E::set32(fields.page_starts_count, value); }
+
+    uint32_t        page_size() const                            INLINE { return E::get32(fields.page_size); }
+    void            set_page_size(uint32_t value)                INLINE { E::set32(fields.page_size, value); }
+
+    uint64_t        auth_value_add() const                       INLINE { return E::get64(fields.auth_value_add); }
+    void            set_auth_value_add(uint64_t value)           INLINE { E::set64(fields.auth_value_add, value); }
+
+    uint16_t        page_starts(unsigned index) const                INLINE { return E::get16(fields.page_starts[index]); }
+    void            set_page_starts(unsigned index, uint16_t value) INLINE { E::set16(fields.page_starts[index], value); }
+
+
+private:
+    dyld_cache_slide_info3            fields;
+};
+
+
 
 template <typename E>
 class dyldCacheLocalSymbolsInfo {
index ebd298d46a876d870923ab5e6f4c0d0659b76094..c0c9550829bbef2ed5c8d1699479ff4f69d3a2b9 100644 (file)
@@ -68,6 +68,17 @@ struct uuid_command {
        #define CPU_TYPE_ARM64                          ((cpu_type_t) (CPU_TYPE_ARM | CPU_ARCH_ABI64))
 #endif
 
+#ifndef CPU_ARCH_ABI64_32
+       #define CPU_ARCH_ABI64_32                               ((cpu_type_t) 0x02000000)
+#endif
+#ifndef CPU_TYPE_ARM64_32
+       #define CPU_TYPE_ARM64_32                               ((cpu_type_t) (CPU_TYPE_ARM | CPU_ARCH_ABI64_32))
+#endif
+#ifndef CPU_SUBTYPE_ARM64_32_V8
+       #define CPU_SUBTYPE_ARM64_32_V8         ((cpu_subtype_t) 1)
+#endif
+
+
 #define ARM64_RELOC_UNSIGNED            0 // for pointers
 
 
@@ -956,7 +967,7 @@ inline int64_t read_sleb128(const uint8_t*& p, const uint8_t* end)
        } while (byte & 0x80);
        // sign extend negative numbers
        if ( (byte & 0x40) != 0 )
-               result |= (-1LL) << bit;
+               result |= (~0ULL) << bit;
        return result;
 }
 
index e55d74abd5f81a884b8293fccd1be38a32e8d76c..c36ab2c7c0f67ed4bd592b656b4905d62027f13a 100644 (file)
@@ -1,16 +1,16 @@
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
  *
  * Copyright (c) 2011 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
- * 
+ *
  * This file contains Original Code and/or Modifications of Original Code
  * as defined in and that are subject to the Apple Public Source License
  * Version 2.0 (the 'License'). You may not use this file except in
  * compliance with the License. Please obtain a copy of the License at
  * http://www.opensource.apple.com/apsl/ and read it before using this
  * file.
- * 
+ *
  * The Original Code and all software distributed under the License are
  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
@@ -18,7 +18,7 @@
  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  * Please see the License for the specific language governing rights and
  * limitations under the License.
- * 
+ *
  * @APPLE_LICENSE_HEADER_END@
  */
 
 #include <mach-o/loader.h>
 #include <Availability.h>
 
-#define NO_ULEB 
+#include "CodeSigningTypes.h"
+#include <CommonCrypto/CommonHMAC.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <CommonCrypto/CommonDigestSPI.h>
+
+#define NO_ULEB
 #include "Architectures.hpp"
 #include "MachOFileAbstraction.hpp"
 #include "CacheFileAbstraction.hpp"
@@ -46,6 +51,8 @@
 #include "dsc_iterator.h"
 #include "dsc_extractor.h"
 #include "MachOTrie.hpp"
+#include "SupportedArchs.h"
+#include "DyldSharedCache.h"
 
 #include <vector>
 #include <set>
 
 struct seg_info
 {
-                               seg_info(const char* n, uint64_t o, uint64_t s) 
-                                       : segName(n), offset(o), sizem(s) { }
-       const char* segName;
-       uint64_t        offset;
-       uint64_t        sizem;
+    seg_info(const char* n, uint64_t o, uint64_t s)
+    : segName(n), offset(o), sizem(s) { }
+    const char* segName;
+    uint64_t    offset;
+    uint64_t    sizem;
 };
 
 class CStringHash {
 public:
-       size_t operator()(const char* __s) const {
-               size_t __h = 0;
-               for ( ; *__s; ++__s)
-                       __h = 5 * __h + *__s;
-               return __h;
-       };
+    size_t operator()(const char* __s) const {
+        size_t __h = 0;
+        for ( ; *__s; ++__s)
+            __h = 5 * __h + *__s;
+        return __h;
+    };
 };
 class CStringEquals {
 public:
-       bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
+    bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
 };
 typedef std::unordered_map<const char*, std::vector<seg_info>, CStringHash, CStringEquals> NameToSegments;
 
 // Filter to find individual symbol re-exports in trie
 class NotReExportSymbol {
 public:
-       NotReExportSymbol(const std::set<int> &rd) :_reexportDeps(rd) {}
-       bool operator()(const mach_o::trie::Entry &entry) const {
-               bool result = isSymbolReExport(entry);
-               if (result) {
-                       // <rdar://problem/17671438> Xcode 6 leaks in dyld_shared_cache_extract_dylibs
-                       ::free((void*)entry.name);
-                       const_cast<mach_o::trie::Entry*>(&entry)->name = NULL;
-               }
-               return result;
-       }
+    NotReExportSymbol(const std::set<int> &rd) :_reexportDeps(rd) {}
+    bool operator()(const mach_o::trie::Entry &entry) const {
+        bool result = isSymbolReExport(entry);
+        if (result) {
+            // <rdar://problem/17671438> Xcode 6 leaks in dyld_shared_cache_extract_dylibs
+            ::free((void*)entry.name);
+            const_cast<mach_o::trie::Entry*>(&entry)->name = NULL;
+        }
+        return result;
+    }
 private:
-       bool isSymbolReExport(const mach_o::trie::Entry &entry) const {
-               if ( (entry.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) != EXPORT_SYMBOL_FLAGS_KIND_REGULAR )
-                       return true;
-               if ( (entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) == 0 )
-                       return true;
-               // If the symbol comes from a dylib that is re-exported, this is not an individual symbol re-export
-               if ( _reexportDeps.count((int)entry.other) != 0 )
-                       return true;
-               return false;
-       }
-       const std::set<int> &_reexportDeps;
+    bool isSymbolReExport(const mach_o::trie::Entry &entry) const {
+        if ( (entry.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) != EXPORT_SYMBOL_FLAGS_KIND_REGULAR )
+            return true;
+        if ( (entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) == 0 )
+            return true;
+        // If the symbol comes from a dylib that is re-exported, this is not an individual symbol re-export
+        if ( _reexportDeps.count((int)entry.other) != 0 )
+            return true;
+        return false;
+    }
+    const std::set<int> &_reexportDeps;
 };
 
+template <typename P>
+struct LoadCommandInfo {
+};
 
 template <typename A>
-int optimize_linkedit(macho_header<typename A::P>* mh, uint64_t textOffsetInCache, const void* mapped_cache, uint64_t* newSize) 
-{
-       typedef typename A::P P;
-       typedef typename A::P::E E;
+class LinkeditOptimizer {
+    typedef typename A::P P;
+    typedef typename A::P::E E;
     typedef typename A::P::uint_t pint_t;
 
-       // update header flags
-       mh->set_flags(mh->flags() & 0x7FFFFFFF); // remove in-cache bit
-       
-       // update load commands
-       uint64_t cumulativeFileSize = 0;
-       const unsigned origLoadCommandsSize = mh->sizeofcmds();
-       unsigned bytesRemaining = origLoadCommandsSize;
-       unsigned removedCount = 0;
-       const macho_load_command<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;
-       macho_segment_command<P>* linkEditSegCmd = NULL;
-       macho_symtab_command<P>* symtab = NULL;
-       macho_dysymtab_command<P>*      dynamicSymTab = NULL;
-       macho_linkedit_data_command<P>* functionStarts = NULL;
-       macho_linkedit_data_command<P>* dataInCode = NULL;
-       uint32_t exportsTrieOffset = 0;
-       uint32_t exportsTrieSize = 0;
-       std::set<int> reexportDeps;
-       int depIndex = 0;
-       for (uint32_t i = 0; i < cmdCount; ++i) {
-           bool remove = false;
-               switch ( cmd->cmd() ) {
-               case macho_segment_command<P>::CMD:
-                       {
-                       // update segment/section file offsets
-                       macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
-                       segCmd->set_fileoff(cumulativeFileSize);
-                       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) {
-                               if ( sect->offset() != 0 )
-                                       sect->set_offset((uint32_t)(cumulativeFileSize+sect->addr()-segCmd->vmaddr()));
-                       }
-                       if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
-                               linkEditSegCmd = segCmd;
-                       }
-                       cumulativeFileSize += segCmd->filesize();
-                       }
-                       break;
-               case LC_DYLD_INFO_ONLY:
-                       {
-                       // zero out all dyld info
-                       macho_dyld_info_command<P>* dyldInfo = (macho_dyld_info_command<P>*)cmd;
-                       exportsTrieOffset = dyldInfo->export_off();
-                       exportsTrieSize = dyldInfo->export_size();
-                       dyldInfo->set_rebase_off(0);
-                       dyldInfo->set_rebase_size(0);
-                       dyldInfo->set_bind_off(0);
-                       dyldInfo->set_bind_size(0);
-                       dyldInfo->set_weak_bind_off(0);
-                       dyldInfo->set_weak_bind_size(0);
-                       dyldInfo->set_lazy_bind_off(0);
-                       dyldInfo->set_lazy_bind_size(0);
-                       dyldInfo->set_export_off(0);
-                       dyldInfo->set_export_size(0);
-                       }
-                       break;
-               case LC_SYMTAB:
-                       symtab = (macho_symtab_command<P>*)cmd;
-                       break;
-               case LC_DYSYMTAB:
-                       dynamicSymTab = (macho_dysymtab_command<P>*)cmd;
-                       break;
-               case LC_FUNCTION_STARTS:
-                       functionStarts = (macho_linkedit_data_command<P>*)cmd;
-                       break;
-               case LC_DATA_IN_CODE:
-                       dataInCode = (macho_linkedit_data_command<P>*)cmd;
-                       break;
-               case LC_LOAD_DYLIB:
-               case LC_LOAD_WEAK_DYLIB:
-               case LC_REEXPORT_DYLIB:
-               case LC_LOAD_UPWARD_DYLIB:
-                       ++depIndex;
-                       if ( cmd->cmd() == LC_REEXPORT_DYLIB ) {
-                               reexportDeps.insert(depIndex);
-                       }
-                       break;
-               case LC_SEGMENT_SPLIT_INFO:
-                       // <rdar://problem/23212513> dylibs iOS 9 dyld caches have bogus 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);
-
-       // rebuild symbol table
-       if ( linkEditSegCmd == NULL ) {
-               fprintf(stderr, "__LINKEDIT not found\n");
-               return -1;
-       }
-       if ( symtab == NULL ) {
-               fprintf(stderr, "LC_SYMTAB not found\n");
-               return -1;
-       }
-       if ( dynamicSymTab == NULL ) {
-               fprintf(stderr, "LC_DYSYMTAB not found\n");
-               return -1;
-       }
-
-       const uint64_t newFunctionStartsOffset = linkEditSegCmd->fileoff();
-       uint32_t functionStartsSize = 0;
-       if ( functionStarts != NULL ) {
-               // copy function starts from original cache file to new mapped dylib file
-               functionStartsSize = functionStarts->datasize();
-               memcpy((char*)mh + newFunctionStartsOffset, (char*)mapped_cache + functionStarts->dataoff(), functionStartsSize);
-       }
-       const uint64_t newDataInCodeOffset = (newFunctionStartsOffset + functionStartsSize + sizeof(pint_t) - 1) & (-sizeof(pint_t)); // pointer align
-       uint32_t dataInCodeSize = 0;
-       if ( dataInCode != NULL ) {
-               // copy data-in-code info from original cache file to new mapped dylib file
-               dataInCodeSize = dataInCode->datasize();
-               memcpy((char*)mh + newDataInCodeOffset, (char*)mapped_cache + dataInCode->dataoff(), dataInCodeSize);
-       }
-
-       std::vector<mach_o::trie::Entry> exports;
-       if ( exportsTrieSize != 0 ) {
-               const uint8_t* exportsStart = ((uint8_t*)mapped_cache) + exportsTrieOffset; 
-               const uint8_t* exportsEnd = &exportsStart[exportsTrieSize];
-               mach_o::trie::parseTrie(exportsStart, exportsEnd, exports);
-               exports.erase(std::remove_if(exports.begin(), exports.end(), NotReExportSymbol(reexportDeps)), exports.end());
-       }
-
-       // look for local symbol info in unmapped part of shared cache
-       dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)mapped_cache;
-       macho_nlist<P>* localNlists = NULL;
-       uint32_t localNlistCount = 0;
-       const char* localStrings = NULL;
-       const char* localStringsEnd = NULL;
-       if ( header->mappingOffset() > offsetof(dyld_cache_header,localSymbolsSize) ) {
-               dyldCacheLocalSymbolsInfo<E>* localInfo = (dyldCacheLocalSymbolsInfo<E>*)(((uint8_t*)mapped_cache) + header->localSymbolsOffset());
-               dyldCacheLocalSymbolEntry<E>* entries = (dyldCacheLocalSymbolEntry<E>*)(((uint8_t*)mapped_cache) + header->localSymbolsOffset() + localInfo->entriesOffset());
-               macho_nlist<P>* allLocalNlists = (macho_nlist<P>*)(((uint8_t*)localInfo) + localInfo->nlistOffset());
-               const uint32_t entriesCount = localInfo->entriesCount();
-               for (uint32_t i=0; i < entriesCount; ++i) {
-                       if ( entries[i].dylibOffset() == textOffsetInCache ) {
-                               uint32_t localNlistStart = entries[i].nlistStartIndex();
-                               localNlistCount = entries[i].nlistCount();
-                               localNlists = &allLocalNlists[localNlistStart];
-                               localStrings = ((char*)localInfo) + localInfo->stringsOffset();
-                               localStringsEnd = &localStrings[localInfo->stringsSize()];
-                               break;
-                       }
-               }
-       }
-       // compute number of symbols in new symbol table
-       const macho_nlist<P>* const mergedSymTabStart = (macho_nlist<P>*)(((uint8_t*)mapped_cache) + symtab->symoff());
-       const macho_nlist<P>* const mergedSymTabend = &mergedSymTabStart[symtab->nsyms()];
-       uint32_t newSymCount = symtab->nsyms();
-       if ( localNlists != NULL ) {
-               newSymCount = localNlistCount;
-               for (const macho_nlist<P>* s = mergedSymTabStart; s != mergedSymTabend; ++s) {
-                       // skip any locals in cache
-                       if ( (s->n_type() & (N_TYPE|N_EXT)) == N_SECT ) 
-                               continue;
-                       ++newSymCount;
-               }
-       }
-       
-       // add room for N_INDR symbols for re-exported symbols
-       newSymCount += exports.size();
-
-       // copy symbol entries and strings from original cache file to new mapped dylib file
-       const uint64_t newSymTabOffset = (newDataInCodeOffset + dataInCodeSize + sizeof(pint_t) - 1) & (-sizeof(pint_t)); // pointer align
-       const uint64_t newIndSymTabOffset = newSymTabOffset + newSymCount*sizeof(macho_nlist<P>);
-       const uint64_t newStringPoolOffset = newIndSymTabOffset + dynamicSymTab->nindirectsyms()*sizeof(uint32_t);
-       macho_nlist<P>* const newSymTabStart = (macho_nlist<P>*)(((uint8_t*)mh) + newSymTabOffset);
-       char* const newStringPoolStart = (char*)mh + newStringPoolOffset;
-       const uint32_t* mergedIndSymTab = (uint32_t*)((char*)mapped_cache + dynamicSymTab->indirectsymoff());
-       const char* mergedStringPoolStart = (char*)mapped_cache + symtab->stroff();
-       const char* mergedStringPoolEnd = &mergedStringPoolStart[symtab->strsize()];
-       macho_nlist<P>* t = newSymTabStart;
-       int poolOffset = 0;
-       uint32_t symbolsCopied = 0;
-       newStringPoolStart[poolOffset++] = '\0'; // first pool entry is always empty string
-       for (const macho_nlist<P>* s = mergedSymTabStart; s != mergedSymTabend; ++s) {
-               // if we have better local symbol info, skip any locals here
-               if ( (localNlists != NULL) && ((s->n_type() & (N_TYPE|N_EXT)) == N_SECT) ) 
-                       continue;
-               *t = *s;
-               t->set_n_strx(poolOffset);
-               const char* symName = &mergedStringPoolStart[s->n_strx()];
-               if ( symName > mergedStringPoolEnd )
-                       symName = "<corrupt symbol name>";
-               strcpy(&newStringPoolStart[poolOffset], symName);
-               poolOffset += (strlen(symName) + 1);
-               ++t;
-               ++symbolsCopied;
-       }
-       // <rdar://problem/16529213> recreate N_INDR symbols in extracted dylibs for debugger
-       for (std::vector<mach_o::trie::Entry>::iterator it = exports.begin(); it != exports.end(); ++it) {
-               strcpy(&newStringPoolStart[poolOffset], it->name);
-               t->set_n_strx(poolOffset);
-               poolOffset += (strlen(it->name) + 1);
-               t->set_n_type(N_INDR | N_EXT);
-               t->set_n_sect(0);
-               t->set_n_desc(0);
-               const char* importName = it->importName;
-               if ( *importName == '\0' )
-                       importName = it->name;
-               strcpy(&newStringPoolStart[poolOffset], importName);
-               t->set_n_value(poolOffset);
-               poolOffset += (strlen(importName) + 1);
-               ++t;
-               ++symbolsCopied;
-       }
-       if ( localNlists != NULL ) {
-               // update load command to reflect new count of locals
-               dynamicSymTab->set_ilocalsym(symbolsCopied);
-               dynamicSymTab->set_nlocalsym(localNlistCount);
-               // copy local symbols
-               for (uint32_t i=0; i < localNlistCount; ++i) {
-                       const char* localName = &localStrings[localNlists[i].n_strx()];
-                       if ( localName > localStringsEnd )
-                               localName = "<corrupt local symbol name>";
-                       *t = localNlists[i];
-                       t->set_n_strx(poolOffset);
-                       strcpy(&newStringPoolStart[poolOffset], localName);
-                       poolOffset += (strlen(localName) + 1);
-                       ++t;
-                       ++symbolsCopied;
-               }
-       }
-       
-       if ( newSymCount != symbolsCopied ) {
-               fprintf(stderr, "symbol count miscalculation\n");
-               return -1;
-       }
-       
-       // pointer align string pool size
-       while ( (poolOffset % sizeof(pint_t)) != 0 )
-               ++poolOffset; 
-       // copy indirect symbol table
-       uint32_t* newIndSymTab = (uint32_t*)((char*)mh + newIndSymTabOffset);
-       memcpy(newIndSymTab, mergedIndSymTab, dynamicSymTab->nindirectsyms()*sizeof(uint32_t));
-       
-       // update load commands
-       if ( functionStarts != NULL ) {
-               functionStarts->set_dataoff((uint32_t)newFunctionStartsOffset);
-               functionStarts->set_datasize(functionStartsSize);
-       }
-       if ( dataInCode != NULL ) {
-               dataInCode->set_dataoff((uint32_t)newDataInCodeOffset);
-               dataInCode->set_datasize(dataInCodeSize);
-       }
-       symtab->set_nsyms(symbolsCopied);
-       symtab->set_symoff((uint32_t)newSymTabOffset);
-       symtab->set_stroff((uint32_t)newStringPoolOffset);
-       symtab->set_strsize(poolOffset);
-       dynamicSymTab->set_extreloff(0);
-       dynamicSymTab->set_nextrel(0);
-       dynamicSymTab->set_locreloff(0);
-       dynamicSymTab->set_nlocrel(0);
-       dynamicSymTab->set_indirectsymoff((uint32_t)newIndSymTabOffset);
-       linkEditSegCmd->set_filesize(symtab->stroff()+symtab->strsize() - linkEditSegCmd->fileoff());
-       linkEditSegCmd->set_vmsize( (linkEditSegCmd->filesize()+4095) & (-4096) );
-       
-       // return new size
-       *newSize = (symtab->stroff()+symtab->strsize()+4095) & (-4096);
-       
-       // <rdar://problem/17671438> Xcode 6 leaks in dyld_shared_cache_extract_dylibs
-       for (std::vector<mach_o::trie::Entry>::iterator it = exports.begin(); it != exports.end(); ++it) {
-               ::free((void*)(it->name));
-       }
-       
-       
-       return 0;
-}
+private:
+    macho_segment_command<P>* linkEditSegCmd = NULL;
+    macho_symtab_command<P>* symtab = NULL;
+    macho_dysymtab_command<P>*    dynamicSymTab = NULL;
+    macho_linkedit_data_command<P>*    functionStarts = NULL;
+    macho_linkedit_data_command<P>*    dataInCode = NULL;
+    uint32_t exportsTrieOffset = 0;
+    uint32_t exportsTrieSize = 0;
+    std::set<int> reexportDeps;
+
+public:
+
+    void optimize_loadcommands(macho_header<typename A::P>* mh)
+    {
+        typedef typename A::P P;
+        typedef typename A::P::E E;
+        typedef typename A::P::uint_t pint_t;
+
+        // update header flags
+        mh->set_flags(mh->flags() & 0x7FFFFFFF); // remove in-cache bit
+
+        // update load commands
+        uint64_t cumulativeFileSize = 0;
+        const unsigned origLoadCommandsSize = mh->sizeofcmds();
+        unsigned bytesRemaining = origLoadCommandsSize;
+        unsigned removedCount = 0;
+        const macho_load_command<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;
+        int depIndex = 0;
+        for (uint32_t i = 0; i < cmdCount; ++i) {
+            bool remove = false;
+            switch ( cmd->cmd() ) {
+                case macho_segment_command<P>::CMD:
+                {
+                    // update segment/section file offsets
+                    macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
+                    segCmd->set_fileoff(cumulativeFileSize);
+                    segCmd->set_filesize(segCmd->vmsize());
+                    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) {
+                        if ( sect->offset() != 0 )
+                            sect->set_offset((uint32_t)(cumulativeFileSize+sect->addr()-segCmd->vmaddr()));
+                    }
+                    if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
+                        linkEditSegCmd = segCmd;
+                    }
+                    cumulativeFileSize += segCmd->filesize();
+                    break;
+                }
+                case LC_DYLD_INFO_ONLY:
+                {
+                    // zero out all dyld info
+                    macho_dyld_info_command<P>* dyldInfo = (macho_dyld_info_command<P>*)cmd;
+                    exportsTrieOffset = dyldInfo->export_off();
+                    exportsTrieSize = dyldInfo->export_size();
+                    dyldInfo->set_rebase_off(0);
+                    dyldInfo->set_rebase_size(0);
+                    dyldInfo->set_bind_off(0);
+                    dyldInfo->set_bind_size(0);
+                    dyldInfo->set_weak_bind_off(0);
+                    dyldInfo->set_weak_bind_size(0);
+                    dyldInfo->set_lazy_bind_off(0);
+                    dyldInfo->set_lazy_bind_size(0);
+                    dyldInfo->set_export_off(0);
+                    dyldInfo->set_export_size(0);
+                }
+                    break;
+                case LC_SYMTAB:
+                    symtab = (macho_symtab_command<P>*)cmd;
+                    break;
+                case LC_DYSYMTAB:
+                    dynamicSymTab = (macho_dysymtab_command<P>*)cmd;
+                    break;
+                case LC_FUNCTION_STARTS:
+                    functionStarts = (macho_linkedit_data_command<P>*)cmd;
+                    break;
+                case LC_DATA_IN_CODE:
+                    dataInCode = (macho_linkedit_data_command<P>*)cmd;
+                    break;
+                case LC_LOAD_DYLIB:
+                case LC_LOAD_WEAK_DYLIB:
+                case LC_REEXPORT_DYLIB:
+                case LC_LOAD_UPWARD_DYLIB:
+                    ++depIndex;
+                    if ( cmd->cmd() == LC_REEXPORT_DYLIB ) {
+                        reexportDeps.insert(depIndex);
+                    }
+                    break;
+                case LC_SEGMENT_SPLIT_INFO:
+                    // <rdar://problem/23212513> dylibs iOS 9 dyld caches have bogus 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);
+    }
 
+    int optimize_linkedit(std::vector<uint8_t> &new_linkedit_data, uint64_t textOffsetInCache, const void* mapped_cache)
+    {
+        typedef typename A::P P;
+        typedef typename A::P::E E;
+        typedef typename A::P::uint_t pint_t;
+
+        // rebuild symbol table
+        if ( linkEditSegCmd == NULL ) {
+            fprintf(stderr, "__LINKEDIT not found\n");
+            return -1;
+        }
+        if ( symtab == NULL ) {
+            fprintf(stderr, "LC_SYMTAB not found\n");
+            return -1;
+        }
+        if ( dynamicSymTab == NULL ) {
+            fprintf(stderr, "LC_DYSYMTAB not found\n");
+            return -1;
+        }
+
+        const uint64_t newFunctionStartsOffset = new_linkedit_data.size();
+        uint32_t functionStartsSize = 0;
+        if ( functionStarts != NULL ) {
+            // copy function starts from original cache file to new mapped dylib file
+            functionStartsSize = functionStarts->datasize();
+            new_linkedit_data.insert(new_linkedit_data.end(),
+                                     (char*)mapped_cache + functionStarts->dataoff(),
+                                     (char*)mapped_cache + functionStarts->dataoff() + functionStartsSize);
+        }
+
+        // pointer align
+        while ((linkEditSegCmd->fileoff() + new_linkedit_data.size()) % sizeof(pint_t))
+            new_linkedit_data.push_back(0);
+
+        const uint64_t newDataInCodeOffset = new_linkedit_data.size();
+        uint32_t dataInCodeSize = 0;
+        if ( dataInCode != NULL ) {
+            // copy data-in-code info from original cache file to new mapped dylib file
+            dataInCodeSize = dataInCode->datasize();
+            new_linkedit_data.insert(new_linkedit_data.end(),
+                                     (char*)mapped_cache + dataInCode->dataoff(),
+                                     (char*)mapped_cache + dataInCode->dataoff() + dataInCodeSize);
+        }
+
+        std::vector<mach_o::trie::Entry> exports;
+        if ( exportsTrieSize != 0 ) {
+            const uint8_t* exportsStart = ((uint8_t*)mapped_cache) + exportsTrieOffset;
+            const uint8_t* exportsEnd = &exportsStart[exportsTrieSize];
+            mach_o::trie::parseTrie(exportsStart, exportsEnd, exports);
+            exports.erase(std::remove_if(exports.begin(), exports.end(), NotReExportSymbol(reexportDeps)), exports.end());
+        }
+
+        // look for local symbol info in unmapped part of shared cache
+        dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)mapped_cache;
+        macho_nlist<P>* localNlists = NULL;
+        uint32_t localNlistCount = 0;
+        const char* localStrings = NULL;
+        const char* localStringsEnd = NULL;
+        if ( header->mappingOffset() > offsetof(dyld_cache_header,localSymbolsSize) ) {
+            dyldCacheLocalSymbolsInfo<E>* localInfo = (dyldCacheLocalSymbolsInfo<E>*)(((uint8_t*)mapped_cache) + header->localSymbolsOffset());
+            dyldCacheLocalSymbolEntry<E>* entries = (dyldCacheLocalSymbolEntry<E>*)(((uint8_t*)mapped_cache) + header->localSymbolsOffset() + localInfo->entriesOffset());
+            macho_nlist<P>* allLocalNlists = (macho_nlist<P>*)(((uint8_t*)localInfo) + localInfo->nlistOffset());
+            const uint32_t entriesCount = localInfo->entriesCount();
+            for (uint32_t i=0; i < entriesCount; ++i) {
+                if ( entries[i].dylibOffset() == textOffsetInCache ) {
+                    uint32_t localNlistStart = entries[i].nlistStartIndex();
+                    localNlistCount = entries[i].nlistCount();
+                    localNlists = &allLocalNlists[localNlistStart];
+                    localStrings = ((char*)localInfo) + localInfo->stringsOffset();
+                    localStringsEnd = &localStrings[localInfo->stringsSize()];
+                    break;
+                }
+            }
+        }
+        // compute number of symbols in new symbol table
+        const macho_nlist<P>* const mergedSymTabStart = (macho_nlist<P>*)(((uint8_t*)mapped_cache) + symtab->symoff());
+        const macho_nlist<P>* const mergedSymTabend = &mergedSymTabStart[symtab->nsyms()];
+        uint32_t newSymCount = symtab->nsyms();
+        if ( localNlists != NULL ) {
+            newSymCount = localNlistCount;
+            for (const macho_nlist<P>* s = mergedSymTabStart; s != mergedSymTabend; ++s) {
+                // skip any locals in cache
+                if ( (s->n_type() & (N_TYPE|N_EXT)) == N_SECT )
+                    continue;
+                ++newSymCount;
+            }
+        }
+
+        // add room for N_INDR symbols for re-exported symbols
+        newSymCount += exports.size();
+
+        // copy symbol entries and strings from original cache file to new mapped dylib file
+        const char* mergedStringPoolStart = (char*)mapped_cache + symtab->stroff();
+        const char* mergedStringPoolEnd = &mergedStringPoolStart[symtab->strsize()];
+
+        // First count how many entries we need
+        std::vector<macho_nlist<P>> newSymTab;
+        newSymTab.reserve(newSymCount);
+        std::vector<char> newSymNames;
+
+        // first pool entry is always empty string
+        newSymNames.push_back('\0');
+
+        for (const macho_nlist<P>* s = mergedSymTabStart; s != mergedSymTabend; ++s) {
+            // if we have better local symbol info, skip any locals here
+            if ( (localNlists != NULL) && ((s->n_type() & (N_TYPE|N_EXT)) == N_SECT) )
+                continue;
+            macho_nlist<P> t = *s;
+            t.set_n_strx((uint32_t)newSymNames.size());
+            const char* symName = &mergedStringPoolStart[s->n_strx()];
+            if ( symName > mergedStringPoolEnd )
+                symName = "<corrupt symbol name>";
+            newSymNames.insert(newSymNames.end(),
+                               symName,
+                               symName + (strlen(symName) + 1));
+            newSymTab.push_back(t);
+        }
+        // <rdar://problem/16529213> recreate N_INDR symbols in extracted dylibs for debugger
+        for (std::vector<mach_o::trie::Entry>::iterator it = exports.begin(); it != exports.end(); ++it) {
+            macho_nlist<P> t;
+            memset(&t, 0, sizeof(t));
+            t.set_n_strx((uint32_t)newSymNames.size());
+            t.set_n_type(N_INDR | N_EXT);
+            t.set_n_sect(0);
+            t.set_n_desc(0);
+            newSymNames.insert(newSymNames.end(),
+                               it->name,
+                               it->name + (strlen(it->name) + 1));
+            const char* importName = it->importName;
+            if ( *importName == '\0' )
+                importName = it->name;
+            t.set_n_value(newSymNames.size());
+            newSymNames.insert(newSymNames.end(),
+                               importName,
+                               importName + (strlen(importName) + 1));
+            newSymTab.push_back(t);
+        }
+        if ( localNlists != NULL ) {
+            // update load command to reflect new count of locals
+            dynamicSymTab->set_ilocalsym((uint32_t)newSymTab.size());
+            dynamicSymTab->set_nlocalsym(localNlistCount);
+            // copy local symbols
+            for (uint32_t i=0; i < localNlistCount; ++i) {
+                const char* localName = &localStrings[localNlists[i].n_strx()];
+                if ( localName > localStringsEnd )
+                    localName = "<corrupt local symbol name>";
+                macho_nlist<P> t = localNlists[i];
+                t.set_n_strx((uint32_t)newSymNames.size());
+                newSymNames.insert(newSymNames.end(),
+                                   localName,
+                                   localName + (strlen(localName) + 1));
+                newSymTab.push_back(t);
+            }
+        }
+
+        if ( newSymCount != newSymTab.size() ) {
+            fprintf(stderr, "symbol count miscalculation\n");
+            return -1;
+        }
+
+        //const uint64_t newStringPoolOffset = newIndSymTabOffset + dynamicSymTab->nindirectsyms()*sizeof(uint32_t);
+        //macho_nlist<P>* const newSymTabStart = (macho_nlist<P>*)(((uint8_t*)mh) + newSymTabOffset);
+        //char* const newStringPoolStart = (char*)mh + newStringPoolOffset;
+
+        // pointer align
+        while ((linkEditSegCmd->fileoff() + new_linkedit_data.size()) % sizeof(pint_t))
+            new_linkedit_data.push_back(0);
+
+        const uint64_t newSymTabOffset = new_linkedit_data.size();
+
+        // Copy sym tab
+        for (macho_nlist<P>& sym : newSymTab) {
+            uint8_t symData[sizeof(macho_nlist<P>)];
+            memcpy(&symData, &sym, sizeof(sym));
+            new_linkedit_data.insert(new_linkedit_data.end(), &symData[0], &symData[sizeof(macho_nlist<P>)]);
+        }
+
+        const uint64_t newIndSymTabOffset = new_linkedit_data.size();
+
+        // Copy indirect symbol table
+        const uint32_t* mergedIndSymTab = (uint32_t*)((char*)mapped_cache + dynamicSymTab->indirectsymoff());
+        new_linkedit_data.insert(new_linkedit_data.end(),
+                                 (char*)mergedIndSymTab,
+                                 (char*)(mergedIndSymTab + dynamicSymTab->nindirectsyms()));
+
+        const uint64_t newStringPoolOffset = new_linkedit_data.size();
+
+        // pointer align string pool size
+        while (newSymNames.size() % sizeof(pint_t))
+            newSymNames.push_back('\0');
+
+        new_linkedit_data.insert(new_linkedit_data.end(), newSymNames.begin(), newSymNames.end());
+
+        // update load commands
+        if ( functionStarts != NULL ) {
+            functionStarts->set_dataoff((uint32_t)(newFunctionStartsOffset + linkEditSegCmd->fileoff()));
+            functionStarts->set_datasize(functionStartsSize);
+        }
+        if ( dataInCode != NULL ) {
+            dataInCode->set_dataoff((uint32_t)(newDataInCodeOffset + linkEditSegCmd->fileoff()));
+            dataInCode->set_datasize(dataInCodeSize);
+        }
+
+        symtab->set_nsyms(newSymCount);
+        symtab->set_symoff((uint32_t)(newSymTabOffset + linkEditSegCmd->fileoff()));
+        symtab->set_stroff((uint32_t)(newStringPoolOffset + linkEditSegCmd->fileoff()));
+        symtab->set_strsize((uint32_t)newSymNames.size());
+        dynamicSymTab->set_extreloff(0);
+        dynamicSymTab->set_nextrel(0);
+        dynamicSymTab->set_locreloff(0);
+        dynamicSymTab->set_nlocrel(0);
+        dynamicSymTab->set_indirectsymoff((uint32_t)(newIndSymTabOffset + linkEditSegCmd->fileoff()));
+        linkEditSegCmd->set_filesize(symtab->stroff()+symtab->strsize() - linkEditSegCmd->fileoff());
+        linkEditSegCmd->set_vmsize( (linkEditSegCmd->filesize()+4095) & (-4096) );
+
+        // <rdar://problem/17671438> Xcode 6 leaks in dyld_shared_cache_extract_dylibs
+        for (std::vector<mach_o::trie::Entry>::iterator it = exports.begin(); it != exports.end(); ++it) {
+            ::free((void*)(it->name));
+        }
+
+
+        return 0;
+    }
 
+};
 
-static void make_dirs(const char* file_path) 
+static void make_dirs(const char* file_path)
 {
-       //printf("make_dirs(%s)\n", file_path);
-       char dirs[strlen(file_path)+1];
-       strcpy(dirs, file_path);
-       char* lastSlash = strrchr(dirs, '/');
-       if ( lastSlash == NULL )
-               return;
-       lastSlash[1] = '\0';
-       struct stat stat_buf;
-       if ( stat(dirs, &stat_buf) != 0 ) {
-               char* afterSlash = &dirs[1];
-               char* slash;
-               while ( (slash = strchr(afterSlash, '/')) != NULL ) {
-                       *slash = '\0';
-                       ::mkdir(dirs, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
-                       //printf("mkdir(%s)\n", dirs);
-                       *slash = '/';
-                       afterSlash = slash+1;
-               }
-       }
+    //printf("make_dirs(%s)\n", file_path);
+    char dirs[strlen(file_path)+1];
+    strcpy(dirs, file_path);
+    char* lastSlash = strrchr(dirs, '/');
+    if ( lastSlash == NULL )
+        return;
+    lastSlash[1] = '\0';
+    struct stat stat_buf;
+    if ( stat(dirs, &stat_buf) != 0 ) {
+        char* afterSlash = &dirs[1];
+        char* slash;
+        while ( (slash = strchr(afterSlash, '/')) != NULL ) {
+            *slash = '\0';
+            ::mkdir(dirs, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
+            //printf("mkdir(%s)\n", dirs);
+            *slash = '/';
+            afterSlash = slash+1;
+        }
+    }
 }
 
 
 
 template <typename A>
-size_t dylib_maker(const void* mapped_cache, std::vector<uint8_t> &dylib_data, const std::vector<seg_info>& segments) {                
-       typedef typename A::P P;
-    
-    size_t  additionalSize  = 0;
-       for(std::vector<seg_info>::const_iterator it=segments.begin(); it != segments.end(); ++it) {
-               additionalSize                          += it->sizem;
-       }
-    
-    dylib_data.reserve(dylib_data.size() + additionalSize);
-    
-    uint32_t                nfat_archs          = 0;
-       uint32_t                offsetInFatFile     = 4096;
+size_t dylib_maker(const void* mapped_cache, std::vector<uint8_t> &dylib_data, const std::vector<seg_info>& segments) {
+    typedef typename A::P P;
+
+    int32_t                nfat_archs          = 0;
+    uint32_t                offsetInFatFile     = 4096;
     uint8_t                 *base_ptr           = &dylib_data.front();
-           
+
 #define FH reinterpret_cast<fat_header*>(base_ptr)
 #define FA reinterpret_cast<fat_arch*>(base_ptr + (8 + (nfat_archs - 1) * sizeof(fat_arch)))
-    
+
     if(dylib_data.size() >= 4096 && OSSwapBigToHostInt32(FH->magic) == FAT_MAGIC) {
-               // have fat header, append new arch to end
+        // have fat header, append new arch to end
         nfat_archs                              = OSSwapBigToHostInt32(FH->nfat_arch);
-               offsetInFatFile                         = OSSwapBigToHostInt32(FA->offset) + OSSwapBigToHostInt32(FA->size);
+        offsetInFatFile                         = OSSwapBigToHostInt32(FA->offset) + OSSwapBigToHostInt32(FA->size);
     }
-    
-    dylib_data.resize(offsetInFatFile);
-    base_ptr                                    = &dylib_data.front();
-    
-    FH->magic                                   = OSSwapHostToBigInt32(FAT_MAGIC);
+
+    // First see if this slice already exists.
+    for(std::vector<seg_info>::const_iterator it=segments.begin(); it != segments.end(); ++it) {
+        if(strcmp(it->segName, "__TEXT") == 0 ) {
+            const macho_header<P> *textMH = reinterpret_cast<macho_header<P>*>((uint8_t*)mapped_cache+it->offset);
+
+            // if this cputype/subtype already exist in fat header, then return immediately
+            for(int32_t i=0; i < nfat_archs; ++i) {
+                fat_arch *afa = reinterpret_cast<fat_arch*>(base_ptr+8)+i;
+                if (afa->cputype == (cpu_type_t)OSSwapHostToBigInt32(textMH->cputype()) && afa->cpusubtype == (cpu_type_t)OSSwapHostToBigInt32(textMH->cpusubtype())) {
+                    //fprintf(stderr, "arch already exists in fat dylib\n");
+                    return offsetInFatFile;
+                }
+            }
+        }
+    }
+
+    if (dylib_data.empty()) {
+        // Reserve space for the fat header.
+        dylib_data.resize(4096);
+        base_ptr = &dylib_data.front();
+        FH->magic = OSSwapHostToBigInt32(FAT_MAGIC);
+    }
+
     FH->nfat_arch                               = OSSwapHostToBigInt32(++nfat_archs);
-    
+
     FA->cputype                                 = 0; // filled in later
     FA->cpusubtype                              = 0; // filled in later
     FA->offset                                  = OSSwapHostToBigInt32(offsetInFatFile);
     FA->size                                    = 0; // filled in later
     FA->align                                   = OSSwapHostToBigInt32(12);
-    
-       // Write regular segments into the buffer
-       uint64_t                totalSize           = 0;
-    uint64_t                           textOffsetInCache       = 0;
-       for( std::vector<seg_info>::const_iterator it=segments.begin(); it != segments.end(); ++it) {
-        
+
+    size_t  additionalSize  = 0;
+    for(std::vector<seg_info>::const_iterator it=segments.begin(); it != segments.end(); ++it) {
+        if ( strcmp(it->segName, "__LINKEDIT") != 0 )
+            additionalSize += it->sizem;
+    }
+
+    std::vector<uint8_t> new_dylib_data;
+    new_dylib_data.reserve(additionalSize);
+
+    // Write regular segments into the buffer
+    uint64_t                textOffsetInCache    = 0;
+    for( std::vector<seg_info>::const_iterator it=segments.begin(); it != segments.end(); ++it) {
+
         if(strcmp(it->segName, "__TEXT") == 0 ) {
-                       textOffsetInCache                                       = it->offset;
+            textOffsetInCache                    = it->offset;
             const macho_header<P>   *textMH     = reinterpret_cast<macho_header<P>*>((uint8_t*)mapped_cache+textOffsetInCache);
-            FA->cputype                         = OSSwapHostToBigInt32(textMH->cputype()); 
+            FA->cputype                         = OSSwapHostToBigInt32(textMH->cputype());
             FA->cpusubtype                      = OSSwapHostToBigInt32(textMH->cpusubtype());
-            
-            // if this cputype/subtype already exist in fat header, then return immediately
-            for(uint32_t i=0; i < nfat_archs-1; ++i) {
-                fat_arch            *afa        = reinterpret_cast<fat_arch*>(base_ptr+8)+i;
-                
-                if(   afa->cputype == FA->cputype
-                   && afa->cpusubtype == FA->cpusubtype) {
-                    //fprintf(stderr, "arch already exists in fat dylib\n");
-                    dylib_data.resize(offsetInFatFile);
-                    return offsetInFatFile;
-                }
-            }
-               }
-        
-               //printf("segName=%s, offset=0x%llX, size=0x%0llX\n", it->segName, it->offset, it->sizem);
-        std::copy(((uint8_t*)mapped_cache)+it->offset, ((uint8_t*)mapped_cache)+it->offset+it->sizem, std::back_inserter(dylib_data));
-        base_ptr                                = &dylib_data.front();
-        totalSize                               += it->sizem;
-       }
-    
-       FA->size                                    = OSSwapHostToBigInt32(totalSize); 
-    
-       // optimize linkedit
-       uint64_t                newSize             = dylib_data.size();
-       optimize_linkedit<A>(((macho_header<P>*)(base_ptr+offsetInFatFile)), textOffsetInCache, mapped_cache, &newSize);
-       
-       // update fat header with new file size
-    dylib_data.resize((size_t)(offsetInFatFile+newSize));
-    base_ptr                                    = &dylib_data.front();
-       FA->size                                    = OSSwapHostToBigInt32(newSize);
+        }
+
+        //printf("segName=%s, offset=0x%llX, size=0x%0llX\n", it->segName, it->offset, it->sizem);
+        // Copy all but the __LINKEDIT.  It will be copied later during the optimizer in to a temporary buffer but it would
+        // not be efficient to copy it all now for each dylib.
+        if (strcmp(it->segName, "__LINKEDIT") == 0 )
+            continue;
+        std::copy(((uint8_t*)mapped_cache)+it->offset, ((uint8_t*)mapped_cache)+it->offset+it->sizem, std::back_inserter(new_dylib_data));
+    }
+
+    // optimize linkedit
+    std::vector<uint8_t> new_linkedit_data;
+    new_linkedit_data.reserve(1 << 20);
+
+    LinkeditOptimizer<A> linkeditOptimizer;
+    macho_header<P>* mh = (macho_header<P>*)&new_dylib_data.front();
+    linkeditOptimizer.optimize_loadcommands(mh);
+    linkeditOptimizer.optimize_linkedit(new_linkedit_data, textOffsetInCache, mapped_cache);
+
+    new_dylib_data.insert(new_dylib_data.end(), new_linkedit_data.begin(), new_linkedit_data.end());
+
+    // Page align file
+    while (new_dylib_data.size() % 4096)
+        new_dylib_data.push_back(0);
+
+    // update fat header with new file size
+    FA->size                                    = OSSwapHostToBigInt32(new_dylib_data.size());
 #undef FH
 #undef FA
-       return offsetInFatFile;
-} 
+    dylib_data.insert(dylib_data.end(), new_dylib_data.begin(), new_dylib_data.end());
+    return offsetInFatFile;
+}
+
+typedef __typeof(dylib_maker<x86>) dylib_maker_func;
+typedef void (^progress_block)(unsigned current, unsigned total);
+
+class SharedCacheExtractor;
+struct SharedCacheDylibExtractor {
+    SharedCacheDylibExtractor(const char* name, std::vector<seg_info> segInfo)
+        : name(name), segInfo(segInfo) { }
+
+    void extractCache(SharedCacheExtractor& context);
 
+    const char*                     name;
+    const std::vector<seg_info>     segInfo;
+    int                             result = 0;
+};
+
+struct SharedCacheExtractor {
+    SharedCacheExtractor(const NameToSegments& map,
+                         const char* extraction_root_path,
+                         dylib_maker_func* dylib_create_func,
+                         void* mapped_cache,
+                         progress_block progress)
+        : map(map), extraction_root_path(extraction_root_path),
+          dylib_create_func(dylib_create_func), mapped_cache(mapped_cache),
+          progress(progress) {
+
+      extractors.reserve(map.size());
+      for (const std::pair<const char*, std::vector<seg_info>>& it : map)
+          extractors.emplace_back(it.first, it.second);
+
+        // Limit the number of open files.  16 seems to give better performance than higher numbers.
+        sema = dispatch_semaphore_create(16);
+    }
+    int extractCaches();
+
+    static void extractCache(void *ctx, size_t i);
+
+    const NameToSegments&                   map;
+    std::vector<SharedCacheDylibExtractor>  extractors;
+    dispatch_semaphore_t                    sema;
+    const char*                             extraction_root_path;
+    dylib_maker_func*                       dylib_create_func;
+    void*                                   mapped_cache;
+    progress_block                          progress;
+    std::atomic_int                         count = { 0 };
+};
+
+int SharedCacheExtractor::extractCaches() {
+    dispatch_queue_t process_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
+    dispatch_apply_f(map.size(), process_queue,
+                     this, extractCache);
+
+    int result = 0;
+    for (const SharedCacheDylibExtractor& extractor : extractors) {
+        if (extractor.result != 0) {
+            result = extractor.result;
+            break;
+        }
+    }
+    return result;
+}
+
+void SharedCacheExtractor::extractCache(void *ctx, size_t i) {
+    SharedCacheExtractor& context = *(SharedCacheExtractor*)ctx;
+    dispatch_semaphore_wait(context.sema, DISPATCH_TIME_FOREVER);
+    context.extractors[i].extractCache(context);
+    dispatch_semaphore_signal(context.sema);
+}
+
+void SharedCacheDylibExtractor::extractCache(SharedCacheExtractor &context) {
+
+    char    dylib_path[PATH_MAX];
+    strcpy(dylib_path, context.extraction_root_path);
+    strcat(dylib_path, "/");
+    strcat(dylib_path, name);
+
+    //printf("%s with %lu segments\n", dylib_path, it->second.size());
+    // make sure all directories in this path exist
+    make_dirs(dylib_path);
+
+    // open file, create if does not already exist
+    int fd = ::open(dylib_path, O_CREAT | O_EXLOCK | O_RDWR, 0644);
+    if ( fd == -1 ) {
+        fprintf(stderr, "can't open or create dylib file %s, errnor=%d\n", dylib_path, errno);
+        result = -1;
+        return;
+    }
+
+    struct stat statbuf;
+    if (fstat(fd, &statbuf)) {
+        fprintf(stderr, "Error: stat failed for dyld file %s, errnor=%d\n", dylib_path, errno);
+        close(fd);
+        result = -1;
+        return;
+    }
+
+    std::vector<uint8_t> vec((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);
+        result = -1;
+        return;
+    }
+
+    const size_t offset = context.dylib_create_func(context.mapped_cache, vec, segInfo);
+    context.progress(context.count++, (unsigned)context.map.size());
+
+    if(offset != vec.size()) {
+        //Write out the first page, and everything after offset
+        if(   pwrite(fd, &vec.front(), 4096, 0) == -1
+           || pwrite(fd, &vec.front() + offset, vec.size() - offset, offset) == -1) {
+            fprintf(stderr, "error writing, errnor=%d\n", errno);
+            result = -1;
+        }
+    }
+
+    close(fd);
+}
+
+static int sharedCacheIsValid(const void* mapped_cache, uint64_t size) {
+    // First check that the size is good.
+    // Note the shared cache may not have a codeSignatureSize value set so we need to first make
+    // sure we have space for the CS_SuperBlob, then later crack that to check for the size of the rest.
+    const DyldSharedCache* dyldSharedCache = (DyldSharedCache*)mapped_cache;
+    uint64_t requiredSizeForCSSuperBlob = dyldSharedCache->header.codeSignatureOffset + sizeof(CS_SuperBlob);
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((uint8_t*)mapped_cache + dyldSharedCache->header.mappingOffset);
+    if ( requiredSizeForCSSuperBlob > size ) {
+        fprintf(stderr, "Error: dyld shared cache size 0x%08llx is less than required size of 0x%08llx.\n", size, requiredSizeForCSSuperBlob);
+        return -1;
+    }
+
+    // Now see if the code signatures are valid as that tells us the pages aren't corrupt.
+    // First find all of the regions of the shared cache we computed cd hashes
+    std::vector<std::pair<uint64_t, uint64_t>> sharedCacheRegions;
+    sharedCacheRegions.emplace_back(std::make_pair(mappings[0].fileOffset, mappings[0].fileOffset + mappings[0].size));
+    sharedCacheRegions.emplace_back(std::make_pair(mappings[1].fileOffset, mappings[1].fileOffset + mappings[1].size));
+    sharedCacheRegions.emplace_back(std::make_pair(mappings[2].fileOffset, mappings[2].fileOffset + mappings[2].size));
+    if (dyldSharedCache->header.localSymbolsSize)
+        sharedCacheRegions.emplace_back(std::make_pair(dyldSharedCache->header.localSymbolsOffset, dyldSharedCache->header.localSymbolsOffset + dyldSharedCache->header.localSymbolsSize));
+    size_t inBbufferSize = 0;
+    for (auto& sharedCacheRegion : sharedCacheRegions)
+        inBbufferSize += (sharedCacheRegion.second - sharedCacheRegion.first);
+    uint32_t slotCountFromRegions = (uint32_t)((inBbufferSize + CS_PAGE_SIZE - 1) / CS_PAGE_SIZE);
+
+    // Now take the cd hash from the cache itself and validate the regions we found.
+    uint8_t* codeSignatureRegion = (uint8_t*)mapped_cache + dyldSharedCache->header.codeSignatureOffset;
+    CS_SuperBlob* sb = reinterpret_cast<CS_SuperBlob*>(codeSignatureRegion);
+    if (sb->magic != htonl(CSMAGIC_EMBEDDED_SIGNATURE)) {
+        fprintf(stderr, "Error: dyld shared cache code signature magic is incorrect.\n");
+        return -1;
+    }
+
+    size_t sbSize = ntohl(sb->length);
+    uint64_t requiredSizeForCS = dyldSharedCache->header.codeSignatureOffset + sbSize;
+    if ( requiredSizeForCS > size ) {
+        fprintf(stderr, "Error: dyld shared cache size 0x%08llx is less than required size of 0x%08llx.\n", size, requiredSizeForCS);
+        return -1;
+    }
+
+    // Find the offset to the code directory.
+    CS_CodeDirectory* cd = nullptr;
+    for (unsigned i =0; i != sb->count; ++i) {
+        if (ntohl(sb->index[i].type) == CSSLOT_CODEDIRECTORY) {
+            cd = (CS_CodeDirectory*)(codeSignatureRegion + ntohl(sb->index[i].offset));
+            break;
+        }
+    }
+
+    if (!cd) {
+        fprintf(stderr, "Error: dyld shared cache code signature directory is missing.\n");
+        return -1;
+    }
+
+    if ( (uint8_t*)cd > (codeSignatureRegion + sbSize) ) {
+        fprintf(stderr, "Error: dyld shared cache code signature directory is out of bounds.\n");
+        return -1;
+    }
+
+    if ( cd->magic != htonl(CSMAGIC_CODEDIRECTORY) ) {
+        fprintf(stderr, "Error: dyld shared cache code signature directory magic is incorrect.\n");
+        return -1;
+    }
+
+    if ( ntohl(cd->nCodeSlots) < slotCountFromRegions ) {
+        fprintf(stderr, "Error: dyld shared cache code signature directory num slots is incorrect.\n");
+        return -1;
+    }
+
+    uint32_t dscDigestFormat = kCCDigestNone;
+    switch (cd->hashType) {
+        case CS_HASHTYPE_SHA1:
+            dscDigestFormat = kCCDigestSHA1;
+            break;
+        case CS_HASHTYPE_SHA256:
+            dscDigestFormat = kCCDigestSHA256;
+            break;
+        default:
+            break;
+    }
+
+    if (dscDigestFormat != kCCDigestNone) {
+        const uint64_t csPageSize = 1 << cd->pageSize;
+        size_t   hashOffset = ntohl(cd->hashOffset);
+        uint8_t* hashSlot = (uint8_t*)cd + hashOffset;
+        uint8_t cdHashBuffer[cd->hashSize];
+
+        // Skip local symbols for now as those aren't being codesign correctly right now.
+        size_t inBbufferSize = 0;
+        for (auto& sharedCacheRegion : sharedCacheRegions) {
+            if (sharedCacheRegion.first == dyldSharedCache->header.localSymbolsOffset)
+                continue;
+            inBbufferSize += (sharedCacheRegion.second - sharedCacheRegion.first);
+        }
+        uint32_t slotCountToProcess = (uint32_t)((inBbufferSize + CS_PAGE_SIZE - 1) / CS_PAGE_SIZE);
+
+        for (unsigned i = 0; i != slotCountToProcess; ++i) {
+            // Skip data pages as those may have been slid by ASLR in the extracted file
+            uint64_t fileOffset = i * csPageSize;
+            if ( (fileOffset >= mappings[1].fileOffset) && (fileOffset < (mappings[1].fileOffset + mappings[1].size)) )
+                continue;
+
+            CCDigest(dscDigestFormat, (uint8_t*)mapped_cache + fileOffset, csPageSize, cdHashBuffer);
+            uint8_t* cacheCdHashBuffer = hashSlot + (i * cd->hashSize);
+            if (memcmp(cdHashBuffer, cacheCdHashBuffer, cd->hashSize) != 0)  {
+                fprintf(stderr, "Error: dyld shared cache code signature for page %d is incorrect.\n", i);
+                return -1;
+            }
+        }
+    }
+    return 0;
+}
 
 int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path, const char* extraction_root_path,
-                                                                                                       void (^progress)(unsigned current, unsigned total))
+                                              progress_block progress)
 {
-       struct stat statbuf;
-       if (stat(shared_cache_file_path, &statbuf)) {
-               fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", shared_cache_file_path);
-               return -1;
-       }
-               
-       int cache_fd = open(shared_cache_file_path, O_RDONLY);
-       if (cache_fd < 0) {
-               fprintf(stderr, "Error: failed to open shared cache file at %s\n", shared_cache_file_path);
-               return -1;
-       }
-       
-       void* mapped_cache = mmap(NULL, (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;
-       }
-    
+    struct stat statbuf;
+    if (stat(shared_cache_file_path, &statbuf)) {
+        fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", shared_cache_file_path);
+        return -1;
+    }
+
+    int cache_fd = open(shared_cache_file_path, O_RDONLY);
+    if (cache_fd < 0) {
+        fprintf(stderr, "Error: failed to open shared cache file at %s\n", shared_cache_file_path);
+        return -1;
+    }
+
+    void* mapped_cache = mmap(NULL, (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;
+    }
+
     close(cache_fd);
 
-       // instantiate arch specific dylib maker
-    size_t (*dylib_create_func)(const void*, std::vector<uint8_t>&, const std::vector<seg_info>&) = NULL;
-            if ( strcmp((char*)mapped_cache, "dyld_v1    i386") == 0 ) 
-               dylib_create_func = dylib_maker<x86>;
-       else if ( strcmp((char*)mapped_cache, "dyld_v1  x86_64") == 0 ) 
-               dylib_create_func = dylib_maker<x86_64>;
-       else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64h") == 0 ) 
-               dylib_create_func = dylib_maker<x86_64>;
-       else if ( strcmp((char*)mapped_cache, "dyld_v1   armv5") == 0 ) 
-               dylib_create_func = dylib_maker<arm>;
-       else if ( strcmp((char*)mapped_cache, "dyld_v1   armv6") == 0 ) 
-               dylib_create_func = dylib_maker<arm>;
-       else if ( strcmp((char*)mapped_cache, "dyld_v1   armv7") == 0 ) 
-               dylib_create_func = dylib_maker<arm>;
-       else if ( strncmp((char*)mapped_cache, "dyld_v1  armv7", 14) == 0 ) 
-               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");
+    // instantiate arch specific dylib maker
+    dylib_maker_func* dylib_create_func = nullptr;
+    if ( strcmp((char*)mapped_cache, "dyld_v1    i386") == 0 )
+        dylib_create_func = dylib_maker<x86>;
+    else if ( strcmp((char*)mapped_cache, "dyld_v1  x86_64") == 0 )
+        dylib_create_func = dylib_maker<x86_64>;
+    else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64h") == 0 )
+        dylib_create_func = dylib_maker<x86_64>;
+    else if ( strcmp((char*)mapped_cache, "dyld_v1   armv5") == 0 )
+        dylib_create_func = dylib_maker<arm>;
+    else if ( strcmp((char*)mapped_cache, "dyld_v1   armv6") == 0 )
+        dylib_create_func = dylib_maker<arm>;
+    else if ( strcmp((char*)mapped_cache, "dyld_v1   armv7") == 0 )
+        dylib_create_func = dylib_maker<arm>;
+    else if ( strncmp((char*)mapped_cache, "dyld_v1  armv7", 14) == 0 )
+        dylib_create_func = dylib_maker<arm>;
+    else if ( strcmp((char*)mapped_cache, "dyld_v1   arm64") == 0 )
+        dylib_create_func = dylib_maker<arm64>;
+#if SUPPORT_ARCH_arm64e
+    else if ( strcmp((char*)mapped_cache, "dyld_v1  arm64e") == 0 )
+        dylib_create_func = dylib_maker<arm64>;
+#endif
+#if SUPPORT_ARCH_arm64_32
+    else if ( strcmp((char*)mapped_cache, "dyld_v1arm64_32") == 0 )
+        dylib_create_func = dylib_maker<arm64_32>;
+#endif
+    else {
+        fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n");
         munmap(mapped_cache, (size_t)statbuf.st_size);
-               return -1;
-       }
+        return -1;
+    }
+
+    // Verify that the cache isn't corrupt.
+    if (int result = sharedCacheIsValid(mapped_cache, (uint64_t)statbuf.st_size)) {
+        munmap(mapped_cache, (size_t)statbuf.st_size);
+        return result;
+    }
 
-       // iterate through all images in cache and build map of dylibs and segments
-       __block NameToSegments  map;
-       __block int                             result              = dyld_shared_cache_iterate(mapped_cache, (uint32_t)statbuf.st_size, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) {
+    // iterate through all images in cache and build map of dylibs and segments
+    __block NameToSegments  map;
+    int                     result = 0;
+
+    result = dyld_shared_cache_iterate(mapped_cache, (uint32_t)statbuf.st_size, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) {
         map[dylibInfo->path].push_back(seg_info(segInfo->name, segInfo->fileOffset, segInfo->fileSize));
     });
 
     if(result != 0) {
-               fprintf(stderr, "Error: dyld_shared_cache_iterate_segments_with_slide failed.\n");
+        fprintf(stderr, "Error: dyld_shared_cache_iterate_segments_with_slide failed.\n");
         munmap(mapped_cache, (size_t)statbuf.st_size);
-               return result;
-    }
-
-       // for each dylib instantiate a dylib file
-    dispatch_group_t        group               = dispatch_group_create();
-    dispatch_semaphore_t    sema                = dispatch_semaphore_create(2);
-    dispatch_queue_t        process_queue       = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
-    dispatch_queue_t        writer_queue        = dispatch_queue_create("dyld writer queue", 0);
-    
-       __block unsigned        count               = 0;
-    
-       for ( NameToSegments::iterator it = map.begin(); it != map.end(); ++it) {
-               dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
-        dispatch_group_async(group, process_queue, ^{
-            
-            char    dylib_path[PATH_MAX];
-            strcpy(dylib_path, extraction_root_path);
-            strcat(dylib_path, "/");
-            strcat(dylib_path, it->first);
-            
-            //printf("%s with %lu segments\n", dylib_path, it->second.size());
-            // make sure all directories in this path exist
-            make_dirs(dylib_path);
-            
-            // open file, create if does not already exist
-            int fd = ::open(dylib_path, O_CREAT | O_EXLOCK | O_RDWR, 0644);
-            if ( fd == -1 ) {
-                fprintf(stderr, "can't open or create dylib file %s, errnor=%d\n", dylib_path, errno);
-                result    = -1;
-                return;
-            }
-            
-            struct stat statbuf;
-            if (fstat(fd, &statbuf)) {
-                fprintf(stderr, "Error: stat failed for dyld file %s, errnor=%d\n", dylib_path, errno);
-                close(fd);
-                result    = -1;
-                return;
-            }
-            
-            std::vector<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);
-                result    = -1;
-                return;
-            }
-            
-            const size_t    offset  = dylib_create_func(mapped_cache, *vec, it->second);
-            
-            dispatch_group_async(group, writer_queue, ^{
-                progress(count++, (unsigned)map.size());
-                
-                if(offset != vec->size()) {
-                    //Write out the first page, and everything after offset
-                    if(   pwrite(fd, &vec->front(), 4096, 0) == -1 
-                       || pwrite(fd, &vec->front() + offset, vec->size() - offset, offset) == -1) {
-                        fprintf(stderr, "error writing, errnor=%d\n", errno);
-                        result    = -1;
-                    }
-                }
-                
-                delete vec;
-                close(fd);
-                dispatch_semaphore_signal(sema);
-            });
-        });
-       }
-    
-    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
-    dispatch_release(group);
-    dispatch_release(writer_queue);
-    
+        return result;
+    }
+
+    // for each dylib instantiate a dylib file
+    SharedCacheExtractor extractor(map, extraction_root_path, dylib_create_func, mapped_cache, progress);
+    result = extractor.extractCaches();
+
     munmap(mapped_cache, (size_t)statbuf.st_size);
-       return result;
+    return result;
 }
 
 
 
 int dyld_shared_cache_extract_dylibs(const char* shared_cache_file_path, const char* extraction_root_path)
 {
-       return dyld_shared_cache_extract_dylibs_progress(shared_cache_file_path, extraction_root_path, 
-                                                                                                       ^(unsigned , unsigned) {} );
+    return dyld_shared_cache_extract_dylibs_progress(shared_cache_file_path, extraction_root_path,
+                                                     ^(unsigned , unsigned) {} );
 }
 
 
-#if 0 
+#if 0
 // test program
 #include <stdio.h>
 #include <stddef.h>
@@ -658,36 +916,36 @@ int dyld_shared_cache_extract_dylibs(const char* shared_cache_file_path, const c
 
 
 typedef int (*extractor_proc)(const char* shared_cache_file_path, const char* extraction_root_path,
-                                                                                                       void (^progress)(unsigned current, unsigned total));
+                              void (^progress)(unsigned current, unsigned total));
 
 int main(int argc, const char* argv[])
 {
-       if ( argc != 3 ) {
-               fprintf(stderr, "usage: dsc_extractor <path-to-cache-file> <path-to-device-dir>\n");
-               return 1;
-       }
-       
-       //void* handle = dlopen("/Volumes/my/src/dyld/build/Debug/dsc_extractor.bundle", RTLD_LAZY);
-       void* handle = dlopen("/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/usr/lib/dsc_extractor.bundle", RTLD_LAZY);
-       if ( handle == NULL ) {
-               fprintf(stderr, "dsc_extractor.bundle could not be loaded\n");
-               return 1;
-       }
-       
-       extractor_proc proc = (extractor_proc)dlsym(handle, "dyld_shared_cache_extract_dylibs_progress");
-       if ( proc == NULL ) {
-               fprintf(stderr, "dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n");
-               return 1;
-       }
-       
-       int result = (*proc)(argv[1], argv[2], ^(unsigned c, unsigned total) { printf("%d/%d\n", c, total); } );
-       fprintf(stderr, "dyld_shared_cache_extract_dylibs_progress() => %d\n", result);
-       return 0;
+    if ( argc != 3 ) {
+        fprintf(stderr, "usage: dsc_extractor <path-to-cache-file> <path-to-device-dir>\n");
+        return 1;
+    }
+
+    //void* handle = dlopen("/Volumes/my/src/dyld/build/Debug/dsc_extractor.bundle", RTLD_LAZY);
+    void* handle = dlopen("/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/usr/lib/dsc_extractor.bundle", RTLD_LAZY);
+    if ( handle == NULL ) {
+        fprintf(stderr, "dsc_extractor.bundle could not be loaded\n");
+        return 1;
+    }
+
+    extractor_proc proc = (extractor_proc)dlsym(handle, "dyld_shared_cache_extract_dylibs_progress");
+    if ( proc == NULL ) {
+        fprintf(stderr, "dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n");
+        return 1;
+    }
+
+    int result = (*proc)(argv[1], argv[2], ^(unsigned c, unsigned total) { printf("%d/%d\n", c, total); } );
+    fprintf(stderr, "dyld_shared_cache_extract_dylibs_progress() => %d\n", result);
+    return 0;
 }
 
 
 #endif
 
+
+
 
index 7aa84ca5b1090d0fa8331d2205069bded14fed49..2196ac3cf40cb450ff0c579a7e34c5cbc6ef4a38 100644 (file)
@@ -33,7 +33,7 @@
 #include "Architectures.hpp"
 #include "MachOFileAbstraction.hpp"
 #include "CacheFileAbstraction.hpp"
-
+#include "SupportedArchs.h"
 
 namespace dyld {
 
@@ -203,8 +203,14 @@ 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);
+#if SUPPORT_ARCH_arm64_32
+       else if ( strcmp((char*)cache, "dyld_v1arm64_32") == 0 ) 
+                       return dyld::walkImages<arm64_32>(cache, shared_cache_size, callback);
+#endif
+#if SUPPORT_ARCH_arm64e
        else if ( strcmp((char*)cache, "dyld_v1  arm64e") == 0 ) 
                        return dyld::walkImages<arm64>(cache, shared_cache_size, callback);
+#endif
        else
                return -1;
 }
index 57b5d074afc4621d52271a6d957e89dcc4644fcf..a8a363b7cfd19ee95b7031f4cab03f53067f1b46 100644 (file)
@@ -230,6 +230,77 @@ struct dyld_cache_slide_info2
 #define DYLD_CACHE_SLIDE_PAGE_ATTR_END                 0x8000  // last chain entry for page
 
 
+
+// The version 3 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
+//
+// There are two cases:
+//
+// 1) pageStarts[pageIndex] == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE
+//    The page contains no values that need rebasing.
+//
+// 2) otherwise...
+//    All rebase locations are in one linked list. The offset of the first
+//    rebase location in the page is pageStarts[pageIndex].
+//
+// A pointer is one of :
+// {
+//     uint64_t pointerValue : 51;
+//     uint64_t offsetToNextPointer : 11;
+//     uint64_t isBind : 1 = 0;
+//     uint64_t authenticated : 1 = 0;
+// }
+// {
+//     uint32_t offsetFromSharedCacheBase;
+//     uint16_t diversityData;
+//     uint16_t hasAddressDiversity : 1;
+//     uint16_t key : 2;
+//     uint16_t offsetToNextPointer : 11;
+//     uint16_t isBind : 1;
+//     uint16_t authenticated : 1 = 1;
+// }
+//
+// The code for processing a linked list (chain) is:
+//
+//    uint32_t delta = pageStarts[pageIndex];
+//    uint8_t* loc = pageStart;
+//    do {
+//        loc += delta;
+//        uintptr_t rawValue = *((uintptr_t*)loc);
+//        delta = ( (value & 0x3FF8000000000000) >> 51) * sizeof(uint64_t);
+//        if (extraBindData.isAuthenticated) {
+//            newValue = ( value & 0xFFFFFFFF )  + results->slide + auth_value_add;
+//            newValue = sign_using_the_various_bits(newValue);
+//        } else {
+//            uint64_t top8Bits = value & 0x0007F80000000000ULL;
+//            uint64_t bottom43Bits = value & 0x000007FFFFFFFFFFULL;
+//            uint64_t targetValue = ( top8Bits << 13 ) | bottom43Bits;
+//            newValue = targetValue + results->slide;
+//        }
+//        *((uintptr_t*)loc) = newValue;
+//    } while (delta != 0 )
+//
+//
+struct dyld_cache_slide_info3
+{
+    uint32_t    version;            // currently 3
+    uint32_t    page_size;          // currently 4096 (may also be 16384)
+    uint32_t    page_starts_count;
+    uint64_t    auth_value_add;
+    uint16_t    page_starts[/* page_starts_count */];
+};
+
+#define DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE    0xFFFF    // page has no rebasing
+
+
 struct dyld_cache_local_symbols_info
 {
        uint32_t        nlistOffset;            // offset into this chunk of nlist entries
index 4c56c35cb77538a4f637c8c12ac0eedc351c308f..0324b03cc9e02023734cf751dc8afd322dabacc2 100644 (file)
 #include <mach-o/arch.h>
 #include <mach-o/loader.h>
 #include <mach-o/dyld.h>
+#include <mach-o/dyld_priv.h>
 #include <mach/mach.h>
 
 #include <map>
 #include <vector>
 
+#include "DyldSharedCache.h"
+
 #include "dsc_iterator.h"
 #include "dsc_extractor.h"
 #include "dyld_cache_format.h"
@@ -47,6 +50,7 @@
 #include "MachOFileAbstraction.hpp"
 #include "CacheFileAbstraction.hpp"
 #include "Trie.hpp"
+#include "SupportedArchs.h"
 
 enum Mode {
        modeNone,
@@ -54,10 +58,12 @@ enum Mode {
        modeMap,
        modeDependencies,
        modeSlideInfo,
+    modeVerboseSlideInfo,
        modeAcceleratorInfo,
        modeTextInfo,
        modeLinkEdit,
        modeLocalSymbols,
+    modeStrings,
        modeInfo,
        modeSize,
        modeExtract
@@ -93,9 +99,55 @@ struct Results {
 };
 
 
+// mmap() an shared cache file read/only but laid out like it would be at runtime
+static const DyldSharedCache* mapCacheFile(const char* path, size_t& cacheLength)
+{
+    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;
+    }
+    
+    uint8_t  firstPage[4096];
+    if ( ::pread(cache_fd, firstPage, 4096, 0) != 4096 ) {
+        fprintf(stderr, "Error: failed to read shared cache file at %s\n", path);
+        return nullptr;
+    }
+    const dyld_cache_header*       header   = (dyld_cache_header*)firstPage;
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(firstPage + header->mappingOffset);
+    
+    size_t vmSize = (size_t)(mappings[2].address + mappings[2].size - mappings[0].address);
+    vm_address_t result;
+    kern_return_t r = ::vm_allocate(mach_task_self(), &result, vmSize, VM_FLAGS_ANYWHERE);
+    if ( r != KERN_SUCCESS ) {
+        fprintf(stderr, "Error: failed to allocate space to load shared cache file at %s\n", path);
+        return nullptr;
+    }
+    for (int i=0; i < 3; ++i) {
+        void* mapped_cache = ::mmap((void*)(result + mappings[i].address - mappings[0].address), (size_t)mappings[i].size,
+                                    PROT_READ, MAP_FIXED | MAP_PRIVATE, cache_fd, mappings[i].fileOffset);
+        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);
+    
+    cacheLength = statbuf.st_size;
+    
+    return (DyldSharedCache*)result;
+}
+
+
 
 void usage() {
-       fprintf(stderr, "Usage: dyld_shared_cache_util -list [ -uuid ] [-vmaddr] | -dependents <dylib-path> [ -versions ] | -linkedit | -map | -slide_info | -info | -extract <dylib-dir>  [ shared-cache-file ] \n");
+       fprintf(stderr, "Usage: dyld_shared_cache_util -list [ -uuid ] [-vmaddr] | -dependents <dylib-path> [ -versions ] | -linkedit | -map | -slide_info | -verbose_slide_info | -info | -extract <dylib-dir>  [ shared-cache-file ] \n");
 }
 
 #if __x86_64__
@@ -136,6 +188,8 @@ 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_ARCH_8_32__
+       return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64_32";
 #elif __arm64e__
        return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64e";
 #elif __arm64__
@@ -341,7 +395,7 @@ void print_map(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_
 
 static void checkMode(Mode mode) {
        if ( mode != modeNone ) {
-               fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -linkedit, -map, -extract, or -size\n");
+               fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -verbose_slide_info, -linkedit, -map, -extract, or -size\n");
                usage();
                exit(1);
        }
@@ -360,6 +414,9 @@ int main (int argc, const char* argv[]) {
     options.dependentsOfPath = NULL;
     options.extractionDir = NULL;
 
+    bool printStrings = false;
+    bool printExports = false;
+
     for (uint32_t i = 1; i < argc; i++) {
         const char* opt = argv[i];
         if (opt[0] == '-') {
@@ -389,6 +446,10 @@ int main (int argc, const char* argv[]) {
                                checkMode(options.mode);
                                options.mode = modeSlideInfo;
                        }
+            else if (strcmp(opt, "-verbose_slide_info") == 0) {
+                checkMode(options.mode);
+                options.mode = modeVerboseSlideInfo;
+            }
                        else if (strcmp(opt, "-accelerator_info") == 0) {
                                checkMode(options.mode);
                                options.mode = modeAcceleratorInfo;
@@ -397,10 +458,22 @@ int main (int argc, const char* argv[]) {
                                checkMode(options.mode);
                                options.mode = modeTextInfo;
                        }
-                       else if (strcmp(opt, "-local_symbols") == 0) {
-                               checkMode(options.mode);
-                               options.mode = modeLocalSymbols;
-                       }
+            else if (strcmp(opt, "-local_symbols") == 0) {
+                checkMode(options.mode);
+                options.mode = modeLocalSymbols;
+            }
+            else if (strcmp(opt, "-strings") == 0) {
+                if (options.mode != modeStrings)
+                    checkMode(options.mode);
+                options.mode = modeStrings;
+                printStrings = true;
+            }
+            else if (strcmp(opt, "-exports") == 0) {
+                if (options.mode != modeStrings)
+                    checkMode(options.mode);
+                options.mode = modeStrings;
+                printExports = true;
+            }
                        else if (strcmp(opt, "-map") == 0) {
                                checkMode(options.mode);
                                options.mode = modeMap;
@@ -448,7 +521,7 @@ int main (int argc, const char* argv[]) {
                exit(1);
        }
                
-       if ( options.mode != modeSlideInfo ) {
+       if ( options.mode != modeSlideInfo &&  options.mode != modeVerboseSlideInfo ) {
                if ( options.printUUIDs && (options.mode != modeList) )
                        fprintf(stderr, "Warning: -uuid option ignored outside of -list mode\n");
                        
@@ -464,35 +537,39 @@ int main (int argc, const char* argv[]) {
                        exit(1);
                }
        }
-                       
-       struct stat statbuf;
-       if ( ::stat(sharedCachePath, &statbuf) == -1 ) {
-               fprintf(stderr, "Error: stat() failed for dyld shared cache at %s, errno=%d\n", sharedCachePath, errno);
-               exit(1);
-       }
-               
-       int cache_fd = ::open(sharedCachePath, O_RDONLY);
-       if ( cache_fd < 0 ) {
-               fprintf(stderr, "Error: open() failed for shared cache file at %s, errno=%d\n", sharedCachePath, errno);
-               exit(1);
-       }
-       options.mappedCache = ::mmap(NULL, (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);
-       }
+    
+    const DyldSharedCache* dyldCache = nullptr;
+    bool dyldCacheIsLive = true;
+    size_t cacheLength = 0;
+    if ( sharedCachePath != nullptr ) {
+        dyldCache = mapCacheFile(sharedCachePath, cacheLength);
+        dyldCacheIsLive = false;
+    }
+    else {
+#if __MAC_OS_X_VERSION_MIN_REQUIRED && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101300)
+        fprintf(stderr, "this tool needs to run on macOS 10.13 or later\n");
+        return 1;
+#else
+        dyldCache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLength);
+#endif
+    }
+
+    options.mappedCache = dyldCache;
        
-       if ( options.mode == modeSlideInfo ) {
+       if ( options.mode == modeSlideInfo || options.mode == modeVerboseSlideInfo ) {
                const dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)options.mappedCache;
                if ( header->slideInfoOffset() == 0 ) {
                        fprintf(stderr, "Error: dyld shared cache does not contain slide info\n");
                        exit(1);
                }
                const dyldCacheFileMapping<LittleEndian>* mappings = (dyldCacheFileMapping<LittleEndian>*)((char*)options.mappedCache + header->mappingOffset());
+        const dyldCacheFileMapping<LittleEndian>* textMapping = &mappings[0];
                const dyldCacheFileMapping<LittleEndian>* dataMapping = &mappings[1];
+        const dyldCacheFileMapping<LittleEndian>* linkEditMapping = &mappings[2];
                uint64_t dataStartAddress = dataMapping->address();
                uint64_t dataSize = dataMapping->size();
-               const dyldCacheSlideInfo<LittleEndian>* slideInfoHeader = (dyldCacheSlideInfo<LittleEndian>*)((char*)options.mappedCache+header->slideInfoOffset());
+        uint64_t slideInfoMappedOffset = (header->slideInfoOffset()-linkEditMapping->file_offset()) + (linkEditMapping->address() - textMapping->address());
+               const dyldCacheSlideInfo<LittleEndian>* slideInfoHeader = (dyldCacheSlideInfo<LittleEndian>*)((char*)options.mappedCache+slideInfoMappedOffset);
                printf("slide info version=%d\n", slideInfoHeader->version());
                if ( slideInfoHeader->version() == 1 ) {
                        printf("toc_count=%d, data page count=%lld\n", slideInfoHeader->toc_count(), dataSize/4096);
@@ -515,6 +592,30 @@ int main (int argc, const char* argv[]) {
                        const uint16_t* extras = (uint16_t* )((char*)slideInfo + slideInfo->page_extras_offset());
                        for (int i=0; i < slideInfo->page_starts_count(); ++i) {
                                const uint16_t start = starts[i];
+                auto rebaseChain = [&](uint8_t* pageContent, uint16_t startOffset)
+                {
+                    uintptr_t slideAmount = 0;
+                    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;
+                        }
+                        printf("    [% 5d + 0x%04llX]: 0x%016llX\n", i, (uint64_t)(pageOffset), (uint64_t)rawValue);
+                        pageOffset += delta;
+                    }
+                };
+                const uint8_t* dataPagesStart = (uint8_t*)((char*)options.mappedCache + dataMapping->file_offset());
                                if ( start == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) {
                                        printf("page[% 5d]: no rebasing\n", i);
                                }
@@ -525,6 +626,11 @@ int main (int argc, const char* argv[]) {
                                        do {
                                                uint16_t aStart = extras[j];
                                                printf("start=0x%04X ", aStart & 0x3FFF);
+                        if ( options.mode == modeVerboseSlideInfo ) {
+                            uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size()*i));
+                            uint16_t pageStartOffset = (aStart & 0x3FFF)*4;
+                            rebaseChain(page, pageStartOffset);
+                        }
                                                done = (extras[j] & DYLD_CACHE_SLIDE_PAGE_ATTR_END);
                                                ++j;
                                        } while ( !done );
@@ -532,11 +638,125 @@ int main (int argc, const char* argv[]) {
                                }
                                else {
                                        printf("page[% 5d]: start=0x%04X\n", i, starts[i]);
+                    if ( options.mode == modeVerboseSlideInfo ) {
+                        uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size()*i));
+                        uint16_t pageStartOffset = start*4;
+                        rebaseChain(page, pageStartOffset);
+                    }
                                }
                        }
                }
+        else if ( slideInfoHeader->version() == 3 ) {
+            const dyldCacheSlideInfo3<LittleEndian>* slideInfo = (dyldCacheSlideInfo3<LittleEndian>*)(slideInfoHeader);
+            printf("page_size=%d\n", slideInfo->page_size());
+            printf("page_starts_count=%d\n", slideInfo->page_starts_count());
+            printf("auth_value_add=0x%016llX\n", slideInfo->auth_value_add());
+            const uint8_t* dataSegmentStart = (uint8_t*)((char*)options.mappedCache + dataMapping->file_offset());
+            for (int i=0; i < slideInfo->page_starts_count(); ++i) {
+                const uint16_t start = slideInfo->page_starts(i);
+                if ( start == 0xFFFF ) {
+                    printf("page[% 5d]: no rebasing\n", i);
+                }
+                else {
+                    printf("page[% 5d]: start=0x%04X\n", i, start);
+                    if ( options.mode == modeVerboseSlideInfo ) {
+                        typedef Pointer64<LittleEndian> P;
+                        typedef typename P::uint_t      pint_t;
+                        const uint8_t* pageStart = dataSegmentStart + (i * slideInfo->page_size());
+                        pint_t delta = start;
+                        const uint8_t* rebaseLocation = pageStart;
+                        do {
+                            rebaseLocation += delta;
+                            pint_t value = (pint_t)P::getP(*(uint64_t*)rebaseLocation);
+                            delta = ( (value & 0x3FF8000000000000) >> 51) * sizeof(pint_t);
+
+                            // Regular pointer which needs to fit in 51-bits of value.
+                            // C++ RTTI uses the top bit, so we'll allow the whole top-byte
+                            // and the signed-extended bottom 43-bits to be fit in to 51-bits.
+                            uint64_t top8Bits = value & 0x007F80000000000ULL;
+                            uint64_t bottom43Bits = value & 0x000007FFFFFFFFFFULL;
+                            uint64_t targetValue = ( top8Bits << 13 ) | (((intptr_t)(bottom43Bits << 21) >> 21) & 0x00FFFFFFFFFFFFFF);
+                            printf("    [% 5d + 0x%04llX]: 0x%016llX\n", i, (uint64_t)(rebaseLocation - pageStart), targetValue);
+                        } while (delta != 0);
+                    }
+                }
+            }
+        }
+        else if ( slideInfoHeader->version() == 4 ) {
+            const dyld_cache_slide_info4* slideInfo = (dyld_cache_slide_info4*)(slideInfoHeader);
+            printf("page_size=%d\n", slideInfo->page_size);
+            printf("delta_mask=0x%016llX\n", slideInfo->delta_mask);
+            printf("value_add=0x%016llX\n", slideInfo->value_add);
+            printf("page_starts_count=%d, page_extras_count=%d\n", slideInfo->page_starts_count, slideInfo->page_extras_count);
+            const uint16_t* starts = (uint16_t* )((char*)slideInfo + slideInfo->page_starts_offset);
+            const uint16_t* extras = (uint16_t* )((char*)slideInfo + slideInfo->page_extras_offset);
+            for (int i=0; i < slideInfo->page_starts_count; ++i) {
+                const uint16_t start = starts[i];
+                auto rebaseChainV4 = [&](uint8_t* pageContent, uint16_t startOffset)
+                {
+                    uintptr_t slideAmount = 0;
+                    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;
+                        uint32_t rawValue = *((uint32_t*)loc);
+                        delta = (uint32_t)((rawValue & deltaMask) >> deltaShift);
+                        uintptr_t value = (rawValue & valueMask);
+                        if ( (value & 0xFFFF8000) == 0 ) {
+                            // small positive non-pointer, use as-is
+                        }
+                        else if ( (value & 0x3FFF8000) == 0x3FFF8000 ) {
+                            // small negative non-pointer
+                            value |= 0xC0000000;
+                        }
+                        else  {
+                            value += valueAdd;
+                            value += slideAmount;
+                        }
+                        printf("    [% 5d + 0x%04X]: 0x%08X\n", i, pageOffset, rawValue);
+                        pageOffset += delta;
+                    }
+                };
+                const uint8_t* dataPagesStart = (uint8_t*)((char*)options.mappedCache + dataMapping->file_offset());
+                if ( start == DYLD_CACHE_SLIDE4_PAGE_NO_REBASE ) {
+                    printf("page[% 5d]: no rebasing\n", i);
+                }
+                else if ( start & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA ) {
+                    printf("page[% 5d]: ", i);
+                    int j=(start & DYLD_CACHE_SLIDE4_PAGE_INDEX);
+                    bool done = false;
+                    do {
+                        uint16_t aStart = extras[j];
+                        printf("start=0x%04X ", aStart & DYLD_CACHE_SLIDE4_PAGE_INDEX);
+                        if ( options.mode == modeVerboseSlideInfo ) {
+                            uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size*i));
+                            uint16_t pageStartOffset = (aStart & DYLD_CACHE_SLIDE4_PAGE_INDEX)*4;
+                            rebaseChainV4(page, pageStartOffset);
+                        }
+                        done = (extras[j] & DYLD_CACHE_SLIDE4_PAGE_EXTRA_END);
+                        ++j;
+                    } while ( !done );
+                    printf("\n");
+                }
+                else {
+                    printf("page[% 5d]: start=0x%04X\n", i, starts[i]);
+                    if ( options.mode == modeVerboseSlideInfo ) {
+                        uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size*i));
+                        uint16_t pageStartOffset = start*4;
+                        rebaseChainV4(page, pageStartOffset);
+                    }
+                }
+            }
+        }
+        return 0;
        }
-       else if ( options.mode == modeInfo ) {
+       
+    if ( options.mode == modeInfo ) {
                const dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)options.mappedCache;
                printf("uuid: ");
                if ( header->mappingOffset() >= 0x68 ) {
@@ -553,25 +773,27 @@ int main (int argc, const char* argv[]) {
                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));
+            uint32_t bitfield = *((uint32_t*)(((char*)header) + 0xDC));
+            uint32_t simulator = bitfield & 0x200;
+            uint32_t locallyBuiltCache = bitfield & 0x400;
             switch (platform) {
                 case 1:
                     printf("platform: macOS\n");
                     break;
                 case 2:
-                    if ( simulator & 0x400 )
+                    if ( simulator )
                         printf("platform: iOS simulator\n");
                     else
                         printf("platform: iOS\n");
                     break;
                 case 3:
-                    if ( simulator & 0x400 )
+                    if ( simulator )
                         printf("platform: tvOS simulator\n");
                     else
                         printf("platform: tvOS\n");
                     break;
                 case 4:
-                    if ( simulator & 0x400 )
+                    if ( simulator )
                         printf("platform: watchOS simulator\n");
                     else
                         printf("platform: watchOS\n");
@@ -582,6 +804,7 @@ int main (int argc, const char* argv[]) {
                 default:
                     printf("platform: 0x%08X 0x%08X\n", platform, simulator);
             }
+            printf("built by: %s\n", locallyBuiltCache ? "local machine" : "B&I");
         }
                printf("image count: %u\n", header->imagesCount());
                if ( (header->mappingOffset() >= 0x78) && (header->branchPoolsOffset() != 0) ) {
@@ -604,7 +827,7 @@ int main (int argc, const char* argv[]) {
                                                                                                            mappings[i].address(), mappings[i].address() + mappings[i].size());
                }
                if ( header->codeSignatureOffset() != 0 ) {
-                       uint64_t size = statbuf.st_size - header->codeSignatureOffset();
+                       uint64_t size = cacheLength - header->codeSignatureOffset();
                        uint64_t csAddr = mappings[header->mappingCount()-1].address() + mappings[header->mappingCount()-1].size();
                        if ( size != 0 )
                                printf("    code sign   %3lluMB,  file offset: 0x%08llX -> 0x%08llX,  address: 0x%08llX -> 0x%08llX\n",
@@ -762,6 +985,51 @@ int main (int argc, const char* argv[]) {
                #endif
                }
        }
+    else if ( options.mode == modeStrings ) {
+        if (printStrings) {
+            dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
+                const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
+                int64_t slide = ma->getSlide();
+                ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool& stop) {
+                    if ( ( (info.sectFlags & SECTION_TYPE) == S_CSTRING_LITERALS ) ) {
+                        if ( malformedSectionRange ) {
+                            stop = true;
+                            return;
+                        }
+                        const uint8_t* content = (uint8_t*)(info.sectAddr + slide);
+                        const char* s   = (char*)content;
+                        const char* end = s + info.sectSize;
+                        while ( s < end ) {
+                            printf("%s: %s\n", ma->installName(), s);
+                            while (*s != '\0' )
+                                ++s;
+                            ++s;
+                        }
+                    }
+                });
+            });
+        }
+
+        if (printExports) {
+            dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
+                const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
+                uint32_t exportTrieRuntimeOffset;
+                uint32_t exportTrieSize;
+                if ( ma->hasExportTrie(exportTrieRuntimeOffset, exportTrieSize) ) {
+                    const uint8_t* start = (uint8_t*)mh + exportTrieRuntimeOffset;
+                    const uint8_t* end = start + exportTrieSize;
+                    std::vector<ExportInfoTrie::Entry> exports;
+                    if ( !ExportInfoTrie::parseTrie(start, end, exports) ) {
+                        return;
+                    }
+
+                    for (const ExportInfoTrie::Entry& entry: exports) {
+                        printf("%s: %s\n", ma->installName(), entry.name.c_str());
+                    }
+                }
+            });
+        }
+    }
        else if ( options.mode == modeExtract ) {
                char pathBuffer[PATH_MAX];
                uint32_t bufferSize = PATH_MAX;
@@ -810,10 +1078,12 @@ int main (int argc, const char* argv[]) {
                                        break;
                                case modeNone:
                                case modeInfo:
-                               case modeSlideInfo:
+                case modeSlideInfo:
+                case modeVerboseSlideInfo:
                                case modeAcceleratorInfo:
                                case modeTextInfo:
                                case modeLocalSymbols:
+                case modeStrings:
                                case modeExtract:
                                        break;
                        }
@@ -838,16 +1108,22 @@ int main (int argc, const char* argv[]) {
                                        break;
                                case modeNone:
                                case modeInfo:
-                               case modeSlideInfo:
+                case modeSlideInfo:
+                case modeVerboseSlideInfo:
                                case modeAcceleratorInfo:
                                case modeTextInfo:
-                               case modeLocalSymbols:
+                case modeLocalSymbols:
+                case modeStrings:
                                case modeExtract:
                                        break;
                        }
                }               
-               else if ( (strncmp((char*)options.mappedCache, "dyld_v1   armv", 14) == 0) 
-                          || (strncmp((char*)options.mappedCache, "dyld_v1  armv", 13) == 0) ) {
+               else if ( (strncmp((char*)options.mappedCache, "dyld_v1   armv", 14) == 0)
+                                || (strncmp((char*)options.mappedCache, "dyld_v1  armv", 13) == 0)
+#if SUPPORT_ARCH_arm64_32
+                                || (strcmp((char*)options.mappedCache, "dyld_v1arm64_32") == 0)
+#endif
+                 ) {
                        switch ( options.mode ) {
                                case modeList:
                                        callback = print_list<arm>;
@@ -866,16 +1142,21 @@ int main (int argc, const char* argv[]) {
                                        break;
                                case modeNone:
                                case modeInfo:
-                               case modeSlideInfo:
+                case modeSlideInfo:
+                case modeVerboseSlideInfo:
                                case modeAcceleratorInfo:
                                case modeTextInfo:
-                               case modeLocalSymbols:
+                case modeLocalSymbols:
+                case modeStrings:
                                case modeExtract:
                                        break;
                        }
                }               
                else if ( (strcmp((char*)options.mappedCache, "dyld_v1   arm64") == 0)
-               || (strcmp((char*)options.mappedCache, "dyld_v1  arm64e") == 0) ) {
+#if SUPPORT_ARCH_arm64e
+               || (strcmp((char*)options.mappedCache, "dyld_v1  arm64e") == 0)
+#endif
+                 ) {
                        switch ( options.mode ) {
                                case modeList:
                                        callback = print_list<arm64>;
@@ -894,22 +1175,55 @@ int main (int argc, const char* argv[]) {
                                        break;
                                case modeNone:
                                case modeInfo:
+                case modeSlideInfo:
+                case modeVerboseSlideInfo:
+                               case modeAcceleratorInfo:
+                               case modeTextInfo:
+                case modeLocalSymbols:
+                case modeStrings:
+                               case modeExtract:
+                                       break;
+                       }
+               }
+#if SUPPORT_ARCH_arm64_32
+               else if ( (strcmp((char*)options.mappedCache, "dyld_v1arm64_32") == 0) ) {
+                       switch ( options.mode ) {
+                               case modeList:
+                                       callback = print_list<arm64_32>;
+                                       break;
+                               case modeMap:
+                                       callback = print_map<arm64_32>;
+                                       break;
+                               case modeDependencies:
+                                       callback = print_dependencies<arm64_32>;
+                                       break;
+                               case modeLinkEdit:
+                                       callback = process_linkedit<arm64_32>;
+                                       break;
+                               case modeSize:
+                                       callback = collect_size<arm64_32>;
+                                       break;
+                               case modeNone:
+                               case modeInfo:
                                case modeSlideInfo:
+                case modeVerboseSlideInfo:
                                case modeAcceleratorInfo:
                                case modeTextInfo:
-                               case modeLocalSymbols:
+                case modeLocalSymbols:
+                case modeStrings:
                                case modeExtract:
                                        break;
                        }
-               }               
-               else {
+               }
+#endif
+        else {
                        fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n");
                        exit(1);
                }
                
                __block Results results;
                results.dependentTargetFound = false;
-               int iterateResult = dyld_shared_cache_iterate(options.mappedCache, (uint32_t)statbuf.st_size
+               int iterateResult = dyld_shared_cache_iterate(options.mappedCache, (uint32_t)cacheLength
                                                                                   ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo ) {
                                                                                           (callback)(dylibInfo, segInfo, options, results);
                                                                                   });
index 13117d5064abcd750ebd526002e211825f2ece42..28f89e27fee944bfc339312bfc3bec396aa5d950 100644 (file)
 #include <sys/mman.h>
 #include <sys/param.h>
 #include <sys/mount.h>
+#include <sys/sysctl.h>
 #include <libkern/OSAtomic.h>
 
+#include "Tracing.h"
+
 #include "ImageLoader.h"
 
 
@@ -368,6 +371,7 @@ const ImageLoader::Symbol* ImageLoader::findExportedSymbolInImageOrDependentImag
 // this is called by initializeMainExecutable() to interpose on the initial set of images
 void ImageLoader::applyInterposing(const LinkContext& context)
 {
+       dyld3::ScopedTimer timer(DBG_DYLD_TIMING_APPLY_INTERPOSING, 0, 0, 0);
        if ( fgInterposingTuples.size() != 0 )
                this->recursiveApplyInterposing(context);
 }
@@ -390,6 +394,58 @@ uintptr_t ImageLoader::interposedAddress(const LinkContext& context, uintptr_t a
        return address;
 }
 
+void ImageLoader::applyInterposingToDyldCache(const LinkContext& context) {
+#if USES_CHAINED_BINDS
+       if (!context.dyldCache)
+               return;
+       if (fgInterposingTuples.empty())
+               return;
+       // For each of the interposed addresses, see if any of them are in the shared cache.  If so, find
+       // that image and apply its patch table to all uses.
+       uintptr_t cacheStart = (uintptr_t)context.dyldCache;
+       for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) {
+               if ( context.verboseInterposing )
+                       dyld::log("dyld: interpose: Trying to interpose address 0x%08llx\n", (uint64_t)it->replacee);
+               uint32_t imageIndex;
+               uint32_t cacheOffsetOfReplacee = (uint32_t)(it->replacee - cacheStart);
+               if (!context.dyldCache->addressInText(cacheOffsetOfReplacee, &imageIndex))
+                       continue;
+               dyld3::closure::ImageNum imageInCache = imageIndex+1;
+               if ( context.verboseInterposing )
+                       dyld::log("dyld: interpose: Found shared cache image %d for 0x%08llx\n", imageInCache, (uint64_t)it->replacee);
+               const dyld3::closure::Image* image = context.dyldCache->cachedDylibsImageArray()->imageForNum(imageInCache);
+               image->forEachPatchableExport(^(uint32_t cacheOffsetOfImpl, const char* exportName) {
+                       // Skip patching anything other than this symbol
+                       if (cacheOffsetOfImpl != cacheOffsetOfReplacee)
+                               return;
+                       if ( context.verboseInterposing )
+                               dyld::log("dyld: interpose: Patching uses of symbol %s in shared cache binary at %s\n", exportName, image->path());
+                       uintptr_t newLoc = it->replacement;
+                       image->forEachPatchableUseOfExport(cacheOffsetOfImpl, ^(dyld3::closure::Image::PatchableExport::PatchLocation patchLocation) {
+                               uintptr_t* loc = (uintptr_t*)(cacheStart+patchLocation.cacheOffset);
+#if __has_feature(ptrauth_calls)
+                               if ( patchLocation.authenticated ) {
+                                       dyld3::MachOLoaded::ChainedFixupPointerOnDisk fixupInfo;
+                                       fixupInfo.authRebase.auth      = true;
+                                       fixupInfo.authRebase.addrDiv   = patchLocation.usesAddressDiversity;
+                                       fixupInfo.authRebase.diversity = patchLocation.discriminator;
+                                       fixupInfo.authRebase.key       = patchLocation.key;
+                                       *loc = fixupInfo.signPointer(loc, newLoc + patchLocation.getAddend());
+                                       if ( context.verboseInterposing )
+                                               dyld::log("dyld: interpose: *%p = %p (JOP: diversity 0x%04X, addr-div=%d, key=%s)\n",
+                                                                 loc, (void*)*loc, patchLocation.discriminator, patchLocation.usesAddressDiversity, patchLocation.keyName());
+                                       return;
+                               }
+#endif
+                               if ( context.verboseInterposing )
+                                       dyld::log("dyld: interpose: *%p = 0x%0llX (dyld cache patch) to %s\n", loc, newLoc + patchLocation.getAddend(), exportName);
+                               *loc = newLoc + patchLocation.getAddend();
+                       });
+               });
+       }
+#endif
+}
+
 void ImageLoader::addDynamicInterposingTuples(const struct dyld_interpose_tuple array[], size_t count)
 {
        for(size_t i=0; i < count; ++i) {
@@ -408,6 +464,26 @@ void ImageLoader::addDynamicInterposingTuples(const struct dyld_interpose_tuple
        }
 }
 
+// <rdar://problem/29099600> dyld should tell the kernel when it is doing root fix-ups
+void ImageLoader::vmAccountingSetSuspended(const LinkContext& context, bool suspend)
+{
+#if __arm__ || __arm64__
+       static bool sVmAccountingSuspended = false;
+       if ( suspend == sVmAccountingSuspended )
+               return;
+    if ( context.verboseBind )
+        dyld::log("set vm.footprint_suspend=%d\n", suspend);
+    int newValue = suspend ? 1 : 0;
+    int oldValue = 0;
+    size_t newlen = sizeof(newValue);
+    size_t oldlen = sizeof(oldValue);
+    int ret = sysctlbyname("vm.footprint_suspend", &oldValue, &oldlen, &newValue, newlen);
+    if ( context.verboseBind && (ret != 0) )
+               dyld::log("vm.footprint_suspend => %d, errno=%d\n", ret, errno);
+       sVmAccountingSuspended = suspend;
+#endif
+}
+
 
 void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool preflightOnly, bool neverUnload, const RPathChain& loaderRPaths, const char* imagePath)
 {
@@ -423,24 +499,30 @@ void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool pr
        // we only do the loading step for preflights
        if ( preflightOnly )
                return;
-               
+
        uint64_t t1 = mach_absolute_time();
        context.clearAllDepths();
        this->recursiveUpdateDepth(context.imageCount());
 
-       uint64_t t2 = mach_absolute_time();
-       this->recursiveRebase(context);
-       context.notifyBatch(dyld_image_state_rebased, false);
-       
-       uint64_t t3 = mach_absolute_time();
-       this->recursiveBind(context, forceLazysBound, neverUnload);
-
-       uint64_t t4 = mach_absolute_time();
-       if ( !context.linkingMainExecutable )
-               this->weakBind(context);
-       uint64_t t5 = mach_absolute_time();     
+       __block uint64_t t2, t3, t4, t5;
+       {
+               dyld3::ScopedTimer(DBG_DYLD_TIMING_APPLY_FIXUPS, 0, 0, 0);
+               t2 = mach_absolute_time();
+               this->recursiveRebase(context);
+               context.notifyBatch(dyld_image_state_rebased, false);
+
+               t3 = mach_absolute_time();
+               if ( !context.linkingMainExecutable )
+                       this->recursiveBindWithAccounting(context, forceLazysBound, neverUnload);
+
+               t4 = mach_absolute_time();
+               if ( !context.linkingMainExecutable )
+                       this->weakBind(context);
+               t5 = mach_absolute_time();
+       }
 
-       context.notifyBatch(dyld_image_state_bound, false);
+    if ( !context.linkingMainExecutable )
+        context.notifyBatch(dyld_image_state_bound, false);
        uint64_t t6 = mach_absolute_time();     
 
        std::vector<DOFInfo> dofs;
@@ -450,9 +532,10 @@ void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool pr
 
        // interpose any dynamically loaded images
        if ( !context.linkingMainExecutable && (fgInterposingTuples.size() != 0) ) {
+               dyld3::ScopedTimer timer(DBG_DYLD_TIMING_APPLY_INTERPOSING, 0, 0, 0);
                this->recursiveApplyInterposing(context);
        }
-       
+
        // clear error strings
        (*context.setErrorStrings)(0, NULL, NULL, NULL);
 
@@ -626,10 +709,16 @@ void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool prefli
                        }
                        try {
                                unsigned cacheIndex;
-                               dependentLib = context.loadLibrary(requiredLibInfo.name, true, this->getPath(), &thisRPaths, cacheIndex);
+                               bool enforceIOSMac = false;
+               #if __MAC_OS_X_VERSION_MIN_REQUIRED
+                               const dyld3::MachOFile* mf = (dyld3::MachOFile*)this->machHeader();
+                               if ( mf->supportsPlatform(dyld3::Platform::iOSMac) && !mf->supportsPlatform(dyld3::Platform::macOS) )
+                                       enforceIOSMac = true;
+               #endif
+                               dependentLib = context.loadLibrary(requiredLibInfo.name, true, this->getPath(), &thisRPaths, enforceIOSMac, cacheIndex);
                                if ( dependentLib == this ) {
                                        // found circular reference, perhaps DYLD_LIBARY_PATH is causing this rdar://problem/3684168 
-                                       dependentLib = context.loadLibrary(requiredLibInfo.name, false, NULL, NULL, cacheIndex);
+                                       dependentLib = context.loadLibrary(requiredLibInfo.name, false, NULL, NULL, enforceIOSMac, cacheIndex);
                                        if ( dependentLib != this )
                                                dyld::warn("DYLD_ setting caused circular dependency in %s\n", this->getPath());
                                }
@@ -650,7 +739,8 @@ void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool prefli
                                }
                                // check found library version is compatible
                                // <rdar://problem/89200806> 0xFFFFFFFF is wildcard that matches any version
-                               if ( (requiredLibInfo.info.minVersion != 0xFFFFFFFF) && (actualInfo.minVersion < requiredLibInfo.info.minVersion) ) {
+                               if ( (requiredLibInfo.info.minVersion != 0xFFFFFFFF) && (actualInfo.minVersion < requiredLibInfo.info.minVersion)
+                                               && ((dyld3::MachOFile*)(dependentLib->machHeader()))->enforceCompatVersion() ) {
                                        // record values for possible use by CrashReporter or Finder
                                        dyld::throwf("Incompatible library version: %s requires version %d.%d.%d or later, but %s provides version %d.%d.%d",
                                                        this->getShortName(), requiredLibInfo.info.minVersion >> 16, (requiredLibInfo.info.minVersion >> 8) & 0xff, requiredLibInfo.info.minVersion & 0xff,
@@ -784,7 +874,11 @@ void ImageLoader::recursiveApplyInterposing(const LinkContext& context)
        }
 }
 
-
+void ImageLoader::recursiveBindWithAccounting(const LinkContext& context, bool forceLazysBound, bool neverUnload)
+{
+       this->recursiveBind(context, forceLazysBound, neverUnload);
+       vmAccountingSetSuspended(context, false);
+}
 
 void ImageLoader::recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload)
 {
@@ -823,6 +917,23 @@ void ImageLoader::recursiveBind(const LinkContext& context, bool forceLazysBound
        }
 }
 
+
+// These are mangled symbols for all the variants of operator new and delete
+// which a main executable can define (non-weak) and override the
+// weak-def implementation in the OS.
+static const char* sTreatAsWeak[] = {
+    "__Znwm", "__ZnwmRKSt9nothrow_t",
+    "__Znam", "__ZnamRKSt9nothrow_t",
+    "__ZdlPv", "__ZdlPvRKSt9nothrow_t", "__ZdlPvm",
+    "__ZdaPv", "__ZdaPvRKSt9nothrow_t", "__ZdaPvm",
+    "__ZnwmSt11align_val_t", "__ZnwmSt11align_val_tRKSt9nothrow_t",
+    "__ZnamSt11align_val_t", "__ZnamSt11align_val_tRKSt9nothrow_t",
+    "__ZdlPvSt11align_val_t", "__ZdlPvSt11align_val_tRKSt9nothrow_t", "__ZdlPvmSt11align_val_t",
+    "__ZdaPvSt11align_val_t", "__ZdaPvSt11align_val_tRKSt9nothrow_t", "__ZdaPvmSt11align_val_t"
+};
+
+
+
 void ImageLoader::weakBind(const LinkContext& context)
 {
        if ( context.verboseWeakBind )
@@ -929,15 +1040,43 @@ void ImageLoader::weakBind(const LinkContext& context)
                                                }
                                        }
                                }
-                               
+
                        }
                }
-               
+
+#if __arm64e__
+               for (int i=0; i < count; ++i) {
+                       if ( imagesNeedingCoalescing[i]->usesChainedFixups() ) {
+                               // during binding of references to weak-def symbols, the dyld cache was patched
+                               // but if main executable has non-weak override of operator new or delete it needs is handled here
+                               if ( !imagesNeedingCoalescing[i]->weakSymbolsBound(imageIndexes[i]) ) {
+                                       for (const char* weakSymbolName : sTreatAsWeak) {
+                                               const ImageLoader* dummy;
+                                               imagesNeedingCoalescing[i]->resolveWeak(context, weakSymbolName, true, false, &dummy);
+                                       }
+                               }
+                       }
+                       else {
+                               // look for weak def symbols in this image which may override the cache
+                               ImageLoader::CoalIterator coaler;
+                               imagesNeedingCoalescing[i]->initializeCoalIterator(coaler, i, 0);
+                               imagesNeedingCoalescing[i]->incrementCoalIterator(coaler);
+                               while ( !coaler.done ) {
+                                       imagesNeedingCoalescing[i]->incrementCoalIterator(coaler);
+                                       const ImageLoader* dummy;
+                                       // a side effect of resolveWeak() is to patch cache
+                                       imagesNeedingCoalescing[i]->resolveWeak(context, coaler.symbolName, true, false, &dummy);
+                               }
+                       }
+               }
+#endif
+
                // mark all as having all weak symbols bound
                for(int i=0; i < count; ++i) {
                        imagesNeedingCoalescing[i]->setWeakSymbolsBound(imageIndexes[i]);
                }
        }
+
        uint64_t t2 = mach_absolute_time();
        fgTotalWeakBindTime += t2  - t1;
        
@@ -1075,9 +1214,9 @@ static void printTime(const char* msg, uint64_t partTime, uint64_t totalTime)
        static uint64_t sUnitsPerSecond = 0;
        if ( sUnitsPerSecond == 0 ) {
                struct mach_timebase_info timeBaseInfo;
-               if ( mach_timebase_info(&timeBaseInfo) == KERN_SUCCESS ) {
-                       sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer;
-               }
+               if ( mach_timebase_info(&timeBaseInfo) != KERN_SUCCESS )
+                       return;
+               sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer;
        }
        if ( partTime < sUnitsPerSecond ) {
                uint32_t milliSecondsTimesHundred = (uint32_t)((partTime*100000)/sUnitsPerSecond);
@@ -1333,7 +1472,7 @@ intptr_t ImageLoader::read_sleb128(const uint8_t*& p, const uint8_t* end)
        } while (byte & 0x80);
        // sign extend negative numbers
        if ( (byte & 0x40) != 0 )
-               result |= (-1LL) << bit;
+               result |= (~0ULL) << bit;
        return (intptr_t)result;
 }
 
index fd70e2d42c67caa2db33d42f43fcf63c797d585a..c0113b141b45035ebe425b40a5b40718d2bf663a 100644 (file)
@@ -39,6 +39,7 @@
 #include <TargetConditionals.h>
 #include <vector>
 #include <new>
+#include <uuid/uuid.h>
 
 #if __arm__
  #include <mach/vm_page_size.h>
@@ -65,6 +66,7 @@
 
 #include "mach-o/dyld_images.h"
 #include "mach-o/dyld_priv.h"
+#include "DyldSharedCache.h"
 
 #if __i386__
        #define SHARED_REGION_BASE SHARED_REGION_BASE_I386
        #define SUPPORT_CLASSIC_MACHO                   __arm__
        #define SUPPORT_ZERO_COST_EXCEPTIONS    (!__USING_SJLJ_EXCEPTIONS__)
        #define INITIAL_IMAGE_COUNT                             150
-       #define SUPPORT_ACCELERATE_TABLES               (__arm__ || __arm64__)
+       #define SUPPORT_ACCELERATE_TABLES               !TARGET_IPHONE_SIMULATOR
        #define SUPPORT_ROOT_PATH                               TARGET_IPHONE_SIMULATOR
+       #define USES_CHAINED_BINDS                              (__arm64e__)
 #else
        #define SPLIT_SEG_SHARED_REGION_SUPPORT 0
        #define SPLIT_SEG_DYLIB_SUPPORT                 __i386__
        #define INITIAL_IMAGE_COUNT                             200
        #define SUPPORT_ACCELERATE_TABLES               0
        #define SUPPORT_ROOT_PATH                               1
+       #define USES_CHAINED_BINDS                              0
 #endif
 
 #define MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE (32*1024)
 
 #define MH_HAS_OBJC                    0x40000000
 
+
 // <rdar://problem/13590567> optimize away dyld's initializers
 #define VECTOR_NEVER_DESTRUCTED(type) \
        namespace std { \
@@ -256,12 +261,14 @@ public:
 
                void addTime(const char* name, uint64_t time);
        };
+
+       typedef void (^CoalesceNotifier)(const Symbol* implSym, const ImageLoader* implIn, const mach_header* implMh);
        
        struct LinkContext {
-               ImageLoader*    (*loadLibrary)(const char* libraryName, bool search, const char* origin, const RPathChain* rpaths, unsigned& cacheIndex);
+               ImageLoader*    (*loadLibrary)(const char* libraryName, bool search, const char* origin, const RPathChain* rpaths, bool enforceIOSMac, unsigned& cacheIndex);
                void                    (*terminationRecorder)(ImageLoader* image);
                bool                    (*flatExportFinder)(const char* name, const Symbol** sym, const ImageLoader** image);
-               bool                    (*coalescedExportFinder)(const char* name, const Symbol** sym, const ImageLoader** image);
+               bool                    (*coalescedExportFinder)(const char* name, const Symbol** sym, const ImageLoader** image, CoalesceNotifier);
                unsigned int    (*getCoalescedImages)(ImageLoader* images[], unsigned imageIndex[]);
                void                    (*undefinedHandler)(const char* name);
                MappedRegion*   (*getAllMappedRegions)(MappedRegion*);
@@ -296,17 +303,23 @@ public:
                const char*             progname;
                ProgramVars             programVars;
                ImageLoader*    mainExecutable;
-               const char*             imageSuffix;
+               const char* const * imageSuffix;
 #if SUPPORT_ROOT_PATH
                const char**    rootPaths;
 #endif
+               const DyldSharedCache*  dyldCache;
                const dyld_interpose_tuple*     dynamicInterposeArray;
                size_t                  dynamicInterposeCount;
                PrebindMode             prebindUsage;
                SharedRegionMode sharedRegionMode;
                bool                    dyldLoadedAtSameAddressNeededBySharedCache;
                bool                    strictMachORequired;
-               bool                    requireCodeSignature;
+               bool                    allowAtPaths;
+               bool                    allowEnvVarsPrint;
+               bool                    allowEnvVarsPath;
+               bool                    allowEnvVarsSharedCache;
+               bool                    allowClassicFallbackPaths;
+               bool                    allowInsertFailures;
                bool                    mainExecutableCodeSigned;
                bool                    preFetchDisabled;
                bool                    prebinding;
@@ -314,8 +327,7 @@ public:
                bool                    linkingMainExecutable;
                bool                    startedInitializingMainExecutable;
 #if __MAC_OS_X_VERSION_MIN_REQUIRED
-               bool                    processIsRestricted;
-               bool                    processUsingLibraryValidation;
+               bool                    marzipan;
 #endif
                bool                    verboseOpts;
                bool                    verboseEnv;
@@ -417,7 +429,7 @@ public:
        bool                                                            leaveMapped() { return fLeaveMapped; }
 
                                                                                // image resides in dyld shared cache
-       virtual bool                                            inSharedCache() const = 0;
+       virtual bool                                            inSharedCache() const { return false; };
 
                                                                                // checks if the specifed address is within one of this image's segments
        virtual bool                                            containsAddress(const void* addr) const;
@@ -434,11 +446,11 @@ public:
                                                                                // st_mtime from stat() on file
        time_t                                                          lastModified() const;
 
-                                                                               // only valid for main executables, returns a pointer its entry point from LC_UNIXTHREAD
-       virtual void*                                           getThreadPC() const = 0;
+                                                                               // only valid for main executables, returns a pointer its entry point from LC_MAIN
+       virtual void*                                           getEntryFromLC_MAIN() const = 0;
        
-                                                                               // only valid for main executables, returns a pointer its main from LC_<MAIN
-       virtual void*                                           getMain() const = 0;
+                                                                               // only valid for main executables, returns a pointer its main from LC_UNIXTHREAD
+       virtual void*                                           getEntryFromLC_UNIXTHREAD() const = 0;
        
                                                                                // dyld API's require each image to have an associated mach_header
        virtual const struct mach_header*   machHeader() const = 0;
@@ -542,6 +554,9 @@ public:
                                                                                // fills in info about __eh_frame and __unwind_info sections
        virtual void                                            getUnwindInfo(dyld_unwind_sections* info) = 0;
 
+                                                                               // given a pointer into an image, find which segment and section it is in
+       virtual const struct macho_section* findSection(const void* imageInterior) const = 0;
+
                                                                                // given a pointer into an image, find which segment and section it is in
        virtual bool                                            findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset) = 0;
        
@@ -571,6 +586,10 @@ public:
                                                                                // Image has objc sections, so information objc about when it comes and goes
        virtual bool                                            notifyObjC() const { return false; }
 
+       virtual bool                                            overridesCachedDylib(uint32_t& num) const { return false; }
+       virtual void                                            setOverridesCachedDylib(uint32_t num) { }
+
+
 //
 // A segment is a chunk of an executable file that is mapped into memory.  
 //
@@ -595,11 +614,16 @@ public:
        virtual uint32_t                                        minOSVersion() const = 0;
        
                                                                                // if the image contains interposing functions, register them
-       virtual void                                            registerInterposing() = 0;
+       virtual void                                            registerInterposing(const LinkContext& context) = 0;
+
+       virtual bool                                            usesChainedFixups() const { return false; }
+
 
                                                                                // when resolving symbols look in subImage if symbol can't be found
        void                                                            reExport(ImageLoader* subImage);
-       
+
+       virtual void                                            recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload);
+       virtual void                                            recursiveBindWithAccounting(const LinkContext& context, bool forceLazysBound, bool neverUnload);
        void                                                            weakBind(const LinkContext& context);
 
        void                                                            applyInterposing(const LinkContext& context);
@@ -629,7 +653,9 @@ public:
        
        bool                                                            isReferencedDownward() { return fIsReferencedDownward; }
 
-       
+       virtual uintptr_t                                       resolveWeak(const LinkContext& context, const char* symbolName, bool weak_import, bool runResolver,
+                                                                                                       const ImageLoader** foundIn) { return 0; } 
+
                                                                                // triggered by DYLD_PRINT_STATISTICS to write info on work done and how fast
        static void                                                     printStatistics(unsigned int imageCount, const InitializerTimingList& timingInfo);
        static void                                                     printStatisticsDetails(unsigned int imageCount, const InitializerTimingList& timingInfo);
@@ -647,6 +673,8 @@ public:
        static bool                                                     haveInterposingTuples() { return !fgInterposingTuples.empty(); }
        static void                                                     clearInterposingTuples() { fgInterposingTuples.clear(); }
 
+       static void                                                     applyInterposingToDyldCache(const LinkContext& context);
+
                        bool                                            dependsOn(ImageLoader* image);
                        
                        void                                            setPath(const char* path);
@@ -676,6 +704,8 @@ public:
        static uintptr_t read_uleb128(const uint8_t*& p, const uint8_t* end);
        static intptr_t read_sleb128(const uint8_t*& p, const uint8_t* end);
 
+       void                    vmAccountingSetSuspended(const LinkContext& context, bool suspend);
+
 protected:                     
        // abstract base class so all constructors protected
                                        ImageLoader(const char* path, unsigned int libCount); 
@@ -723,7 +753,6 @@ protected:
        virtual void            recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath);
        virtual unsigned        recursiveUpdateDepth(unsigned int maxDepth);
        virtual void            recursiveRebase(const LinkContext& context);
-       virtual void            recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload);
        virtual void            recursiveApplyInterposing(const LinkContext& context);
        virtual void            recursiveGetDOFSections(const LinkContext& context, std::vector<DOFInfo>& dofs);
        virtual void            recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize,
@@ -806,12 +835,13 @@ public:
        static uint64_t                         fgTotalObjCSetupTime;
        static uint64_t                         fgTotalDebuggerPausedTime;
        static uint64_t                         fgTotalRebindCacheTime;
-protected:
        static uint64_t                         fgTotalRebaseTime;
        static uint64_t                         fgTotalBindTime;
        static uint64_t                         fgTotalWeakBindTime;
        static uint64_t                         fgTotalDOF;
        static uint64_t                         fgTotalInitTime;
+
+protected:
        static std::vector<InterposeTuple>      fgInterposingTuples;
        
        const char*                                     fPath;
index 1becea3418b4e79ea5c02192339e8007015e3d24..f63769bce1b24faae113159ae297e50cb18cc266 100644 (file)
 #include <stdint.h>
 #include <System/sys/codesign.h>
 
+#if __has_feature(ptrauth_calls)
+#include <ptrauth.h>
+#endif
+
 #include "ImageLoaderMachO.h"
 #include "ImageLoaderMachOCompressed.h"
 #if SUPPORT_CLASSIC_MACHO
@@ -106,13 +110,7 @@ extern "C" long __stack_chk_guard;
        #define TOOL_LD         3
 #endif
 
-
-
-#if TARGET_IPHONE_SIMULATOR
-       #define LIBSYSTEM_DYLIB_PATH "/usr/lib/libSystem.dylib"
-#else
-       #define LIBSYSTEM_DYLIB_PATH "/usr/lib/libSystem.B.dylib"
-#endif
+#define LIBSYSTEM_DYLIB_PATH "/usr/lib/libSystem.B.dylib"
 
 // relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables
 #if __LP64__
@@ -147,7 +145,7 @@ fSegmentsCount(segCount), fIsSplitSeg(false), fInSharedCache(false),
        fReadOnlyImportSegment(false),
 #endif
        fHasSubLibraries(false), fHasSubUmbrella(false), fInUmbrella(false), fHasDOFSections(false), fHasDashInit(false),
-       fHasInitializers(false), fHasTerminators(false), fNotifyObjC(false), fRetainForObjC(false), fRegisteredAsRequiresCoalescing(false)
+       fHasInitializers(false), fHasTerminators(false), fNotifyObjC(false), fRetainForObjC(false), fRegisteredAsRequiresCoalescing(false), fOverrideOfCacheImageNum(0)
 {
        fIsSplitSeg = ((mh->flags & MH_SPLIT_SEGS) != 0);        
 
@@ -383,7 +381,8 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat
                        case LC_VERSION_MIN_WATCHOS:
                        case LC_VERSION_MIN_TVOS:
                        case LC_VERSION_MIN_IPHONEOS:
-                               throw "mach-o, but built for simulator (not macOS)";
+                               if ( !context.marzipan )
+                                       throw "mach-o, but built for simulator (not macOS)";
                                break;
 #endif
                }
@@ -673,8 +672,11 @@ void ImageLoaderMachO::parseLoadCmds(const LinkContext& context)
        for(unsigned int i=0; i < fSegmentsCount; ++i) {
                // set up pointer to __LINKEDIT segment
                if ( strcmp(segName(i),"__LINKEDIT") == 0 ) {
-                       if ( context.requireCodeSignature && (segFileOffset(i) > fCoveredCodeLength))
+       #if !__MAC_OS_X_VERSION_MIN_REQUIRED
+                       // <rdar://problem/42419336> historically, macOS never did this check
+                       if ( segFileOffset(i) > fCoveredCodeLength )
                                dyld::throwf("cannot load '%s' (segment outside of code signature)", this->getShortName());
+       #endif
                        fLinkEditBase = (uint8_t*)(segActualLoadAddress(i) - segFileOffset(i));
                }
 #if TEXT_RELOC_SUPPORT
@@ -1134,24 +1136,22 @@ void ImageLoaderMachO::setSlide(intptr_t slide)
 
 void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* codeSigCmd, int fd,  uint64_t offsetInFatFile, const LinkContext& context)
 {
+       dyld3::ScopedTimer(DBG_DYLD_TIMING_ATTACH_CODESIGNATURE, 0, 0, 0);
        // if dylib being loaded has no code signature load command
        if ( codeSigCmd == NULL) {
-               if (context.requireCodeSignature ) {
-                       // if we require dylibs to be codesigned there needs to be a signature.
-                       dyld::throwf("required code signature missing for '%s'\n", this->getPath());
-               } else {
-                       disableCoverageCheck();
-               }
+               disableCoverageCheck();
        }
        else {
 #if __MAC_OS_X_VERSION_MIN_REQUIRED
                // <rdar://problem/13622786> ignore code signatures in binaries built with pre-10.9 tools
                if ( this->sdkVersion() < DYLD_MACOSX_VERSION_10_9 ) {
+                       disableCoverageCheck();
                        return;
                }
 #endif
+
                fsignatures_t siginfo;
-               siginfo.fs_file_start=offsetInFatFile;                          // start of mach-o slice in fat file 
+               siginfo.fs_file_start=offsetInFatFile;                          // start of mach-o slice in fat file
                siginfo.fs_blob_start=(void*)(long)(codeSigCmd->dataoff);       // start of CD in mach-o file
                siginfo.fs_blob_size=codeSigCmd->datasize;                      // size of CD
                int result = fcntl(fd, F_ADDFILESIGS_RETURN, &siginfo);
@@ -1169,28 +1169,26 @@ void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* cod
                if ( result == -1 ) {
                        if ( (errno == EPERM) || (errno == EBADEXEC) )
                                dyld::throwf("code signature invalid for '%s'\n", this->getPath());
-                       if ( context.verboseCodeSignatures ) 
+                       if ( context.verboseCodeSignatures )
                                dyld::log("dyld: Failed registering code signature for %s, errno=%d\n", this->getPath(), errno);
                        siginfo.fs_file_start = UINT64_MAX;
                } else if ( context.verboseCodeSignatures )  {
                        dyld::log("dyld: Registered code signature for %s\n", this->getPath());
                }
                fCoveredCodeLength = siginfo.fs_file_start;
+       }
 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-               if ( context.processUsingLibraryValidation ) {
-                       fchecklv checkInfo;
-                       char  messageBuffer[512];
-                       messageBuffer[0] = '\0';
-                       checkInfo.lv_file_start = offsetInFatFile;
-                       checkInfo.lv_error_message_size = sizeof(messageBuffer);
-                       checkInfo.lv_error_message = messageBuffer;
-                       int res = fcntl(fd, F_CHECK_LV, &checkInfo);
-                       if ( res == -1 ) {
-                               dyld::throwf("code signature in (%s) not valid for use in process using Library Validation: %s", this->getPath(), messageBuffer);
-                       }
+       {
+               fchecklv checkInfo;
+               char  messageBuffer[512];
+               messageBuffer[0] = '\0';
+               checkInfo.lv_file_start = offsetInFatFile;
+               checkInfo.lv_error_message_size = sizeof(messageBuffer);
+               checkInfo.lv_error_message = messageBuffer;
+               int res = fcntl(fd, F_CHECK_LV, &checkInfo);
+               if ( res == -1 ) {
+                       dyld::throwf("code signature in (%s) not valid for use in process using Library Validation: %s", this->getPath(), messageBuffer);
                }
-#endif
        }
 }
 
@@ -1204,25 +1202,17 @@ void ImageLoaderMachO::validateFirstPages(const struct linkedit_data_command* co
        }
 #endif
        if (codeSigCmd != NULL) {
-               void *fdata = xmmap(NULL, lenFileData, PROT_READ | PROT_EXEC, MAP_SHARED, fd, offsetInFat);
+               void *fdata = xmmap(NULL, lenFileData, PROT_READ, MAP_SHARED, fd, offsetInFat);
                if ( fdata == MAP_FAILED ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-                       if ( context.processUsingLibraryValidation ) {
-                               dyld::throwf("cannot load image with wrong team ID in process using Library Validation");
-                       }
-                       else
-#endif
-                       {
-                               int errnoCopy = errno;
-                               if ( errnoCopy == EPERM ) {
-                                       if ( dyld::sandboxBlockedMmap(getPath()) )
-                                               dyld::throwf("file system sandbox blocked mmap() of '%s'", getPath());
-                                       else
-                                               dyld::throwf("code signing blocked mmap() of '%s'", getPath());
-                               }
+                       int errnoCopy = errno;
+                       if ( errnoCopy == EPERM ) {
+                               if ( dyld::sandboxBlockedMmap(getPath()) )
+                                       dyld::throwf("file system sandbox blocked mmap() of '%s'", getPath());
                                else
-                                       dyld::throwf("mmap() errno=%d validating first page of '%s'", errnoCopy, getPath());
+                                       dyld::throwf("code signing blocked mmap() of '%s'", getPath());
                        }
+                       else
+                               dyld::throwf("mmap() errno=%d validating first page of '%s'", errnoCopy, getPath());
                }
                if ( memcmp(fdata, fileData, lenFileData) != 0 )
                        dyld::throwf("mmap() page compare failed for '%s'", getPath());
@@ -1240,7 +1230,7 @@ const char* ImageLoaderMachO::getInstallPath() const
        return NULL;
 }
 
-void ImageLoaderMachO::registerInterposing()
+void ImageLoaderMachO::registerInterposing(const LinkContext& context)
 {
        // mach-o files advertise interposing by having a __DATA __interpose section
        struct InterposeData { uintptr_t replacement; uintptr_t replacee; };
@@ -1349,7 +1339,7 @@ uint32_t ImageLoaderMachO::minOSVersion() const
 }
 
 
-void* ImageLoaderMachO::getThreadPC() const
+void* ImageLoaderMachO::getEntryFromLC_MAIN() const
 {
        const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
        const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
@@ -1370,36 +1360,33 @@ void* ImageLoaderMachO::getThreadPC() const
 }
 
 
-void* ImageLoaderMachO::getMain() const
+void* ImageLoaderMachO::getEntryFromLC_UNIXTHREAD() const
 {
        const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
        const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
        const struct load_command* cmd = cmds;
        for (uint32_t i = 0; i < cmd_count; ++i) {
-               switch (cmd->cmd) {
-                       case LC_UNIXTHREAD:
-                       {
-                       #if __i386__
-                               const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16);
-                               void* entry = (void*)(registers->eip + fSlide);
-                       #elif __x86_64__
-                               const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16);
-                               void* entry = (void*)(registers->rip + fSlide);
-                       #elif __arm__
-                               const arm_thread_state_t* registers = (arm_thread_state_t*)(((char*)cmd) + 16);
-                               void* entry = (void*)(registers->__pc + fSlide);
-                       #elif __arm64__
-                               const arm_thread_state64_t* registers = (arm_thread_state64_t*)(((char*)cmd) + 16);
-                               void* entry = (void*)(registers->__pc + fSlide);
-                       #else
-                               #warning need processor specific code
-                       #endif
-                               // <rdar://problem/8543820&9228031> verify entry point is in image
-                               if ( this->containsAddress(entry) ) {
-                                       return entry;
-                               }
-                       }
-                       break;
+               if ( cmd->cmd == LC_UNIXTHREAD ) {
+       #if __i386__
+                       const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16);
+                       void* entry = (void*)(registers->eip + fSlide);
+                       // <rdar://problem/8543820&9228031> verify entry point is in image
+                       if ( this->containsAddress(entry) )
+                               return entry;
+       #elif __x86_64__
+                       const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16);
+                       void* entry = (void*)(registers->rip + fSlide);
+                       // <rdar://problem/8543820&9228031> verify entry point is in image
+                       if ( this->containsAddress(entry) )
+                               return entry;
+       #elif __arm64__ && !__arm64e__
+                       // temp support until <rdar://39514191> is fixed
+                       const uint64_t* regs64 = (uint64_t*)(((char*)cmd) + 16);
+                       void* entry = (void*)(regs64[32] + fSlide); // arm_thread_state64_t.__pc
+                       // <rdar://problem/8543820&9228031> verify entry point is in image
+                       if ( this->containsAddress(entry) )
+                               return entry;
+       #endif
                }
                cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
        }
@@ -1514,12 +1501,10 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vector<const c
                                const char* pathToAdd = NULL;
                                const char* path = (char*)cmd + ((struct rpath_command*)cmd)->path.offset;
                                if ( (strncmp(path, "@loader_path", 12) == 0) && ((path[12] == '/') || (path[12] == '\0')) ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-                                       if ( context.processIsRestricted && (context.mainExecutable == this) ) {
+                                       if ( !context.allowAtPaths && (context.mainExecutable == this) ) {
                                                dyld::warn("LC_RPATH %s in %s being ignored in restricted program because of @loader_path\n", path, this->getPath());
                                                break;
                                        }
-#endif
                                        char resolvedPath[PATH_MAX];
                                        if ( realpath(this->getPath(), resolvedPath) != NULL ) {
                                                char newRealPath[strlen(resolvedPath) + strlen(path)];
@@ -1532,12 +1517,10 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vector<const c
                                        }
                                }
                                else if ( (strncmp(path, "@executable_path", 16) == 0) && ((path[16] == '/') || (path[16] == '\0')) ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-                                       if ( context.processIsRestricted ) {
+                                       if ( !context.allowAtPaths) {
                                                dyld::warn("LC_RPATH %s in %s being ignored in restricted program because of @executable_path\n", path, this->getPath());
                                                break;
                                        }
-#endif
                                        char resolvedPath[PATH_MAX];
                                        if ( realpath(context.mainExecutable->getPath(), resolvedPath) != NULL ) {
                                                char newRealPath[strlen(resolvedPath) + strlen(path)];
@@ -1549,12 +1532,10 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vector<const c
                                                }
                                        }
                                }
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-                               else if ( (path[0] != '/') && context.processIsRestricted ) {
+                               else if ( (path[0] != '/') && !context.allowAtPaths) {
                                        dyld::warn("LC_RPATH %s in %s being ignored in restricted program because it is a relative path\n", path, this->getPath());
                                        break;
                                }
-#endif
 #if SUPPORT_ROOT_PATH
                                else if ( (path[0] == '/') && (context.rootPaths != NULL) ) {
                                        // <rdar://problem/5869973> DYLD_ROOT_PATH should apply to LC_RPATH rpaths
@@ -1880,7 +1861,7 @@ bool ImageLoaderMachO::findSection(const mach_header* mh, const char* segmentNam
 }
 
 
-bool ImageLoaderMachO::findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset)
+const macho_section* ImageLoaderMachO::findSection(const void* imageInterior) const
 {
        const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
        const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
@@ -1896,13 +1877,7 @@ bool ImageLoaderMachO::findSection(const void* imageInterior, const char** segme
                                                const struct macho_section* const sectionsEnd = &sectionsStart[seg->nsects];
                                                for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
                                                        if ((sect->addr <= unslidInteriorAddress) && (unslidInteriorAddress < (sect->addr+sect->size))) {
-                                                               if ( segmentName != NULL )
-                                                                       *segmentName = sect->segname;
-                                                               if ( sectionName != NULL )
-                                                                       *sectionName = sect->sectname;
-                                                               if ( sectionOffset != NULL )
-                                                                       *sectionOffset = unslidInteriorAddress - sect->addr;
-                                                               return true;
+                                                               return sect;
                                                        }
                                                }
                                        }
@@ -1911,6 +1886,22 @@ bool ImageLoaderMachO::findSection(const void* imageInterior, const char** segme
                }
                cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
        }
+       return nullptr;
+}
+
+
+bool ImageLoaderMachO::findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset)
+{
+       if (const struct macho_section* sect = findSection(imageInterior)) {
+               const uintptr_t unslidInteriorAddress = (uintptr_t)imageInterior - this->getSlide();
+               if ( segmentName != NULL )
+                       *segmentName = sect->segname;
+               if ( sectionName != NULL )
+                       *sectionName = sect->sectname;
+               if ( sectionOffset != NULL )
+                       *sectionOffset = unslidInteriorAddress - sect->addr;
+               return true;
+       }
        return false;
 }
 
@@ -1979,24 +1970,29 @@ const void* ImageLoaderMachO::getEnd() const
        return (const void*)lastAddress;
 }
 
+uintptr_t ImageLoaderMachO::bindLocation(const LinkContext& context, uintptr_t baseVMAddress,
+                                         uintptr_t location, uintptr_t value,
+                                         uint8_t type, const char* symbolName,
+                                         intptr_t addend, const char* inPath, const char* toPath, const char* msg,
+                                         ExtraBindData *extraBindData, uintptr_t slide)
+{
+    auto logBind = [&]() {
+        if ( !context.verboseBind )
+            return;
+        if ( addend != 0 ) {
+            dyld::log("dyld: %sbind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX + %ld\n",
+                      msg, shortName(inPath), (uintptr_t)location,
+                      ((toPath != NULL) ? shortName(toPath) : "<missing weak_import>"),
+                      symbolName, (uintptr_t)location, value, addend);
+        } else {
+            dyld::log("dyld: %sbind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX\n",
+                      msg, shortName(inPath), (uintptr_t)location,
+                      ((toPath != NULL) ? shortName(toPath) : "<missing weak_import>"),
+                      symbolName, (uintptr_t)location, value);
+        }
+    };
+
 
-uintptr_t ImageLoaderMachO::bindLocation(const LinkContext& context, uintptr_t location, uintptr_t value, 
-                                                                               uint8_t type, const char* symbolName,
-                                                                               intptr_t addend, const char* inPath, const char* toPath, const char* msg)
-{
-       // log
-       if ( context.verboseBind ) {
-               if ( addend != 0 )
-                       dyld::log("dyld: %sbind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX + %ld\n",
-                                               msg, shortName(inPath), (uintptr_t)location,
-                                               ((toPath != NULL) ? shortName(toPath) : "<missing weak_import>"),
-                                               symbolName, (uintptr_t)location, value, addend);
-               else
-                       dyld::log("dyld: %sbind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX\n",
-                                               msg, shortName(inPath), (uintptr_t)location,
-                                               ((toPath != NULL) ? shortName(toPath) : "<missing weak_import>"),
-                                                symbolName, (uintptr_t)location, value);
-       }
 #if LOG_BINDINGS
 //     dyld::logBindings("%s: %s\n", targetImage->getShortName(), symbolName);
 #endif
@@ -2008,22 +2004,45 @@ uintptr_t ImageLoaderMachO::bindLocation(const LinkContext& context, uintptr_t l
        uint32_t value32;
        switch (type) {
                case BIND_TYPE_POINTER:
+            logBind();
                        // test first so we don't needless dirty pages
                        if ( *locationToFix != newValue )
                                *locationToFix = newValue;
                        break;
-               case BIND_TYPE_TEXT_ABSOLUTE32:
+        case BIND_TYPE_TEXT_ABSOLUTE32:
+            logBind();
                        loc32 = (uint32_t*)locationToFix;
                        value32 = (uint32_t)newValue;
                        if ( *loc32 != value32 )
                                *loc32 = value32;
                        break;
-               case BIND_TYPE_TEXT_PCREL32:
+        case BIND_TYPE_TEXT_PCREL32:
+            logBind();
                        loc32 = (uint32_t*)locationToFix;
                        value32 = (uint32_t)(newValue - (((uintptr_t)locationToFix) + 4));
                        if ( *loc32 != value32 )
                                *loc32 = value32;
+            break;
+        case BIND_TYPE_THREADED_BIND:
+            logBind();
+            // test first so we don't needless dirty pages
+            if ( *locationToFix != newValue )
+                *locationToFix = newValue;
+            break;
+        case BIND_TYPE_THREADED_REBASE: {
+            // Regular pointer which needs to fit in 51-bits of value.
+            // C++ RTTI uses the top bit, so we'll allow the whole top-byte
+            // and the signed-extended bottom 43-bits to be fit in to 51-bits.
+            uint64_t top8Bits = *locationToFix & 0x0007F80000000000ULL;
+            uint64_t bottom43Bits = *locationToFix & 0x000007FFFFFFFFFFULL;
+            uint64_t targetValue = ( top8Bits << 13 ) | (((intptr_t)(bottom43Bits << 21) >> 21) & 0x00FFFFFFFFFFFFFF);
+            newValue = (uintptr_t)(targetValue + slide);
+            if ( context.verboseRebase ) {
+                dyld::log("dyld: rebase: %s:*0x%08lX += 0x%08lX = 0x%08lX\n", shortName(inPath), (uintptr_t)locationToFix, slide, newValue);
+            }
+            *locationToFix = newValue;
                        break;
+        }
                default:
                        dyld::throwf("bad bind type %d", type);
        }
@@ -2082,6 +2101,24 @@ void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context)
                                                                        dd->dyldLazyBinder = (void*)&stub_binding_helper;
                                                        }
                                #endif // !__arm64__
+                                                       // <rdar://problem/40352925> Add work around for existing apps that have deprecated __dyld section
+                                                       const char* installNm = this->getInstallPath();
+                                                       if ( (mh->filetype != MH_DYLIB) || (installNm == NULL) || (strcmp(installNm, "/usr/lib/system/libdyld.dylib") != 0) ) {
+                                               #if TARGET_OS_OSX
+                                                               // don't allow macOS apps build with 10.14 or later SDK and targeting 10.8 or later to have a __dyld section
+                                                               if ( (minOSVersion() >= 0x000a0800) && (sdkVersion() >= 0x000a0e00) )
+                                                                       dyld::throwf("__dyld section not supported in %s", this->getPath());
+                                               #endif
+                                               #if TARGET_OS_IOS || TARGET_OS_TV
+                                                               // don't allow iOS apps build with 12.0 or later SDK to have a __dyld section
+                                                               if ( sdkVersion() >= 0x000c0000 )
+                                                                       dyld::throwf("__dyld section not supported in %s", this->getPath());
+                                               #endif
+                                               #if TARGET_OS_WATCH
+                                                               if ( sdkVersion() >= 0x00050000 )
+                                                                       dyld::throwf("__dyld section not supported in %s", this->getPath());
+                                               #endif
+                                                       }
                                                        if ( sect->size > offsetof(DATAdyld, dyldFuncLookup) ) {
                                                                if ( dd->dyldFuncLookup != (void*)&_dyld_func_lookup )
                                                                        dd->dyldFuncLookup = (void*)&_dyld_func_lookup;
@@ -2101,7 +2138,7 @@ void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context)
                                                                        // match what crt1.o supplies, then the program has a custom entry point.
                                                                        // This means it might be doing something that needs to be executed before 
                                                                        // initializers are run. 
-                                                                       if ( memcmp(this->getMain(), sStandardEntryPointInstructions, 16) != 0 ) {
+                                                                       if ( memcmp(this->getEntryFromLC_UNIXTHREAD(), sStandardEntryPointInstructions, 16) != 0 ) {
                                                                                if ( context.verboseInit )
                                                                                        dyld::log("dyld: program uses non-standard entry point so delaying running of initializers\n");
                                                                                context.setRunInitialzersOldWay();
@@ -2171,7 +2208,10 @@ bool ImageLoaderMachO::usablePrebinding(const LinkContext& context) const
        // if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind
        if ( ((this->isPrebindable() && (this->getSlide() == 0)) || fInSharedCache)
                && this->usesTwoLevelNameSpace()
-               && this->allDependentLibrariesAsWhenPreBound() ) {
+#if !USES_CHAINED_BINDS
+               && this->allDependentLibrariesAsWhenPreBound()
+#endif
+                ) {
                // allow environment variables to disable prebinding
                if ( context.bindFlat )
                        return false;
@@ -2189,6 +2229,14 @@ bool ImageLoaderMachO::usablePrebinding(const LinkContext& context) const
        return false;
 }
 
+static void *stripPointer(void *ptr) {
+#if __has_feature(ptrauth_calls)
+    return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
+#else
+    return ptr;
+#endif
+}
+
 
 void ImageLoaderMachO::doImageInit(const LinkContext& context)
 {
@@ -2200,8 +2248,11 @@ void ImageLoaderMachO::doImageInit(const LinkContext& context)
                        switch (cmd->cmd) {
                                case LC_ROUTINES_COMMAND:
                                        Initializer func = (Initializer)(((struct macho_routines_command*)cmd)->init_address + fSlide);
+#if __has_feature(ptrauth_calls)
+                                       func = (Initializer)__builtin_ptrauth_sign_unauthenticated((void*)func, ptrauth_key_asia, 0);
+#endif
                                        // <rdar://problem/8543820&9228031> verify initializers are in image
-                                       if ( ! this->containsAddress((void*)func) ) {
+                                       if ( ! this->containsAddress(stripPointer((void*)func)) ) {
                                                dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath());
                                        }
                                        if ( ! dyld::gProcessInfo->libSystemInitialized ) {
@@ -2210,9 +2261,10 @@ void ImageLoaderMachO::doImageInit(const LinkContext& context)
                                        }
                                        if ( context.verboseInit )
                                                dyld::log("dyld: calling -init function %p in %s\n", func, this->getPath());
-                    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);
-                    });
+                                       {
+                                               dyld3::ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)fMachOData, (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);
@@ -2242,7 +2294,7 @@ void ImageLoaderMachO::doModInitFunctions(const LinkContext& context)
                                                for (size_t j=0; j < count; ++j) {
                                                        Initializer func = inits[j];
                                                        // <rdar://problem/8543820&9228031> verify initializers are in image
-                                                       if ( ! this->containsAddress((void*)func) ) {
+                                                       if ( ! this->containsAddress(stripPointer((void*)func)) ) {
                                                                dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath());
                                                        }
                                                        if ( ! dyld::gProcessInfo->libSystemInitialized ) {
@@ -2254,9 +2306,10 @@ 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);
-                            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);
-                            });
+                                                       {
+                                                               dyld3::ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)fMachOData, (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
@@ -2354,8 +2407,11 @@ void ImageLoaderMachO::doTermination(const LinkContext& context)
                                                const size_t count = sect->size / sizeof(uintptr_t);
                                                for (size_t j=count; j > 0; --j) {
                                                        Terminator func = terms[j-1];
+#if __has_feature(ptrauth_calls)
+                                                       func = (Terminator)__builtin_ptrauth_sign_unauthenticated((void*)func, ptrauth_key_asia, 0);
+#endif
                                                        // <rdar://problem/8543820&9228031> verify terminators are in image
-                                                       if ( ! this->containsAddress((void*)func) ) {
+                                                       if ( ! this->containsAddress(stripPointer((void*)func)) ) {
                                                                dyld::throwf("termination function %p not in mapped image for %s\n", func, this->getPath());
                                                        }
                                                        if ( context.verboseInit )
@@ -2481,9 +2537,7 @@ void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInF
                uintptr_t requestedLoadAddress = segPreferredLoadAddress(i) + slide;
                int protection = 0;
                if ( !segUnaccessible(i) ) {
-                       // If has text-relocs, don't set x-bit initially.
-                       // Instead set it later after text-relocs have been done.
-                       if ( segExecutable(i) && !(segHasRebaseFixUps(i) && (slide != 0)) )
+                       if ( segExecutable(i) )
                                protection   |= PROT_EXEC;
                        if ( segReadable(i) )
                                protection   |= PROT_READ;
@@ -2582,12 +2636,13 @@ void ImageLoaderMachO::segProtect(unsigned int segIndex, const ImageLoader::Link
        }
 }      
 
+#if TEXT_RELOC_SUPPORT
 void ImageLoaderMachO::segMakeWritable(unsigned int segIndex, const ImageLoader::LinkContext& context)
 {
        vm_address_t addr = segActualLoadAddress(segIndex);
        vm_size_t size = segSize(segIndex);
        const bool setCurrentPermissions = false;
-       vm_prot_t protection = VM_PROT_WRITE | VM_PROT_READ;
+       vm_prot_t protection = VM_PROT_WRITE | VM_PROT_READ | VM_PROT_COPY;
        if ( segExecutable(segIndex) && !segHasRebaseFixUps(segIndex) )
                protection |= VM_PROT_EXECUTE;
        kern_return_t r = vm_protect(mach_task_self(), addr, size, setCurrentPermissions, protection);
@@ -2600,7 +2655,7 @@ void ImageLoaderMachO::segMakeWritable(unsigned int segIndex, const ImageLoader:
                        (protection & PROT_READ) ? 'r' : '.',  (protection & PROT_WRITE) ? 'w' : '.',  (protection & PROT_EXEC) ? 'x' : '.' );
        }
 }
-
+#endif
 
 const char* ImageLoaderMachO::findClosestSymbol(const mach_header* mh, const void* addr, const void** closestAddr)
 {
@@ -2790,3 +2845,12 @@ uintptr_t ImageLoaderMachO::segPreferredAddress(const mach_header* mh, unsigned
 
 
 
+uintptr_t ImageLoaderMachO::imageBaseAddress() const {
+    //printf("imageBaseAddress: %s %d->%d\n", getPath(), 0, segmentCount());
+    for (unsigned int i = 0, e = segmentCount(); i != e; ++i) {
+        if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) )
+            return segPreferredLoadAddress(i);
+    }
+    return 0;
+}
+
index 5c485892fe101ee9bfd853a5efc457c50b125344..db90ff1af62a857510bece332e70284778472666 100644 (file)
 
 #include <stdint.h> 
 #include <mach-o/loader.h> 
-#include <mach-o/nlist.h> 
+#include <mach-o/nlist.h>
+#include <uuid/uuid.h>
+
+#if __has_feature(ptrauth_calls)
+#include <ptrauth.h>
+#endif
 
 #include "ImageLoader.h"
 #include "mach-o/dyld_images.h"
 
+#define BIND_TYPE_THREADED_BIND 100
+
+
+#define BIND_TYPE_THREADED_REBASE 102
+
 
 //
 // ImageLoaderMachO is a subclass of ImageLoader which loads mach-o format files.
@@ -51,8 +61,8 @@ public:
        void                                                            disableCoverageCheck() { fCoveredCodeLength = UINT64_MAX; }
 
        const char*                                                     getInstallPath() const;
-       virtual void*                                           getMain() const;
-       virtual void*                                           getThreadPC() const;
+       virtual void*                                           getEntryFromLC_UNIXTHREAD() const;
+       virtual void*                                           getEntryFromLC_MAIN() const;
        virtual const struct mach_header*   machHeader() const;
        virtual uintptr_t                                       getSlide() const;
        virtual const void*                                     getEnd() const;
@@ -85,6 +95,7 @@ public:
        virtual bool                                            needsInitialization();
        virtual bool                                            getSectionContent(const char* segmentName, const char* sectionName, void** start, size_t* length);
        virtual void                                            getUnwindInfo(dyld_unwind_sections* info);
+    virtual const struct macho_section* findSection(const void* imageInterior) const;
        virtual bool                                            findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset);
        virtual bool                                            usablePrebinding(const LinkContext& context) const;
        virtual unsigned int                            segmentCount() const;
@@ -101,11 +112,14 @@ public:
        virtual uintptr_t                                       segActualLoadAddress(unsigned int) const;
        virtual uintptr_t                                       segPreferredLoadAddress(unsigned int) const;
        virtual uintptr_t                                       segActualEndAddress(unsigned int) const;
-       virtual void                                            registerInterposing();
+       virtual void                                            registerInterposing(const LinkContext& context);
        virtual uint32_t                                        sdkVersion() const;
        virtual uint32_t                                        minOSVersion() const;
        virtual const char*                                     libPath(unsigned int) const;
        virtual bool                                            notifyObjC() const { return fNotifyObjC; }
+       virtual bool                                            overridesCachedDylib(uint32_t& num) const { num = fOverrideOfCacheImageNum; return (num != 0); }
+       virtual void                                            setOverridesCachedDylib(uint32_t num) { fOverrideOfCacheImageNum = num; }
+
 
        static void                                                     printStatisticsDetails(unsigned int imageCount, const InitializerTimingList&);
        static uint32_t                                         minOSVersion(const mach_header*);
@@ -117,9 +131,34 @@ public:
        static bool                                                     getLazyBindingInfo(uint32_t& lazyBindingInfoOffset, const uint8_t* lazyInfoStart, const uint8_t* lazyInfoEnd,
                                                                                                                  uint8_t* segIndex, uintptr_t* segOffset, int* ordinal, const char** symbolName, bool* doneAfterBind);
        static uintptr_t                                        segPreferredAddress(const mach_header* mh, unsigned segIndex);
-       static uintptr_t                                        bindLocation(const LinkContext& context, uintptr_t location, uintptr_t value, 
-                                                                                                       uint8_t type, const char* symbolName,
-                                                                                                       intptr_t addend, const char* inPath, const char* toPath, const char* msg);
+
+    uintptr_t                           imageBaseAddress() const;
+
+    struct ExtraBindData {
+        ExtraBindData() = default;
+        explicit ExtraBindData(uint64_t d) : data(d) { }
+
+        union {
+            uint64_t data = 0;
+        };
+        bool operator==(const ExtraBindData& other) const {
+            return this->data == other.data;
+        }
+        bool operator!=(const ExtraBindData& other) const {
+            return !(*this == other);
+        }
+        bool operator<(const ExtraBindData& other) const {
+            return data < other.data;
+        }
+
+    };
+
+       static uintptr_t                                        bindLocation(const LinkContext& context, uintptr_t baseVMAddress,
+                                                     uintptr_t location, uintptr_t value,
+                                                     uint8_t type, const char* symbolName,
+                                                     intptr_t addend, const char* inPath, const char* toPath, const char* msg,
+                                                     ExtraBindData *extraBindData,
+                                                     uintptr_t fSlide);
        virtual void                                            rebase(const LinkContext& context, uintptr_t slide) = 0;
 
 
@@ -239,7 +278,8 @@ protected:
                                                                                        fHasTerminators : 1,
                                                                                        fNotifyObjC : 1,
                                                                                        fRetainForObjC : 1,
-                                                                                       fRegisteredAsRequiresCoalescing : 1;    // <rdar://problem/7886402> Loading MH_DYLIB_STUB causing coalescable miscount
+                                                                                       fRegisteredAsRequiresCoalescing : 1,    // <rdar://problem/7886402> Loading MH_DYLIB_STUB causing coalescable miscount
+                                                                                       fOverrideOfCacheImageNum : 12;
 
                                                                                        
        static uint32_t                                 fgSymbolTableBinarySearchs;
index 86723e72d04d94c9c70dfaaba8cfab93c991d0d2..70068cdd12b7dd3c672269ae008cf385dad1b68e 100644 (file)
@@ -601,11 +601,13 @@ bool ImageLoaderMachOClassic::isSubframeworkOf(const LinkContext& context, const
                                                        return true;
                                                if ( context.imageSuffix != NULL ) {
                                                        // when DYLD_IMAGE_SUFFIX is used, lastSlash string needs imageSuffix removed from end
-                                                       char reexportAndSuffix[strlen(context.imageSuffix)+strlen(exportThruName)+1];
-                                                       strcpy(reexportAndSuffix, exportThruName);
-                                                       strcat(reexportAndSuffix, context.imageSuffix);
-                                                       if ( strcmp(&lastSlash[1], reexportAndSuffix) == 0 )
-                                                               return true;
+                                                       for(const char* const* suffix = context.imageSuffix; *suffix != NULL; ++suffix) {
+                                                               char reexportAndSuffix[strlen(*suffix)+strlen(exportThruName)+1];
+                                                               strcpy(reexportAndSuffix, exportThruName);
+                                                               strcat(reexportAndSuffix, *suffix);
+                                                               if ( strcmp(&lastSlash[1], reexportAndSuffix) == 0 )
+                                                                       return true;
+                                                       }
                                                }
                                        }
                                }
@@ -647,11 +649,13 @@ bool ImageLoaderMachOClassic::hasSubLibrary(const LinkContext& context, const Im
                                                                        return true;
                                                                if ( context.imageSuffix != NULL ) {
                                                                        // when DYLD_IMAGE_SUFFIX is used, childLeafName string needs imageSuffix removed from end
-                                                                       char aSubLibNameAndSuffix[strlen(context.imageSuffix)+strlen(aSubLibName)+1];
-                                                                       strcpy(aSubLibNameAndSuffix, aSubLibName);
-                                                                       strcat(aSubLibNameAndSuffix, context.imageSuffix);
-                                                                       if ( strcmp(aSubLibNameAndSuffix, childLeafName) == 0 )
-                                                                               return true;
+                                                                       for(const char* const* suffix = context.imageSuffix; *suffix != NULL; ++suffix) {
+                                                                               char aSubLibNameAndSuffix[strlen(*suffix)+strlen(aSubLibName)+1];
+                                                                               strcpy(aSubLibNameAndSuffix, aSubLibName);
+                                                                               strcat(aSubLibNameAndSuffix, *suffix);
+                                                                               if ( strcmp(aSubLibNameAndSuffix, childLeafName) == 0 )
+                                                                                       return true;
+                                                                       }
                                                                }
                                                        }
                                                        break;
@@ -680,11 +684,13 @@ bool ImageLoaderMachOClassic::hasSubLibrary(const LinkContext& context, const Im
                                                                        return true;
                                                                if ( context.imageSuffix != NULL ) {
                                                                        // when DYLD_IMAGE_SUFFIX is used, lastSlash string needs imageSuffix removed from end
-                                                                       char umbrellaAndSuffix[strlen(context.imageSuffix)+strlen(aSubUmbrellaName)+1];
-                                                                       strcpy(umbrellaAndSuffix, aSubUmbrellaName);
-                                                                       strcat(umbrellaAndSuffix, context.imageSuffix);
-                                                                       if ( strcmp(umbrellaAndSuffix, &lastSlash[1]) == 0 )
-                                                                               return true;
+                                                                       for(const char* const* suffix = context.imageSuffix; *suffix != NULL; ++suffix) {
+                                                                               char umbrellaAndSuffix[strlen(*suffix)+strlen(aSubUmbrellaName)+1];
+                                                                               strcpy(umbrellaAndSuffix, aSubUmbrellaName);
+                                                                               strcat(umbrellaAndSuffix, *suffix);
+                                                                               if ( strcmp(umbrellaAndSuffix, &lastSlash[1]) == 0 )
+                                                                                       return true;
+                                                                       }
                                                                }
                                                        }
                                                        break;
@@ -1083,7 +1089,7 @@ uintptr_t ImageLoaderMachOClassic::resolveUndefined(const LinkContext& context,
                // symbol requires searching images with coalesced symbols (not done during prebinding)
                if ( !context.prebinding && !dontCoalesce && (symbolIsWeakReference(undefinedSymbol) || symbolIsWeakDefinition(undefinedSymbol)) ) {
                        const Symbol* sym;
-                       if ( context.coalescedExportFinder(symbolName, &sym, foundIn) ) {
+                       if ( context.coalescedExportFinder(symbolName, &sym, foundIn, nullptr) ) {
                                if ( *foundIn != this )
                                        context.addDynamicReference(this, const_cast<ImageLoader*>(*foundIn));
                                return (*foundIn)->getExportedSymbolAddress(sym, context, this);
@@ -1643,7 +1649,7 @@ void ImageLoaderMachOClassic::updateUsesCoalIterator(CoalIterator& it, uintptr_t
                        if ( reloc->r_pcrel ) 
                                type = BIND_TYPE_TEXT_PCREL32;
                #endif
-                       this->bindLocation(context, (uintptr_t)location, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ");
+                       this->bindLocation(context, this->imageBaseAddress(), (uintptr_t)location, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ", NULL, fSlide);
                        boundSomething = true;
                }
        }
index 627d572220b305d12b8938777dffeee75525faaf..a908c915aa986625dd76c831cdb486cda99d452e 100644 (file)
 #include <sys/fcntl.h>
 #include <sys/stat.h> 
 #include <sys/param.h>
-#include <sys/sysctl.h>
 #include <mach/mach.h>
 #include <mach/thread_status.h>
 #include <mach-o/loader.h> 
 #include "ImageLoaderMachOCompressed.h"
 #include "mach-o/dyld_images.h"
+#include "Closure.h"
+#include "Array.h"
 
 #ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
        #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE                       0x02
 #endif
 
+
+#ifndef BIND_OPCODE_THREADED
+#define BIND_OPCODE_THREADED    0xD0
+#endif
+
+#ifndef BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB
+#define BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB       0x00
+#endif
+
+#ifndef BIND_SUBOPCODE_THREADED_APPLY
+#define BIND_SUBOPCODE_THREADED_APPLY                                                          0x01
+#endif
+
+
+#ifndef BIND_SPECIAL_DYLIB_WEAK_LOOKUP
+#define BIND_SPECIAL_DYLIB_WEAK_LOOKUP                                                         -3
+#endif
+
+#ifndef CPU_SUBTYPE_ARM64_E
+       #define CPU_SUBTYPE_ARM64_E    2
+#endif
+
 // relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables
 #if __LP64__
        #define RELOC_SIZE 3
        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
@@ -489,7 +507,7 @@ void ImageLoaderMachOCompressed::rebase(const LinkContext& context, uintptr_t sl
                                        fgTotalRebaseFixups += count;
                                        break;
                                default:
-                                       dyld::throwf("bad rebase opcode %d", *p);
+                                       dyld::throwf("bad rebase opcode %d", *(p-1));
                        }
                }
        }
@@ -662,6 +680,95 @@ uintptr_t ImageLoaderMachOCompressed::resolveFlat(const LinkContext& context, co
 }
 
 
+#if USES_CHAINED_BINDS
+static void patchCacheUsesOf(const ImageLoader::LinkContext& context, const dyld3::closure::Image* overriddenImage,
+                                                        uint32_t cacheOffsetOfImpl, const char* symbolName, uintptr_t newImpl)
+{
+       uintptr_t cacheStart = (uintptr_t)context.dyldCache;
+       overriddenImage->forEachPatchableUseOfExport(cacheOffsetOfImpl, ^(dyld3::closure::Image::PatchableExport::PatchLocation patchLocation) {
+               uintptr_t* loc = (uintptr_t*)(cacheStart+patchLocation.cacheOffset);
+#if __has_feature(ptrauth_calls)
+               if ( patchLocation.authenticated ) {
+                        dyld3::MachOLoaded::ChainedFixupPointerOnDisk fixupInfo;
+                       fixupInfo.authRebase.auth      = true;
+                       fixupInfo.authRebase.addrDiv   = patchLocation.usesAddressDiversity;
+                       fixupInfo.authRebase.diversity = patchLocation.discriminator;
+                       fixupInfo.authRebase.key       = patchLocation.key;
+                       uintptr_t newValue = fixupInfo.signPointer(loc, newImpl + patchLocation.getAddend());
+                       if ( *loc != newValue ) {
+                               *loc = newValue;
+                               if ( context.verboseBind )
+                                       dyld::log("dyld: cache fixup: *%p = %p (JOP: diversity 0x%04X, addr-div=%d, key=%s) to %s\n",
+                                                       loc, (void*)newValue, patchLocation.discriminator, patchLocation.usesAddressDiversity, patchLocation.keyName(), symbolName);
+                       }
+                       return;
+               }
+#endif
+               uintptr_t newValue =newImpl + patchLocation.getAddend();
+               if ( *loc != newValue ) {
+                       *loc = newValue;
+                       if ( context.verboseBind )
+                               dyld::log("dyld: cache fixup: *%p = %p to %s\n", loc, (void*)newValue, symbolName);
+               }
+       });
+}
+#endif
+
+
+uintptr_t ImageLoaderMachOCompressed::resolveWeak(const LinkContext& context, const char* symbolName, bool weak_import,
+                                                                                                 bool runResolver, const ImageLoader** foundIn)
+{
+       const Symbol* sym;
+#if USES_CHAINED_BINDS
+       __block uintptr_t foundOutsideCache  = 0;
+       __block uintptr_t lastFoundInCache = 0;
+       CoalesceNotifier notifier = ^(const Symbol* implSym, const ImageLoader* implIn, const mach_header* implMh) {
+               //dyld::log("notifier: found %s in %p %s\n", symbolName, implMh, implIn->getPath());
+               // This block is only called in dyld2 mode when a non-cached image is search for which weak-def implementation to use
+               // As a side effect of that search we notice any implementations outside and inside the cache,
+               // and use that to trigger patching the cache to use the implementation outside the cache.
+               uintptr_t implAddr = implIn->getExportedSymbolAddress(implSym, context, nullptr, false, symbolName);
+               if ( ((dyld3::MachOLoaded*)implMh)->inDyldCache() ) {
+                       if ( foundOutsideCache != 0 ) {
+                               // have an implementation in cache and and earlier one not in the cache, patch cache to use earlier one
+                               lastFoundInCache = implAddr;
+                               uint32_t imageIndex;
+                               if ( context.dyldCache->findMachHeaderImageIndex(implMh, imageIndex) ) {
+                                       const dyld3::closure::Image* overriddenImage = context.dyldCache->cachedDylibsImageArray()->imageForNum(imageIndex+1);
+                                       uint32_t cacheOffsetOfImpl = (uint32_t)((uintptr_t)implAddr - (uintptr_t)context.dyldCache);
+                                       patchCacheUsesOf(context, overriddenImage, cacheOffsetOfImpl, symbolName, foundOutsideCache);
+                               }
+                       }
+               }
+               else {
+                       // record first non-cache implementation
+                       if ( foundOutsideCache == 0 )
+                               foundOutsideCache = implAddr;
+               }
+       };
+#else
+       CoalesceNotifier notifier = nullptr;
+#endif
+       if ( context.coalescedExportFinder(symbolName, &sym, foundIn, notifier) ) {
+               if ( *foundIn != this )
+                       context.addDynamicReference(this, const_cast<ImageLoader*>(*foundIn));
+               return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver);
+       }
+       // if a bundle is loaded privately the above will not find its exports
+       if ( this->isBundle() && this->hasHiddenExports() ) {
+               // look in self for needed symbol
+               sym = this->findShallowExportedSymbol(symbolName, foundIn);
+               if ( sym != NULL )
+                       return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver);
+       }
+       if ( weak_import ) {
+               // definition can't be found anywhere, ok because it is weak, just return 0
+               return 0;
+       }
+       throwSymbolNotFound(context, symbolName, this->getPath(), "", "weak");
+}
+
+
 uintptr_t ImageLoaderMachOCompressed::resolveTwolevel(const LinkContext& context, const char* symbolName, const ImageLoader* definedInImage,
                                                                                                          const ImageLoader* requestorImage, unsigned requestorOrdinalOfDef, bool weak_import, bool runResolver,
                                                                                                          const ImageLoader** foundIn)
@@ -718,6 +825,9 @@ uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const
        if ( context.bindFlat || (libraryOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP) ) {
                symbolAddress = this->resolveFlat(context, symbolName, weak_import, runResolver, targetImage);
        }
+       else if ( libraryOrdinal == BIND_SPECIAL_DYLIB_WEAK_LOOKUP ) {
+               symbolAddress = this->resolveWeak(context, symbolName, false, runResolver, targetImage);
+       }
        else {
                if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) {
                        *targetImage = context.mainExecutable;
@@ -763,18 +873,24 @@ uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const
        return symbolAddress;
 }
 
-uintptr_t ImageLoaderMachOCompressed::bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, 
-                                                               uint8_t symbolFlags, intptr_t addend, long libraryOrdinal, const char* msg,
-                                                               LastLookup* last, bool runResolver)
+uintptr_t ImageLoaderMachOCompressed::bindAt(const LinkContext& context, ImageLoaderMachOCompressed* image,
+                                                                                        uintptr_t addr, uint8_t type, const char* symbolName,
+                                                                                        uint8_t symbolFlags, intptr_t addend, long libraryOrdinal,
+                                                                                        ExtraBindData *extraBindData,
+                                                                                        const char* msg, LastLookup* last, bool runResolver)
 {
        const ImageLoader*      targetImage;
        uintptr_t                       symbolAddress;
        
        // resolve symbol
-       symbolAddress = this->resolve(context, symbolName, symbolFlags, libraryOrdinal, &targetImage, last, runResolver);
+    if (type == BIND_TYPE_THREADED_REBASE) {
+        symbolAddress = 0;
+        targetImage = nullptr;
+    } else
+        symbolAddress = image->resolve(context, symbolName, symbolFlags, libraryOrdinal, &targetImage, last, runResolver);
 
        // do actual update
-       return this->bindLocation(context, addr, symbolAddress, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, msg);
+       return image->bindLocation(context, image->imageBaseAddress(), addr, symbolAddress, type, symbolName, addend, image->getPath(), targetImage ? targetImage->getPath() : NULL, msg, extraBindData, image->fSlide);
 }
 
 
@@ -795,6 +911,21 @@ void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLa
        // note: flat-namespace binaries need to have imports rebound (even if correctly prebound)
        if ( this->usablePrebinding(context) ) {
                // don't need to bind
+               // except weak which may now be inline with the regular binds
+               if (this->participatesInCoalescing()) {
+                       // run through all binding opcodes
+                       eachBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image,
+                                                               uintptr_t addr, uint8_t type, const char* symbolName,
+                                                               uint8_t symbolFlags, intptr_t addend, long libraryOrdinal,
+                                                               ExtraBindData *extraBindData,
+                                                               const char* msg, LastLookup* last, bool runResolver) {
+                               if ( libraryOrdinal != BIND_SPECIAL_DYLIB_WEAK_LOOKUP )
+                                       return (uintptr_t)0;
+                               return ImageLoaderMachOCompressed::bindAt(ctx, image, addr, type, symbolName, symbolFlags,
+                                                                                                                 addend, libraryOrdinal, extraBindData,
+                                                                                                                 msg, last, runResolver);
+                       });
+               }
        }
        else {
                uint64_t t0 = mach_absolute_time();
@@ -804,9 +935,21 @@ void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLa
                if ( fTextSegmentBinds ) 
                        this->makeTextSegmentWritable(context, true);
        #endif
-       
+
+               uint32_t ignore;
+               bool bindingBecauseOfRoot = ( this->overridesCachedDylib(ignore) || this->inSharedCache() );
+               vmAccountingSetSuspended(context, bindingBecauseOfRoot);
+
                // run through all binding opcodes
-               eachBind(context, &ImageLoaderMachOCompressed::bindAt);
+               eachBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image,
+                                                       uintptr_t addr, uint8_t type, const char* symbolName,
+                                                       uint8_t symbolFlags, intptr_t addend, long libraryOrdinal,
+                                                       ExtraBindData *extraBindData,
+                                                       const char* msg, LastLookup* last, bool runResolver) {
+                       return ImageLoaderMachOCompressed::bindAt(ctx, image, addr, type, symbolName, symbolFlags,
+                                                                                                         addend, libraryOrdinal, extraBindData,
+                                                                                                         msg, last, runResolver);
+               });
                        
        #if TEXT_RELOC_SUPPORT
                // if there were __TEXT fixups, restore write protection
@@ -824,7 +967,7 @@ void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLa
         // the stub may have been altered to point to a shared lazy pointer.
                if ( fInSharedCache ) 
                        this->updateOptimizedLazyPointers(context);
-       
+
                // tell kernel we are done with chunks of LINKEDIT
                if ( !context.preFetchDisabled ) 
                        this->markFreeLINKEDIT(context);
@@ -832,6 +975,23 @@ void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLa
                uint64_t t1 = mach_absolute_time();
                ImageLoader::fgTotalRebindCacheTime += (t1-t0);
        }
+
+#if USES_CHAINED_BINDS
+       // See if this dylib overrides something in the dyld cache
+       uint32_t dyldCacheOverrideImageNum;
+       if ( context.dyldCache && overridesCachedDylib(dyldCacheOverrideImageNum) ) {
+               // need to patch all other places in cache that point to the overridden dylib, to point to this dylib instead
+               const dyld3::closure::Image* overriddenImage = context.dyldCache->cachedDylibsImageArray()->imageForNum(dyldCacheOverrideImageNum);
+               overriddenImage->forEachPatchableExport(^(uint32_t cacheOffsetOfImpl, const char* exportName) {
+                       uintptr_t newImpl = 0;
+                       const ImageLoader* foundIn;
+                       if ( const ImageLoader::Symbol* sym = this->findShallowExportedSymbol(exportName, &foundIn) ) {
+                               newImpl = foundIn->getExportedSymbolAddress(sym, context, this);
+                       }
+                       patchCacheUsesOf(context, overriddenImage, cacheOffsetOfImpl, exportName, newImpl);
+               });
+       }
+#endif
        
        // set up dyld entry points in image
        // do last so flat main executables will have __dyld or __program_vars set up
@@ -842,49 +1002,179 @@ void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLa
 
 void ImageLoaderMachOCompressed::doBindJustLazies(const LinkContext& context)
 {
-       eachLazyBind(context, &ImageLoaderMachOCompressed::bindAt);
+       eachLazyBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image,
+                                                       uintptr_t addr, uint8_t type, const char* symbolName,
+                                                       uint8_t symbolFlags, intptr_t addend, long libraryOrdinal,
+                                                       ExtraBindData *extraBindData,
+                                                       const char* msg, LastLookup* last, bool runResolver) {
+               return ImageLoaderMachOCompressed::bindAt(ctx, image, addr, type, symbolName, symbolFlags,
+                                                                                                 addend, libraryOrdinal, extraBindData,
+                                                                                                 msg, last, runResolver);
+       });
 }
 
-#if __arm__ || __arm64__
-int ImageLoaderMachOCompressed::vmAccountingSetSuspended(bool suspend, const LinkContext& context)
+void ImageLoaderMachOCompressed::registerInterposing(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);
+       // mach-o files advertise interposing by having a __DATA __interpose section
+       struct InterposeData { uintptr_t replacement; uintptr_t replacee; };
+       const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
+       const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
+       const struct load_command* cmd = cmds;
+       for (uint32_t i = 0; i < cmd_count; ++i) {
+               switch (cmd->cmd) {
+                       case LC_SEGMENT_COMMAND:
+                       {
+                               const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
+                               const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
+                               const struct macho_section* const sectionsEnd = &sectionsStart[seg->nsects];
+                               for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+                                       if ( ((sect->flags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(sect->sectname, "__interpose") == 0) && (strcmp(seg->segname, "__DATA") == 0)) ) {
+                                               // <rdar://problem/23929217> Ensure section is within segment
+                                               if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) )
+                                                       dyld::throwf("interpose section has malformed address range for %s\n", this->getPath());
+                                               __block uintptr_t sectionStart = sect->addr + fSlide;
+                                               __block uintptr_t sectionEnd = sectionStart + sect->size;
+                                               const size_t count = sect->size / sizeof(InterposeData);
+                                               InterposeData interposeArray[count];
+                                               // Note, we memcpy here as rebases may have already been applied.
+                                               memcpy(&interposeArray[0], (void*)sectionStart, sect->size);
+                                               __block InterposeData *interposeArrayStart = &interposeArray[0];
+                                               eachBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image, uintptr_t addr, uint8_t type,
+                                                                                       const char* symbolName, uint8_t symbolFlags, intptr_t addend, long libraryOrdinal,
+                                                                                       ExtraBindData *extraBindData,
+                                                                                       const char* msg, LastLookup* last, bool runResolver) {
+                                                       if (addr >= sectionStart && addr < sectionEnd) {
+                                                               if ( context.verboseInterposing ) {
+                                                                       dyld::log("dyld: interposing %s at 0x%lx in range 0x%lx..0x%lx\n",
+                                                                                         symbolName, addr, sectionStart, sectionEnd);
+                                                               }
+                                                               const ImageLoader*      targetImage;
+                                                               uintptr_t                       symbolAddress;
+
+                                                               // resolve symbol
+                                                               if (type == BIND_TYPE_THREADED_REBASE) {
+                                                                       symbolAddress = 0;
+                                                                       targetImage = nullptr;
+                                                               } else
+                                                                       symbolAddress = image->resolve(ctx, symbolName, symbolFlags, libraryOrdinal, &targetImage, last, runResolver);
+
+                                                               uintptr_t newValue = symbolAddress+addend;
+                                                               uintptr_t index = (addr - sectionStart) / sizeof(uintptr_t);
+                                                               switch (type) {
+                                                                       case BIND_TYPE_POINTER:
+                                                                               ((uintptr_t*)interposeArrayStart)[index] = newValue;
+                                                                               break;
+                                                                       case BIND_TYPE_TEXT_ABSOLUTE32:
+                                                                               // unreachable!
+                                                                               abort();
+                                                                       case BIND_TYPE_TEXT_PCREL32:
+                                                                               // unreachable!
+                                                                               abort();
+                                                                       case BIND_TYPE_THREADED_BIND:
+                                                                               ((uintptr_t*)interposeArrayStart)[index] = newValue;
+                                                                               break;
+                                                                       case BIND_TYPE_THREADED_REBASE: {
+                                                                               // Regular pointer which needs to fit in 51-bits of value.
+                                                                               // C++ RTTI uses the top bit, so we'll allow the whole top-byte
+                                                                               // and the signed-extended bottom 43-bits to be fit in to 51-bits.
+                                                                               uint64_t top8Bits = (*(uint64_t*)addr) & 0x0007F80000000000ULL;
+                                                                               uint64_t bottom43Bits = (*(uint64_t*)addr) & 0x000007FFFFFFFFFFULL;
+                                                                               uint64_t targetValue = ( top8Bits << 13 ) | (((intptr_t)(bottom43Bits << 21) >> 21) & 0x00FFFFFFFFFFFFFF);
+                                                                               newValue = (uintptr_t)(targetValue + fSlide);
+                                                                               ((uintptr_t*)interposeArrayStart)[index] = newValue;
+                                                                               break;
+                                                                       }
+                                                                       default:
+                                                                               dyld::throwf("bad bind type %d", type);
+                                                               }
+                                                       }
+                                                       return (uintptr_t)0;
+                                               });
+                                               for (size_t j=0; j < count; ++j) {
+                                                       ImageLoader::InterposeTuple tuple;
+                                                       tuple.replacement        = interposeArray[j].replacement;
+                                                       tuple.neverImage        = this;
+                                                       tuple.onlyImage            = NULL;
+                                                       tuple.replacee            = interposeArray[j].replacee;
+                                                       if ( context.verboseInterposing ) {
+                                                               dyld::log("dyld: interposing index %d 0x%lx with 0x%lx\n",
+                                                                                 (unsigned)j, interposeArray[j].replacee, interposeArray[j].replacement);
+                                                       }
+                                                       // <rdar://problem/25686570> ignore interposing on a weak function that does not exist
+                                                       if ( tuple.replacee == 0 )
+                                                               continue;
+                                                       // <rdar://problem/7937695> verify that replacement is in this image
+                                                       if ( this->containsAddress((void*)tuple.replacement) ) {
+                                                               // chain to any existing interpositions
+                                                               for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) {
+                                                                       if ( it->replacee == tuple.replacee ) {
+                                                                               tuple.replacee = it->replacement;
+                                                                       }
+                                                               }
+                                                               ImageLoader::fgInterposingTuples.push_back(tuple);
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+                               break;
+               }
+               cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+       }
 }
+
+bool ImageLoaderMachOCompressed::usesChainedFixups() const
+{
+#if __arm64e__
+       return ( machHeader()->cpusubtype == CPU_SUBTYPE_ARM64_E );
+#else
+       return false;
 #endif
+}
+
+struct ThreadedBindData {
+    ThreadedBindData(const char* symbolName, int64_t addend, long libraryOrdinal, uint8_t symbolFlags, uint8_t type)
+    : symbolName(symbolName), addend(addend), libraryOrdinal(libraryOrdinal), symbolFlags(symbolFlags), type(type) { }
+
+    std::tuple<const char*, int64_t, long, bool, uint8_t> pack() const {
+        return std::make_tuple(symbolName, addend, libraryOrdinal, symbolFlags, type);
+    }
+
+    const char* symbolName     = nullptr;
+    int64_t addend             = 0;
+    long libraryOrdinal        = 0;
+    uint8_t symbolFlags        = 0;
+    uint8_t type               = 0;
+};
 
 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;
+    try {
+        const dysymtab_command* dynSymbolTable = NULL;
+        const macho_nlist* symbolTable = NULL;
+        const char* symbolTableStrings = NULL;
+        uint32_t maxStringOffset = 0;
+
+        const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
+        const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
+        const struct load_command* cmd = cmds;
+        for (uint32_t i = 0; i < cmd_count; ++i) {
+            switch (cmd->cmd) {
+                case LC_SYMTAB:
+                {
+                    const struct symtab_command* symtab = (struct symtab_command*)cmd;
+                    symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff];
+                    maxStringOffset = symtab->strsize;
+                    symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]);
+                }
+                    break;
+                case LC_DYSYMTAB:
+                    dynSymbolTable = (struct dysymtab_command*)cmd;
+                    break;
             }
+            cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
         }
-        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;
                uintptr_t address = segActualLoadAddress(0);
@@ -897,7 +1187,11 @@ void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handl
                intptr_t addend = 0;
                uintptr_t count;
                uintptr_t skip;
-               uintptr_t segOffset;
+        uintptr_t segOffset = 0;
+
+               dyld3::OverflowSafeArray<ThreadedBindData> ordinalTable;
+        bool useThreadedRebaseBind = false;
+        ExtraBindData extraBindData;
                LastLookup last = { 0, 0, NULL, 0, NULL };
                const uint8_t* const start = fLinkEditBase + fDyldInfo->bind_off;
                const uint8_t* const end = &start[fDyldInfo->bind_size];
@@ -964,16 +1258,21 @@ void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handl
                                        address += read_uleb128(p, end);
                                        break;
                                case BIND_OPCODE_DO_BIND:
-                                       if ( (address < segmentStartAddress) || (address >= segmentEndAddress) )
-                                               throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
-                                       if ( symbolName  == NULL )
-                                               dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM");
-                                       if ( segmentIndex == -1 )
-                                               dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB");
-                                       if ( !libraryOrdinalSet )
-                                               dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*");
-                                       (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false);
-                                       address += sizeof(intptr_t);
+                    if (!useThreadedRebaseBind) {
+                        if ( (address < segmentStartAddress) || (address >= segmentEndAddress) )
+                            throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
+                        if ( symbolName  == NULL )
+                            dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM");
+                        if ( segmentIndex == -1 )
+                            dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB");
+                        if ( !libraryOrdinalSet )
+                            dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*");
+                        handler(context, this, address, type, symbolName, symboFlags, addend, libraryOrdinal,
+                                                               &extraBindData, "", &last, false);
+                        address += sizeof(intptr_t);
+                    } else {
+                        ordinalTable.push_back(ThreadedBindData(symbolName, addend, libraryOrdinal, symboFlags, type));
+                    }
                                        break;
                                case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
                                        if ( (address < segmentStartAddress) || (address >= segmentEndAddress) )
@@ -984,7 +1283,8 @@ void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handl
                                                dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB");
                                        if ( !libraryOrdinalSet )
                                                dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*");
-                                       (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false);
+                    handler(context, this, address, type, symbolName, symboFlags, addend, libraryOrdinal,
+                                     &extraBindData, "", &last, false);
                                        address += read_uleb128(p, end) + sizeof(intptr_t);
                                        break;
                                case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
@@ -996,7 +1296,8 @@ void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handl
                                                dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB");
                                        if ( !libraryOrdinalSet )
                                                dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*");
-                                       (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false);
+                    handler(context, this, address, type, symbolName, symboFlags, addend, libraryOrdinal,
+                                     &extraBindData, "", &last, false);
                                        address += immediate*sizeof(intptr_t) + sizeof(intptr_t);
                                        break;
                                case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
@@ -1011,10 +1312,69 @@ void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handl
                                        for (uint32_t i=0; i < count; ++i) {
                                                if ( (address < segmentStartAddress) || (address >= segmentEndAddress) )
                                                        throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
-                                               (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false);
+                        handler(context, this, address, type, symbolName, symboFlags, addend, libraryOrdinal,
+                                         &extraBindData, "", &last, false);
                                                address += skip + sizeof(intptr_t);
                                        }
-                                       break;
+                    break;
+                case BIND_OPCODE_THREADED:
+                    if (sizeof(intptr_t) != 8) {
+                        dyld::throwf("BIND_OPCODE_THREADED require 64-bit");
+                        return;
+                    }
+                    // Note the immediate is a sub opcode
+                    switch (immediate) {
+                        case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB:
+                            count = read_uleb128(p, end);
+                            ordinalTable.clear();
+                            // FIXME: ld64 wrote the wrong value here and we need to offset by 1 for now.
+                            ordinalTable.reserve(count + 1);
+                            useThreadedRebaseBind = true;
+                            break;
+                        case BIND_SUBOPCODE_THREADED_APPLY: {
+                            uint64_t delta = 0;
+                            do {
+                                address = segmentStartAddress + segOffset;
+                                uint64_t value = *(uint64_t*)address;
+
+
+                                bool isRebase = (value & (1ULL << 62)) == 0;
+                                if (isRebase) {
+                                    {
+                                        // Call the bind handler which knows about our bind type being set to rebase
+                                        handler(context, this, address, BIND_TYPE_THREADED_REBASE, nullptr, 0, 0, 0,
+                                                         nullptr, "", &last, false);
+                                    }
+                                } else {
+                                    // the ordinal is bits [0..15]
+                                    uint16_t ordinal = value & 0xFFFF;
+                                    if (ordinal >= ordinalTable.count()) {
+                                        dyld::throwf("bind ordinal is out of range\n");
+                                        return;
+                                    }
+                                    std::tie(symbolName, addend, libraryOrdinal, symboFlags, type) = ordinalTable[ordinal].pack();
+                                    if ( (address < segmentStartAddress) || (address >= segmentEndAddress) )
+                                        throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
+                                    {
+                                        handler(context, this, address, BIND_TYPE_THREADED_BIND,
+                                                         symbolName, symboFlags, addend, libraryOrdinal,
+                                                         nullptr, "", &last, false);
+                                    }
+                                }
+
+                                // The delta is bits [51..61]
+                                // And bit 62 is to tell us if we are a rebase (0) or bind (1)
+                                value &= ~(1ULL << 62);
+                                delta = ( value & 0x3FF8000000000000 ) >> 51;
+                                segOffset += delta * sizeof(intptr_t);
+                            } while ( delta != 0 );
+                            break;
+                        }
+
+                        default:
+                            dyld::throwf("bad threaded bind subopcode 0x%02X", *p);
+                    }
+                    break;
                                default:
                                        dyld::throwf("bad bind opcode %d in bind info", *p);
                        }
@@ -1104,7 +1464,8 @@ void ImageLoaderMachOCompressed::eachLazyBind(const LinkContext& context, bind_h
                                                throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
                                        if ( symbolName  == NULL )
                                                dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM");
-                                       (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "forced lazy ", NULL, false);
+                    handler(context, this, address, type, symbolName, symboFlags, addend, libraryOrdinal,
+                                     NULL, "forced lazy ", NULL, false);
                                        address += sizeof(intptr_t);
                                        break;
                                case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
@@ -1183,7 +1544,8 @@ uintptr_t ImageLoaderMachOCompressed::doBindLazySymbol(uintptr_t* lazyPointer, c
                                                        if ( !twoLevel || context.bindFlat ) 
                                                                libraryOrdinal = BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
                                                        uintptr_t ptrToBind = (uintptr_t)lazyPointer;
-                                                       uintptr_t symbolAddr = bindAt(context, ptrToBind, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL);
+                            uintptr_t symbolAddr = bindAt(context, this, ptrToBind, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal,
+                                                          NULL, "lazy ", NULL);
                                                        ++fgTotalLazyBindFixups;
                                                        return symbolAddr;
                                                }
@@ -1229,7 +1591,8 @@ uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingI
                if ( segOffset > segSize(segIndex) )
                        dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(segIndex));
                uintptr_t address = segActualLoadAddress(segIndex) + segOffset;
-               result = this->bindAt(context, address, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL, true);
+        result = bindAt(context, this, address, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal,
+                              NULL, "lazy ", NULL, true);
                // <rdar://problem/24140465> Some old apps had multiple lazy symbols bound at once
        } while (!doneAfterBind && !context.strictMachORequired);
 
@@ -1422,17 +1785,17 @@ void ImageLoaderMachOCompressed::updateUsesCoalIterator(CoalIterator& it, uintpt
                                address += read_uleb128(p, end);
                                break;
                        case BIND_OPCODE_DO_BIND:
-                               bindLocation(context, address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ");
+                               bindLocation(context, this->imageBaseAddress(), address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ", NULL, fSlide);
                                boundSomething = true;
                                address += sizeof(intptr_t);
                                break;
                        case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
-                               bindLocation(context, address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ");
+                               bindLocation(context, this->imageBaseAddress(), address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ", NULL, fSlide);
                                boundSomething = true;
                                address += read_uleb128(p, end) + sizeof(intptr_t);
                                break;
                        case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
-                               bindLocation(context, address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ");
+                               bindLocation(context, this->imageBaseAddress(), address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ", NULL, fSlide);
                                boundSomething = true;
                                address += immediate*sizeof(intptr_t) + sizeof(intptr_t);
                                break;
@@ -1440,7 +1803,7 @@ void ImageLoaderMachOCompressed::updateUsesCoalIterator(CoalIterator& it, uintpt
                                count = read_uleb128(p, end);
                                skip = read_uleb128(p, end);
                                for (uint32_t i=0; i < count; ++i) {
-                                       bindLocation(context, address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ");
+                                       bindLocation(context, this->imageBaseAddress(), address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak ", NULL, fSlide);
                                        boundSomething = true;
                                        address += skip + sizeof(intptr_t);
                                }
@@ -1454,13 +1817,16 @@ void ImageLoaderMachOCompressed::updateUsesCoalIterator(CoalIterator& it, uintpt
                context.addDynamicReference(this, targetImage);
 }
 
-uintptr_t ImageLoaderMachOCompressed::interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*, 
-                                                                                               uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver)
+uintptr_t ImageLoaderMachOCompressed::interposeAt(const LinkContext& context, ImageLoaderMachOCompressed* image,
+                                                                                                 uintptr_t addr, uint8_t type, const char*,
+                                                  uint8_t, intptr_t, long,
+                                                  ExtraBindData *extraBindData,
+                                                  const char*, LastLookup*, bool runResolver)
 {
        if ( type == BIND_TYPE_POINTER ) {
                uintptr_t* fixupLocation = (uintptr_t*)addr;
                uintptr_t curValue = *fixupLocation;
-               uintptr_t newValue = interposedAddress(context, curValue, this);
+               uintptr_t newValue = interposedAddress(context, curValue, image);
                if ( newValue != curValue) {
                        *fixupLocation = newValue;
                }
@@ -1474,13 +1840,32 @@ void ImageLoaderMachOCompressed::doInterpose(const LinkContext& context)
                dyld::log("dyld: interposing %lu tuples onto image: %s\n", fgInterposingTuples.size(), this->getPath());
 
        // update prebound symbols
-       eachBind(context, &ImageLoaderMachOCompressed::interposeAt);
-       eachLazyBind(context, &ImageLoaderMachOCompressed::interposeAt);
+       eachBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image,
+                                               uintptr_t addr, uint8_t type, const char* symbolName,
+                                               uint8_t symbolFlags, intptr_t addend, long libraryOrdinal,
+                                               ExtraBindData *extraBindData,
+                                               const char* msg, LastLookup* last, bool runResolver) {
+               return ImageLoaderMachOCompressed::interposeAt(ctx, image, addr, type, symbolName, symbolFlags,
+                                                                                                          addend, libraryOrdinal, extraBindData,
+                                                                                                          msg, last, runResolver);
+       });
+       eachLazyBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image,
+                                                       uintptr_t addr, uint8_t type, const char* symbolName,
+                                                       uint8_t symbolFlags, intptr_t addend, long libraryOrdinal,
+                                                       ExtraBindData *extraBindData,
+                                                       const char* msg, LastLookup* last, bool runResolver) {
+               return ImageLoaderMachOCompressed::interposeAt(ctx, image, addr, type, symbolName, symbolFlags,
+                                                                                                          addend, libraryOrdinal, extraBindData,
+                                                                                                          msg, last, runResolver);
+       });
 }
 
 
-uintptr_t ImageLoaderMachOCompressed::dynamicInterposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, 
-                                                                                               uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver)
+uintptr_t ImageLoaderMachOCompressed::dynamicInterposeAt(const LinkContext& context, ImageLoaderMachOCompressed* image,
+                                                                                                                uintptr_t addr, uint8_t type, const char* symbolName,
+                                                         uint8_t, intptr_t, long,
+                                                         ExtraBindData *extraBindData,
+                                                         const char*, LastLookup*, bool runResolver)
 {
        if ( type == BIND_TYPE_POINTER ) {
                uintptr_t* fixupLocation = (uintptr_t*)addr;
@@ -1492,7 +1877,7 @@ uintptr_t ImageLoaderMachOCompressed::dynamicInterposeAt(const LinkContext& cont
                        if ( value == (uintptr_t)context.dynamicInterposeArray[i].replacee ) {
                                if ( context.verboseInterposing ) {
                                        dyld::log("dyld: dynamic interposing: at %p replace %p with %p in %s\n", 
-                                               fixupLocation, context.dynamicInterposeArray[i].replacee, context.dynamicInterposeArray[i].replacement, this->getPath());
+                                               fixupLocation, context.dynamicInterposeArray[i].replacee, context.dynamicInterposeArray[i].replacement, image->getPath());
                                }
                                *fixupLocation = (uintptr_t)context.dynamicInterposeArray[i].replacement;
                        }
@@ -1507,8 +1892,24 @@ void ImageLoaderMachOCompressed::dynamicInterpose(const LinkContext& context)
                dyld::log("dyld: dynamic interposing %lu tuples onto image: %s\n", context.dynamicInterposeCount, this->getPath());
 
        // update already bound references to symbols
-       eachBind(context, &ImageLoaderMachOCompressed::dynamicInterposeAt);
-       eachLazyBind(context, &ImageLoaderMachOCompressed::dynamicInterposeAt);
+       eachBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image,
+                                               uintptr_t addr, uint8_t type, const char* symbolName,
+                                               uint8_t symbolFlags, intptr_t addend, long libraryOrdinal,
+                                               ExtraBindData *extraBindData,
+                                               const char* msg, LastLookup* last, bool runResolver) {
+               return ImageLoaderMachOCompressed::dynamicInterposeAt(ctx, image, addr, type, symbolName, symbolFlags,
+                                                                                                                         addend, libraryOrdinal, extraBindData,
+                                                                                                                         msg, last, runResolver);
+       });
+       eachLazyBind(context, ^(const LinkContext& ctx, ImageLoaderMachOCompressed* image,
+                                                       uintptr_t addr, uint8_t type, const char* symbolName,
+                                                       uint8_t symbolFlags, intptr_t addend, long libraryOrdinal,
+                                                       ExtraBindData *extraBindData,
+                                                       const char* msg, LastLookup* last, bool runResolver) {
+               return ImageLoaderMachOCompressed::dynamicInterposeAt(ctx, image, addr, type, symbolName, symbolFlags,
+                                                                                                                         addend, libraryOrdinal, extraBindData,
+                                                                                                                         msg, last, runResolver);
+       });
 }
 
 const char* ImageLoaderMachOCompressed::findClosestSymbol(const void* addr, const void** closestAddr) const
index d0423875a002bf01b02d5406c0724d120b4d6746..23bd022d002d767a17bfcd434256437661fc6617 100644 (file)
@@ -66,6 +66,8 @@ public:
        virtual bool                                            incrementCoalIterator(CoalIterator&);
        virtual uintptr_t                                       getAddressCoalIterator(CoalIterator&, const LinkContext& contex);
        virtual void                                            updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, unsigned targetIndex, const LinkContext& context);
+       virtual void                                            registerInterposing(const LinkContext& context);
+       virtual bool                                            usesChainedFixups() const;
 
 protected:
        virtual void                                            doInterpose(const LinkContext& context);
@@ -89,15 +91,18 @@ protected:
 #if PREBOUND_IMAGE_SUPPORT
        virtual void                                            resetPreboundLazyPointers(const LinkContext& context);
 #endif
+       virtual uintptr_t                                       resolveWeak(const LinkContext& context, const char* symbolName, bool weak_import, bool runResolver,
+                                                                                                       const ImageLoader** foundIn);
 
                
 private:
        struct LastLookup { long ordinal; uint8_t flags; const char* name; uintptr_t result; const ImageLoader* foundIn; };
 
 
-       typedef uintptr_t (ImageLoaderMachOCompressed::*bind_handler)(const LinkContext& context, uintptr_t addr, uint8_t type, 
-                                                                                       const char* symbolName, uint8_t symboFlags, intptr_t addend, long libraryOrdinal, 
-                                                                                       const char* msg, LastLookup* last, bool runResolver);
+       typedef uintptr_t                   (^bind_handler)(const LinkContext& context, ImageLoaderMachOCompressed* image, uintptr_t addr, uint8_t type,
+                                                                                                               const char* symbolName, uint8_t symboFlags, intptr_t addend, long libraryOrdinal,
+                                                                                                               ExtraBindData *extraBindData,
+                                                                                                               const char* msg, LastLookup* last, bool runResolver);
 
        void                                                            eachLazyBind(const LinkContext& context, bind_handler);
        void                                                            eachBind(const LinkContext& context, bind_handler);
@@ -114,8 +119,10 @@ private:
        void                                                            rebaseAt(const LinkContext& context, uintptr_t addr, uintptr_t slide, uint8_t type);
        void                                                            throwBadRebaseAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, 
                                                                                                const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos);
-       uintptr_t                                                       bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, 
-                                                                                               uint8_t symboFlags, intptr_t addend, long libraryOrdinal, const char* msg,
+       static uintptr_t                                        bindAt(const LinkContext& context, ImageLoaderMachOCompressed* image, uintptr_t addr, uint8_t type, const char* symbolName,
+                                               uint8_t symboFlags, intptr_t addend, long libraryOrdinal,
+                                               ExtraBindData *extraBindData,
+                                               const char* msg,
                                                                                                LastLookup* last, bool runResolver=false);
        void                                                            bindCompressed(const LinkContext& context);
        void                                                            throwBadBindingAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, 
@@ -129,21 +136,19 @@ private:
        uintptr_t                                                       resolveTwolevel(const LinkContext& context, const char* symbolName, const ImageLoader* definedInImage,
                                                                                                          const ImageLoader* requestorImage, unsigned requestorOrdinalOfDef, bool weak_import, bool runResolver,
                                                                                                          const ImageLoader** foundInn);
-       uintptr_t                                                       interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*, 
-                                                                                               uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver);
-       uintptr_t                                                       dynamicInterposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*, 
-                                                                                               uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver);
+       static uintptr_t                                        interposeAt(const LinkContext& context, ImageLoaderMachOCompressed* image, uintptr_t addr, uint8_t type, const char*, 
+                                                    uint8_t, intptr_t, long,
+                                                    ExtraBindData *extraBindData,
+                                                    const char*, LastLookup*, bool runResolver);
+       static uintptr_t                                        dynamicInterposeAt(const LinkContext& context, ImageLoaderMachOCompressed* image, uintptr_t addr, uint8_t type, const char*, 
+                                                           uint8_t, intptr_t, long,
+                                                           ExtraBindData *extraBindData,
+                                                           const char*, LastLookup*, bool runResolver);
     void                                updateOptimizedLazyPointers(const LinkContext& context);
     void                                updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr, const LinkContext& context);
        void                                                            registerEncryption(const struct encryption_info_command* encryptCmd, const LinkContext& context);
 
        const struct dyld_info_command*                 fDyldInfo;
-
-#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 a54625d2ab457436daa707c2697dda3560e62f60..54929e153e32bd5b352ceaf437f1ea9f36574b53 100644 (file)
@@ -460,17 +460,17 @@ void ImageLoaderMegaDylib::updateUsesCoalIterator(CoalIterator& it, uintptr_t va
                                address += read_uleb128(p, end);
                                break;
                        case BIND_OPCODE_DO_BIND:
-                               ImageLoaderMachO::bindLocation(context, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak ");
+                               ImageLoaderMachO::bindLocation(context, 0, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak ", NULL, _slide);
                                boundSomething = true;
                                address += sizeof(intptr_t);
                                break;
                        case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
-                               ImageLoaderMachO::bindLocation(context, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak ");
+                               ImageLoaderMachO::bindLocation(context, 0, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak ", NULL, _slide);
                                boundSomething = true;
                                address += read_uleb128(p, end) + sizeof(intptr_t);
                                break;
                        case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
-                               ImageLoaderMachO::bindLocation(context, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak ");
+                               ImageLoaderMachO::bindLocation(context, 0, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak ", NULL, _slide);
                                boundSomething = true;
                                address += immediate*sizeof(intptr_t) + sizeof(intptr_t);
                                break;
@@ -478,7 +478,7 @@ void ImageLoaderMegaDylib::updateUsesCoalIterator(CoalIterator& it, uintptr_t va
                                count = read_uleb128(p, end);
                                skip = read_uleb128(p, end);
                                for (uint32_t i=0; i < count; ++i) {
-                                       ImageLoaderMachO::bindLocation(context, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak ");
+                                       ImageLoaderMachO::bindLocation(context, 0, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak ", NULL, _slide);
                                        boundSomething = true;
                                        address += skip + sizeof(intptr_t);
                                }
@@ -777,24 +777,37 @@ bool ImageLoaderMegaDylib::findInChainedTriesAndDependents(const LinkContext& co
 }
 
 
-bool ImageLoaderMegaDylib::flatFindSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image)
+bool ImageLoaderMegaDylib::flatFindSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image, ImageLoader::CoalesceNotifier notifier)
 {
+       bool found = false;
        // check export trie of all in-use images
        for (unsigned i=0; i < _imageCount ; ++i) {
                uint16_t imageIndex = _bottomUpArray[i];
                if ( _stateFlags[imageIndex] == kStateUnused )
                        continue;
+#if USES_CHAINED_BINDS
+               const macho_header* mh = getIndexedMachHeader(imageIndex);
+               if ( onlyInCoalesced && (mh->flags & MH_WEAK_DEFINES) == 0 )
+                       continue;
+#else
                if ( onlyInCoalesced && (_imageExtras[imageIndex].weakBindingsSize == 0) )
                        continue;
+#endif
                const uint8_t* exportNode;
                const uint8_t* exportTrieEnd;
                if ( exportTrieHasNode(name, imageIndex, &exportNode, &exportTrieEnd) ) {
-                       *sym = (Symbol*)exportNode;
-                       *image = this;
-                       return true;
+                       if ( notifier )
+                               notifier((Symbol*)exportNode, this, (mach_header*)getIndexedMachHeader(imageIndex));
+                       if ( !found ) {
+                               *sym = (Symbol*)exportNode;
+                               *image = this;
+                               found = true;
+                       }
+                       if ( !onlyInCoalesced )
+                               return true;
                }
        }
-       return false;
+       return found;
 }
 
 
@@ -881,9 +894,10 @@ 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);
-                               dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{
+                               {
+                                       dyld3::ScopedTimer timer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)getIndexedMachHeader(imageIndex), (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 ) {
index 6046da63c653c46b71c36174769b12ea06deec42..80ea046b9d47f36b88c24589ef1282a46e2baaac 100644 (file)
@@ -27,7 +27,8 @@
 #define __IMAGELOADER_MEGADYLIB__
 
 #include <stdint.h> 
-#include <pthread.h> 
+#include <pthread.h>
+#include <uuid/uuid.h>
 
 #include "ImageLoaderMachO.h"
 #include "dyld_cache_format.h"
@@ -55,8 +56,8 @@ public:
        virtual const char*                                     getInstallPath() const;
        virtual bool                                            inSharedCache() const { return true; }
        virtual bool                                            containsSymbol(const void* addr) const { unreachable(); }
-       virtual void*                                           getThreadPC() const { unreachable(); }
-       virtual void*                                           getMain() const { unreachable(); }
+       virtual void*                                           getEntryFromLC_MAIN() const { unreachable(); }
+       virtual void*                                           getEntryFromLC_UNIXTHREAD() const { unreachable(); }
        virtual const struct mach_header*   machHeader() const { unreachable(); }
        virtual uintptr_t                                       getSlide() const { return _slide; }
        virtual const void*                                     getEnd() const { unreachable(); }
@@ -89,7 +90,8 @@ public:
        virtual bool                                            needsInitialization() { unreachable(); }
        virtual bool                                            getSectionContent(const char* segmentName, const char* sectionName, void** start, size_t* length) { unreachable(); }
        virtual void                                            getUnwindInfo(dyld_unwind_sections* info) { unreachable(); }
-       virtual bool                                            findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset) { unreachable(); }
+    virtual bool                        findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset) { unreachable(); }
+    virtual const struct macho_section* findSection(const void* imageInterior) const { unreachable(); }
        virtual bool                                            isPrebindable() const { unreachable();  }
        virtual bool                                            usablePrebinding(const LinkContext& context) const { unreachable(); }
        virtual void                                            getRPaths(const LinkContext& context, std::vector<const char*>&) const { }
@@ -118,7 +120,7 @@ public:
        virtual uint32_t                                        minOSVersion() const { unreachable(); }
        
                                                                                // if the image contains interposing functions, register them
-       virtual void                                            registerInterposing() { unreachable(); }
+       virtual void                                            registerInterposing(const LinkContext& context) { unreachable(); }
 
        virtual ImageLoader*                            libImage(unsigned int) const { unreachable(); }
        virtual bool                                            libReExported(unsigned int) const { unreachable(); }
@@ -137,7 +139,7 @@ public:
        bool                                                            findUnwindSections(const void* addr, dyld_unwind_sections* info);
        bool                                                            dladdrFromCache(const void* address, Dl_info* info);
        uintptr_t                                                       bindLazy(uintptr_t lazyBindingInfoOffset, const LinkContext& context, const mach_header* mh, unsigned index);
-       bool                                                            flatFindSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image);
+       bool                                                            flatFindSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image, ImageLoader::CoalesceNotifier);
        void                                                            getDylibUUID(unsigned int index, uuid_t) const;
 
 protected:
index 808305e29b2eb37c264ee31945a39b7c235e6ba0..0a4d97bda1b0588100153a9299d4cae9791a8b67 100644 (file)
@@ -40,7 +40,6 @@
 #include <sys/un.h>
 #include <sys/syslog.h>
 #include <sys/uio.h>
-#include <sys/xattr.h>
 #include <mach/mach.h>
 #include <mach-o/fat.h>
 #include <mach-o/loader.h> 
 #include <System/machine/cpu_capabilities.h>
 #include <System/sys/reason.h>
 #include <kern/kcdata.h>
+#if TARGET_IPHONE_SIMULATOR
+       enum {
+               AMFI_DYLD_INPUT_PROC_IN_SIMULATOR = (1 << 0),
+       };
+       enum amfi_dyld_policy_output_flag_set {
+               AMFI_DYLD_OUTPUT_ALLOW_AT_PATH = (1 << 0),
+               AMFI_DYLD_OUTPUT_ALLOW_PATH_VARS = (1 << 1),
+               AMFI_DYLD_OUTPUT_ALLOW_CUSTOM_SHARED_CACHE = (1 << 2),
+               AMFI_DYLD_OUTPUT_ALLOW_FALLBACK_PATHS = (1 << 3),
+               AMFI_DYLD_OUTPUT_ALLOW_PRINT_VARS = (1 << 4),
+               AMFI_DYLD_OUTPUT_ALLOW_FAILED_LIBRARY_INSERTION = (1 << 5),
+       };
+       extern "C" int amfi_check_dyld_policy_self(uint64_t input_flags, uint64_t* output_flags);
+#else
+       #include <libamfi.h>
+#endif
+extern "C" {
+       #include <corecrypto/ccdigest.h>
+       #include <corecrypto/ccsha2.h>
+}
 #include <sandbox.h>
 #include <sandbox/private.h>
+#if __has_feature(ptrauth_calls)
+       #include <ptrauth.h>
+#endif
 
 extern "C" int __fork();
 
@@ -99,6 +121,16 @@ extern "C" int __fork();
        #define CPU_SUBTYPE_ARM64_E    2
 #endif
 
+#ifndef CPU_ARCH_ABI64_32
+       #define CPU_ARCH_ABI64_32                               ((cpu_type_t) 0x02000000)
+#endif
+#ifndef CPU_TYPE_ARM64_32
+       #define CPU_TYPE_ARM64_32                               ((cpu_type_t) (CPU_TYPE_ARM | CPU_ARCH_ABI64_32))
+#endif
+#ifndef CPU_SUBTYPE_ARM64_32_V8
+       #define CPU_SUBTYPE_ARM64_32_V8         ((cpu_subtype_t) 1)
+#endif
+
 #ifndef VM_PROT_SLIDE   
     #define VM_PROT_SLIDE 0x20
 #endif
@@ -111,13 +143,6 @@ extern "C" int __fork();
 #include "dyldLibSystemInterface.h"
 #include "dyld_cache_format.h"
 #include "dyld_process_info_internal.h"
-#include <coreSymbolicationDyldSupport.h>
-#if TARGET_IPHONE_SIMULATOR
-       extern "C" void xcoresymbolication_load_notifier(void *connection, uint64_t load_timestamp, const char *image_path, const struct mach_header *mach_header);
-       extern "C" void xcoresymbolication_unload_notifier(void *connection, uint64_t unload_timestamp, const char *image_path, const struct mach_header *mach_header);
-       #define coresymbolication_load_notifier(c, t, p, h) xcoresymbolication_load_notifier(c, t, p, h)
-       #define coresymbolication_unload_notifier(c, t, p, h) xcoresymbolication_unload_notifier(c, t, p, h)
-#endif
 
 #if SUPPORT_ACCELERATE_TABLES
        #include "ImageLoaderMegaDylib.h"
@@ -129,19 +154,17 @@ extern "C" int __fork();
        #include "dyldSyscallInterface.h"
 #endif
 
-#include "LaunchCache.h"
+#include "Closure.h"
 #include "libdyldEntryVector.h"
-#include "MachOParser.h"
+#include "MachOLoaded.h"
 #include "Loading.h"
 #include "DyldSharedCache.h"
 #include "SharedCacheRuntime.h"
 #include "StringUtils.h"
 #include "Tracing.h"
-#include "DyldCacheParser.h"
-
-extern "C" {
-    #include "closuredProtocol.h"
-}
+#include "ClosureBuilder.h"
+#include "ClosureFileSystemPhysical.h"
+#include "FileUtils.h"
 
 
 // not libc header for send() syscall interface
@@ -231,7 +254,6 @@ struct EnvironmentVariables {
        bool                                            DYLD_PRINT_OPTS;
        bool                                            DYLD_PRINT_ENV;
        bool                                            DYLD_DISABLE_DOFS;
-       bool                                            DYLD_PRINT_CS_NOTIFICATIONS;
                             //  DYLD_SHARED_CACHE_DIR           ==> sSharedCacheOverrideDir
                                                        //      DYLD_ROOT_PATH                                  ==> gLinkContext.rootPaths
                                                        //      DYLD_IMAGE_SUFFIX                               ==> gLinkContext.imageSuffix
@@ -272,7 +294,6 @@ static cpu_type_t                                   sHostCPU;
 static cpu_subtype_t                           sHostCPUsubtype;
 #endif
 static ImageLoaderMachO*                       sMainExecutable = NULL;
-static EnvVarMode                                      sEnvMode = envNone;
 static size_t                                          sInsertedDylibCount = 0;
 static std::vector<ImageLoader*>       sAllImages;
 static std::vector<ImageLoader*>       sImageRoots;
@@ -280,6 +301,7 @@ static std::vector<ImageLoader*>    sImageFilesNeedingTermination;
 static std::vector<RegisteredDOF>      sImageFilesNeedingDOFUnregistration;
 static std::vector<ImageCallback>   sAddImageCallbacks;
 static std::vector<ImageCallback>   sRemoveImageCallbacks;
+static std::vector<LoadImageCallback> sAddLoadImageCallbacks;
 static bool                                                    sRemoveImageCallbacksInUse = false;
 static void*                                           sSingleHandlers[7][3];
 static void*                                           sBatchHandlers[7][3];
@@ -322,7 +344,6 @@ static OSSpinLock                                   sDynamicReferencesLock = 0;
 static bool                                                    sLogToFile = false;
 #endif
 static char                                                    sLoadingCrashMessage[1024] = "dyld: launch, loading dependent libraries";
-static bool                                                    sSafeMode = false;
 static _dyld_objc_notify_mapped                sNotifyObjCMapped;
 static _dyld_objc_notify_init          sNotifyObjCInit;
 static _dyld_objc_notify_unmapped      sNotifyObjCUnmapped;
@@ -340,6 +361,7 @@ static bool                                                 sDisableAcceleratorTables = false;
 bool                                                           gUseDyld3 = false;
 static bool                                                    sSkipMain = false;
 static bool                                                    sEnableClosures = false;
+static uint64_t                                                launchTraceID = 0;
 
 //
 // The MappedRanges structure is used for fast address->image lookups.
@@ -681,8 +703,14 @@ static void notifyAddImageCallbacks(ImageLoader* image)
 {
        // use guard so that we cannot notify about the same image twice
        if ( ! image->addFuncNotified() ) {
-               for (std::vector<ImageCallback>::iterator it=sAddImageCallbacks.begin(); it != sAddImageCallbacks.end(); it++)
+               for (std::vector<ImageCallback>::iterator it=sAddImageCallbacks.begin(); it != sAddImageCallbacks.end(); it++) {
+                       dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)image->machHeader(), (uint64_t)(*it), 0);
                        (*it)(image->machHeader(), image->getSlide());
+               }
+               for (LoadImageCallback func : sAddLoadImageCallbacks) {
+                       dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)image->machHeader(), (uint64_t)(*func), 0);
+                       (*func)(image->machHeader(), image->getPath(), !image->neverUnload());
+               }
                image->setAddFuncNotified();
        }
 }
@@ -763,138 +791,108 @@ static void notifySingleFromCache(dyld_image_states state, const mach_header* mh
                }
        }
        if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && (mh->flags & MH_HAS_OBJC) ) {
+               dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)mh, 0, 0);
                (*sNotifyObjCInit)(path, 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] )
+#if !TARGET_OS_SIMULATOR
+static void sendMessage(unsigned portSlot, mach_msg_id_t msgId, mach_msg_size_t sendSize, mach_msg_header_t* buffer, mach_msg_size_t bufferSize) {
+       // Allocate a port to listen on in this monitoring task
+       mach_port_t sendPort = dyld::gProcessInfo->notifyPorts[portSlot];
+       if (sendPort == MACH_PORT_NULL) {
                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) + 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 = imageCount/2;
-               notifyMonitoringDyld(unloading, portSlot, imageHalfCount, infos);
-               notifyMonitoringDyld(unloading, portSlot, imageCount - imageHalfCount, &infos[imageHalfCount]);
+       }
+       mach_port_t replyPort = MACH_PORT_NULL;
+       mach_port_options_t options = { .flags = MPO_CONTEXT_AS_GUARD | MPO_STRICT,
+               .mpl = { 1 }};
+       kern_return_t kr = mach_port_construct(mach_task_self(), &options, (mach_port_context_t)&replyPort, &replyPort);
+       if (kr != KERN_SUCCESS) {
                return;
        }
-       uint8_t buffer[totalSize];
-       dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)buffer;
-       header->version                 = 1;
-       header->imageCount              = imageCount;
-       header->imagesOffset    = sizeof(dyld_process_info_notify_header);
-       header->stringsOffset   = sizeof(dyld_process_info_notify_header) + entriesSize;
-       header->timestamp               = dyld::gProcessInfo->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 (unsigned j=0; j < imageCount; ++j) {
-               strcpy(pathPool, infos[j].imageFilePath);
-               uint32_t len = (uint32_t)strlen(pathPool);
-               bzero(entries->uuid, 16);
-               const ImageLoader* image = findImageByMachHeader(infos[j].imageLoadAddress);
-               if ( image != NULL ) {
-                       image->getUUID(entries->uuid);
+       // Assemble a message
+       mach_msg_header_t* h = buffer;
+       h->msgh_bits         = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND_ONCE);
+       h->msgh_id           = msgId;
+       h->msgh_local_port   = replyPort;
+       h->msgh_remote_port  = sendPort;
+       h->msgh_reserved     = 0;
+       h->msgh_size         = sendSize;
+       kr = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG, h->msgh_size, bufferSize, replyPort, 0, MACH_PORT_NULL);
+       mach_msg_destroy(h);
+       if ( kr == MACH_SEND_INVALID_DEST ) {
+               if (OSAtomicCompareAndSwap32(sendPort, 0, (volatile int32_t*)&dyld::gProcessInfo->notifyPorts[portSlot])) {
+                       mach_port_deallocate(mach_task_self(), sendPort);
                }
-#if SUPPORT_ACCELERATE_TABLES
-               else if ( sAllCacheImagesProxy != NULL ) {
-                       const mach_header* mh;
-                       const char* path;
-                       unsigned index;
-                       if ( sAllCacheImagesProxy->addressInCache(infos[j].imageLoadAddress, &mh, &path, &index) ) {
-                               sAllCacheImagesProxy->getDylibUUID(index, entries->uuid);
-                       }
+       }
+       mach_port_destruct(mach_task_self(), replyPort, 0, (mach_port_context_t)&replyPort);
+}
+
+static void notifyMonitoringDyld(bool unloading, unsigned imageCount, const struct mach_header* loadAddresses[],
+                                                                const char* imagePaths[])
+{
+       dyld3::ScopedTimer(DBG_DYLD_REMOTE_IMAGE_NOTIFIER, 0, 0, 0);
+       for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
+               if ( dyld::gProcessInfo->notifyPorts[slot] == 0) continue;
+               unsigned entriesSize = imageCount*sizeof(dyld_process_info_image_entry);
+               unsigned pathsSize = 0;
+               for (unsigned j=0; j < imageCount; ++j) {
+                       pathsSize += (strlen(imagePaths[j]) + 1);
+               }
+               unsigned totalSize = (sizeof(dyld_process_info_notify_header) + entriesSize + pathsSize + 127) & -128;   // align
+               if ( totalSize > DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE ) {
+                       // Putting all image paths into one message would make buffer too big.
+                       // Instead split into two messages.  Recurse as needed until paths fit in buffer.
+                       unsigned imageHalfCount = imageCount/2;
+                       notifyMonitoringDyld(unloading, imageHalfCount, loadAddresses, imagePaths);
+                       notifyMonitoringDyld(unloading, imageCount - imageHalfCount, &loadAddresses[imageHalfCount], &imagePaths[imageHalfCount]);
+                       return;
+               }
+               uint8_t buffer[totalSize + MAX_TRAILER_SIZE];
+               dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)buffer;
+               header->version                 = 1;
+               header->imageCount              = imageCount;
+               header->imagesOffset    = sizeof(dyld_process_info_notify_header);
+               header->stringsOffset   = sizeof(dyld_process_info_notify_header) + entriesSize;
+               header->timestamp               = dyld::gProcessInfo->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 (unsigned j=0; j < imageCount; ++j) {
+                       strcpy(pathPool, imagePaths[j]);
+                       uint32_t len = (uint32_t)strlen(pathPool);
+                       bzero(entries->uuid, 16);
+                       dyld3::MachOFile* mf = (dyld3::MachOFile*)loadAddresses[j];
+                       mf->getUuid(entries->uuid);
+                       entries->loadAddress = (uint64_t)loadAddresses[j];
+                       entries->pathStringOffset = (uint32_t)(pathPool - pathPoolStart);
+                       entries->pathLength  = len;
+                       pathPool += (len +1);
+                       ++entries;
+               }
+               if (unloading) {
+                       sendMessage(slot, DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID, totalSize, (mach_msg_header_t*)buffer, totalSize+MAX_TRAILER_SIZE);
+               } else {
+                       sendMessage(slot, DYLD_PROCESS_INFO_NOTIFY_LOAD_ID, totalSize, (mach_msg_header_t*)buffer, totalSize+MAX_TRAILER_SIZE);
                }
-#endif
-               entries->loadAddress = (uint64_t)infos[j].imageLoadAddress;
-               entries->pathStringOffset = (uint32_t)(pathPool - pathPoolStart);
-               entries->pathLength  = len;
-               pathPool += (len +1);
-               ++entries;
-       }
-
-       if ( sNotifyReplyPorts[portSlot] == 0 ) {
-               if ( !mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sNotifyReplyPorts[portSlot]) )
-                       mach_port_insert_right(mach_task_self(), sNotifyReplyPorts[portSlot], sNotifyReplyPorts[portSlot], MACH_MSG_TYPE_MAKE_SEND);
-               //dyld::log("allocated reply port %d\n", sNotifyReplyPorts[portSlot]);
-       }
-       //dyld::log("found port to send to\n");
-       mach_msg_header_t* h = (mach_msg_header_t*)buffer;
-       h->msgh_bits            = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND); // MACH_MSG_TYPE_MAKE_SEND_ONCE
-       h->msgh_id                      = unloading ? DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID : DYLD_PROCESS_INFO_NOTIFY_LOAD_ID;
-       h->msgh_local_port      = sNotifyReplyPorts[portSlot];
-       h->msgh_remote_port = dyld::gProcessInfo->notifyPorts[portSlot];
-       h->msgh_reserved        = 0;
-       h->msgh_size            = (mach_msg_size_t)sizeof(buffer);
-       //dyld::log("sending to port[%d]=%d, size=%d, reply port=%d, id=0x%X\n", portSlot, dyld::gProcessInfo->notifyPorts[portSlot], h->msgh_size, sNotifyReplyPorts[portSlot], h->msgh_id);
-       kern_return_t sendResult = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT, h->msgh_size, h->msgh_size, sNotifyReplyPorts[portSlot], 5000, MACH_PORT_NULL);
-       //dyld::log("send result = 0x%X, msg_id=%d, msg_size=%d\n", sendResult, h->msgh_id, h->msgh_size);
-       if ( sendResult == MACH_SEND_INVALID_DEST ) {
-               // sender is not responding, detatch
-               //dyld::log("process requesting notification gone. deallocation send port %d and receive port %d\n", dyld::gProcessInfo->notifyPorts[portSlot], sNotifyReplyPorts[portSlot]);
-               mach_port_deallocate(mach_task_self(), dyld::gProcessInfo->notifyPorts[portSlot]);
-               mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[portSlot]);
-               dyld::gProcessInfo->notifyPorts[portSlot] = 0;
-               sNotifyReplyPorts[portSlot] = 0;
-       }
-       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()
 {
+       dyld3::ScopedTimer(DBG_DYLD_REMOTE_IMAGE_NOTIFIER, 0, 0, 0);
        for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
-               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");
-                       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]);
-                               mach_port_deallocate(mach_task_self(), dyld::gProcessInfo->notifyPorts[slot]);
-                               mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
-                               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;
-                       }
-               }
+               if ( dyld::gProcessInfo->notifyPorts[slot] == 0) continue;
+               uint8_t buffer[sizeof(mach_msg_header_t) + MAX_TRAILER_SIZE];
+               sendMessage(slot, DYLD_PROCESS_INFO_NOTIFY_MAIN_ID, sizeof(mach_msg_header_t), (mach_msg_header_t*)buffer, sizeof(mach_msg_header_t) + MAX_TRAILER_SIZE);
        }
 }
+#else
+extern void notifyMonitoringDyldMain() VIS_HIDDEN;
+extern void notifyMonitoringDyld(bool unloading, unsigned imageCount, const struct mach_header* loadAddresses[],
+                                                                const char* imagePaths[]) VIS_HIDDEN;
+#endif
 
 void notifyKernel(const ImageLoader& image, bool loading) {
        if ( !image.inSharedCache() ) {
@@ -937,6 +935,7 @@ static void notifySingle(dyld_image_states state, const ImageLoader* image, Imag
        }
        if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && image->notifyObjC() ) {
                uint64_t t0 = mach_absolute_time();
+               dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
                (*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
                uint64_t t1 = mach_absolute_time();
                uint64_t t2 = mach_absolute_time();
@@ -949,33 +948,10 @@ static void notifySingle(dyld_image_states state, const ImageLoader* image, Imag
     // mach message csdlc about dynamically unloaded images
        if ( image->addFuncNotified() && (state == dyld_image_state_terminated) ) {
                notifyKernel(*image, false);
-
-               uint64_t loadTimestamp = mach_absolute_time();
-               if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) {
-                       dyld::log("dyld: coresymbolication_unload_notifier(%p, 0x%016llX, %p, %s)\n",
-                                         dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, image->machHeader(), image->getPath());
-               }
-               if ( dyld::gProcessInfo->coreSymbolicationShmPage != NULL) {
-                       coresymbolication_unload_notifier(dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, image->getPath(), image->machHeader());
-               }
-               for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
-                       if ( dyld::gProcessInfo->notifyPorts[slot] != 0 ) {
-                               dyld_image_info info;
-                               info.imageLoadAddress   = image->machHeader();
-                               info.imageFilePath              = image->getPath();
-                               info.imageFileModDate   = 0;
-                               notifyMonitoringDyld(true, slot, 1, &info);
-                       }
-                       else if ( sNotifyReplyPorts[slot] != 0 ) {
-                               // monitoring process detached from this process, so release reply port
-                               //dyld::log("deallocated reply port %d\n", sNotifyReplyPorts[slot]);
-                               mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
-                               sNotifyReplyPorts[slot] = 0;
-                               sZombieNotifiers[slot] = false;
-                       }
-               }
+               const struct mach_header* loadAddress[] = { image->machHeader() };
+               const char* loadPath[] = { image->getPath() };
+               notifyMonitoringDyld(true, 1, loadAddress, loadPath);
        }
-
 }
 
 
@@ -1070,8 +1046,16 @@ static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image
                        // support _dyld_register_func_for_add_image()
                        if ( state == dyld_image_state_bound ) {
                                for (ImageCallback callback : sAddImageCallbacks) {
-                                       for (unsigned i=0; i < cacheCount; ++i)
+                                       for (unsigned i=0; i < cacheCount; ++i) {
+                                               dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)infos[imageCount+i].imageLoadAddress, (uint64_t)(*callback), 0);
                                                (*callback)(infos[imageCount+i].imageLoadAddress, sSharedCacheLoadInfo.slide);
+                                       }
+                               }
+                               for (LoadImageCallback func : sAddLoadImageCallbacks) {
+                                       for (unsigned i=0; i < cacheCount; ++i) {
+                                               dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)infos[imageCount+i].imageLoadAddress, (uint64_t)(*func), 0);
+                                               (*func)(infos[imageCount+i].imageLoadAddress, infos[imageCount+i].imageFilePath, false);
+                                       }
                                }
                        }
                        imageCount += cacheCount;
@@ -1133,6 +1117,7 @@ static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image
                                        }
                                }
                                if ( objcImageCount != 0 ) {
+                                       dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_MAP, 0, 0, 0);
                                        uint64_t t0 = mach_absolute_time();
                                        (*sNotifyObjCMapped)(objcImageCount, paths, mhs);
                                        uint64_t t1 = mach_absolute_time();
@@ -1144,32 +1129,38 @@ static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image
         if ( dontLoadReason != NULL )
             throw dontLoadReason;
                if ( !preflightOnly && (state == dyld_image_state_dependents_mapped) ) {
-                       if ( (dyld::gProcessInfo->coreSymbolicationShmPage != NULL) || sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) {
-                               // mach message csdlc about loaded images
-                               uint64_t loadTimestamp = mach_absolute_time();
-                               for (unsigned j=0; j < imageCount; ++j) {
-                                       if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) {
-                                               dyld::log("dyld: coresymbolication_load_notifier(%p, 0x%016llX, %p, %s)\n",
-                                                                 dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, infos[j].imageLoadAddress, infos[j].imageFilePath);
-                                       }
-                                       coresymbolication_load_notifier(dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, infos[j].imageFilePath, infos[j].imageLoadAddress);
-                               }
-                       }
-                       for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
-                               if ( dyld::gProcessInfo->notifyPorts[slot] )
-                                       notifyMonitoringDyld(false, slot, imageCount, infos);
+                       const struct mach_header* loadAddresses[imageCount];
+                       const char* loadPaths[imageCount];
+                       for(uint32_t i = 0; i<imageCount; ++i) {
+                               loadAddresses[i] = infos[i].imageLoadAddress;
+                               loadPaths[i] = infos[i].imageFilePath;
                        }
+                       notifyMonitoringDyld(false, imageCount, loadAddresses, loadPaths);
                }
        }
 }
 
-
-
 static void notifyBatch(dyld_image_states state, bool preflightOnly)
 {
        notifyBatchPartial(state, false, NULL, preflightOnly, false);
 }
 
+static
+void coresymbolication_load_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh)
+{
+       const struct mach_header* loadAddress[] = { mh };
+       const char* loadPath[] = { path };
+       notifyMonitoringDyld(false, 1, loadAddress, loadPath);
+}
+
+static
+void coresymbolication_unload_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh)
+{
+       const struct mach_header* loadAddress = { mh };
+       const char* loadPath = { path };
+       notifyMonitoringDyld(true, 1, &loadAddress, &loadPath);
+}
+
 // In order for register_func_for_add_image() callbacks to to be called bottom up,
 // we need to maintain a list of root images. The main executable is usally the
 // first root. Any images dynamically added are also roots (unless already loaded).
@@ -1347,6 +1338,7 @@ void removeImage(ImageLoader* image)
        if ( image->getState() >= dyld_image_state_bound ) {
                sRemoveImageCallbacksInUse = true; // This only runs inside dyld's global lock, so ok to use a global for the in-use flag.
                for (std::vector<ImageCallback>::iterator it=sRemoveImageCallbacks.begin(); it != sRemoveImageCallbacks.end(); it++) {
+                       dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_REMOVE_IMAGE, (uint64_t)image->machHeader(), (uint64_t)(*it), 0);
                        (*it)(image->machHeader(), image->getSlide());
                }
                sRemoveImageCallbacksInUse = false;
@@ -1633,12 +1625,10 @@ static const char** parseColonList(const char* list, const char* mainExecutableD
                if (*s == ':') {
                        size_t len = s-start;
                        if ( (mainExecutableDir != NULL) && (strncmp(start, "@loader_path/", 13) == 0) ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-                               if ( gLinkContext.processIsRestricted ) {
-                                       dyld::log("dyld: warning: @loader_path/ ignored in restricted process\n");
+                               if ( !gLinkContext.allowAtPaths ) {
+                                       dyld::log("dyld: warning: @loader_path/ ignored because of amfi policy\n");
                                        continue;
                                }
-#endif
                                size_t mainExecDirLen = strlen(mainExecutableDir);
                                char* str = new char[mainExecDirLen+len+1];
                                strcpy(str, mainExecutableDir);
@@ -1648,12 +1638,10 @@ static const char** parseColonList(const char* list, const char* mainExecutableD
                                result[index++] = str;
                        }
                        else if ( (mainExecutableDir != NULL) && (strncmp(start, "@executable_path/", 17) == 0) ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-                               if ( gLinkContext.processIsRestricted ) {
-                                       dyld::log("dyld: warning: @executable_path/ ignored in restricted process\n");
+                               if ( !gLinkContext.allowAtPaths ) {
+                                       dyld::log("dyld: warning: @executable_path/ ignored because of amfi policy\n");
                                        continue;
                                }
-#endif
                                size_t mainExecDirLen = strlen(mainExecutableDir);
                                char* str = new char[mainExecDirLen+len+1];
                                strcpy(str, mainExecutableDir);
@@ -1673,12 +1661,10 @@ static const char** parseColonList(const char* list, const char* mainExecutableD
        }
        size_t len = strlen(start);
        if ( (mainExecutableDir != NULL) && (strncmp(start, "@loader_path/", 13) == 0) ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-               if ( gLinkContext.processIsRestricted ) {
-                       dyld::log("dyld: warning: @loader_path/ ignored in restricted process\n");
+               if ( !gLinkContext.allowAtPaths ) {
+                       dyld::log("dyld: warning: @loader_path/ ignored because of amfi policy\n");
                }
                else
-#endif
                {
                        size_t mainExecDirLen = strlen(mainExecutableDir);
                        char* str = new char[mainExecDirLen+len+1];
@@ -1689,12 +1675,10 @@ static const char** parseColonList(const char* list, const char* mainExecutableD
                }
        }
        else if ( (mainExecutableDir != NULL) && (strncmp(start, "@executable_path/", 17) == 0) ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-               if ( gLinkContext.processIsRestricted ) {
-                       dyld::log("dyld: warning: @executable_path/ ignored in restricted process\n");
+               if ( !gLinkContext.allowAtPaths ) {
+                       dyld::log("dyld: warning: @executable_path/ ignored because of amfi policy\n");
                }
                else
-#endif
                {
                        size_t mainExecDirLen = strlen(mainExecutableDir);
                        char* str = new char[mainExecDirLen+len+1];
@@ -1841,7 +1825,7 @@ void processDyldEnvironmentVariable(const char* key, const char* value, const ch
        }
 #endif
        else if ( strcmp(key, "DYLD_IMAGE_SUFFIX") == 0 ) {
-               gLinkContext.imageSuffix = value;
+               gLinkContext.imageSuffix = parseColonList(value, NULL);
        }
        else if ( strcmp(key, "DYLD_INSERT_LIBRARIES") == 0 ) {
                sEnv.DYLD_INSERT_LIBRARIES = parseColonList(value, NULL);
@@ -1929,16 +1913,13 @@ void processDyldEnvironmentVariable(const char* key, const char* value, const ch
        else if ( strcmp(key, "DYLD_PRINT_RPATHS") == 0 ) {
                gLinkContext.verboseRPaths = true;
        }
-       else if ( strcmp(key, "DYLD_PRINT_CS_NOTIFICATIONS") == 0 ) {
-               sEnv.DYLD_PRINT_CS_NOTIFICATIONS = true;
-       }
        else if ( strcmp(key, "DYLD_PRINT_INTERPOSING") == 0 ) {
                gLinkContext.verboseInterposing = true;
        }
        else if ( strcmp(key, "DYLD_PRINT_CODE_SIGNATURES") == 0 ) {
                gLinkContext.verboseCodeSignatures = true;
        }
-       else if ( (strcmp(key, "DYLD_SHARED_REGION") == 0) && !sSafeMode ) {
+       else if ( (strcmp(key, "DYLD_SHARED_REGION") == 0) && gLinkContext.allowEnvVarsSharedCache ) {
                if ( strcmp(value, "private") == 0 ) {
                        gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion;
                }
@@ -1955,12 +1936,17 @@ 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");
                }
        }
-       else if ( (strcmp(key, "DYLD_SHARED_CACHE_DIR") == 0) && !sSafeMode  ) {
+       else if ( (strcmp(key, "DYLD_SHARED_CACHE_DIR") == 0) && gLinkContext.allowEnvVarsSharedCache  ) {
                sSharedCacheOverrideDir = value;
        }
        else if ( strcmp(key, "DYLD_USE_CLOSURES") == 0 ) {
-               if ( dyld3::loader::internalInstall() )
+               if ( dyld3::internalInstall() ) {
+#if __MAC_OS_X_VERSION_MIN_REQUIRED && __i386__
+                       // don't support dyld3 for 32-bit macOS
+#else
                        sEnableClosures = true;
+#endif
+               }
        }
        else if ( strcmp(key, "DYLD_IGNORE_PREBINDING") == 0 ) {
                if ( strcmp(value, "all") == 0 ) {
@@ -1994,7 +1980,7 @@ void processDyldEnvironmentVariable(const char* key, const char* value, const ch
        }
 #endif
 #if !TARGET_IPHONE_SIMULATOR
-       else if ( (strcmp(key, "DYLD_PRINT_TO_FILE") == 0) && (mainExecutableDir == NULL) && !sSafeMode ) {
+       else if ( (strcmp(key, "DYLD_PRINT_TO_FILE") == 0) && (mainExecutableDir == NULL) && gLinkContext.allowEnvVarsSharedCache ) {
                int fd = open(value, O_WRONLY | O_CREAT | O_APPEND, 0644);
                if ( fd != -1 ) {
                        sLogfile = fd;
@@ -2005,7 +1991,7 @@ void processDyldEnvironmentVariable(const char* key, const char* value, const ch
                }
        }
        else if ( (strcmp(key, "DYLD_SKIP_MAIN") == 0)) {
-               if ( dyld3::loader::internalInstall() )
+               if ( dyld3::internalInstall() )
                        sSkipMain = true;
        }
 #endif
@@ -2109,7 +2095,7 @@ static void pruneEnvironmentVariables(const char* envp[], const char*** applep)
 
     // Are we testing dyld on an internal config?
     if ( _simple_getenv(envp, "DYLD_SKIP_MAIN") != NULL ) {
-               if ( dyld3::loader::internalInstall() )
+               if ( dyld3::internalInstall() )
             sSkipMain = true;
     }
 
@@ -2148,7 +2134,7 @@ static void pruneEnvironmentVariables(const char* envp[], const char*** applep)
 static void defaultUninitializedFallbackPaths(const char* envp[])
 {
 #if __MAC_OS_X_VERSION_MIN_REQUIRED
-       if ( gLinkContext.processIsRestricted ) {
+       if ( !gLinkContext.allowClassicFallbackPaths ) {
                sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = sRestrictedFrameworkFallbackPaths;
                sEnv.DYLD_FALLBACK_LIBRARY_PATH   = sRestrictedLibraryFallbackPaths;
                return;
@@ -2186,7 +2172,7 @@ static void defaultUninitializedFallbackPaths(const char* envp[])
 
 static void checkEnvironmentVariables(const char* envp[])
 {
-       if ( sEnvMode == envNone )
+       if ( !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsPrint )
                return;
        const char** p;
        for(p = envp; *p != NULL; p++) {
@@ -2201,7 +2187,7 @@ static void checkEnvironmentVariables(const char* envp[])
                                char key[keyLen+1];
                                strncpy(key, keyEqualsValue, keyLen);
                                key[keyLen] = '\0';
-                               if ( (sEnvMode == envPrintOnly) && (strncmp(key, "DYLD_PRINT_", 11) != 0) )
+                               if ( (strncmp(key, "DYLD_PRINT_", 11) == 0) && !gLinkContext.allowEnvVarsPrint )
                                        continue;
                                processDyldEnvironmentVariable(key, value, NULL);
                        }
@@ -2218,9 +2204,9 @@ static void checkEnvironmentVariables(const char* envp[])
        
 #if SUPPORT_ROOT_PATH
        // <rdar://problem/11281064> DYLD_IMAGE_SUFFIX and DYLD_ROOT_PATH cannot be used together
-       if ( (gLinkContext.imageSuffix != NULL) && (gLinkContext.rootPaths != NULL) ) {
+       if ( (gLinkContext.imageSuffix != NULL && *gLinkContext.imageSuffix != NULL) && (gLinkContext.rootPaths != NULL) ) {
                dyld::warn("Ignoring DYLD_IMAGE_SUFFIX because DYLD_ROOT_PATH is used.\n");
-               gLinkContext.imageSuffix = NULL;
+               gLinkContext.imageSuffix = NULL; // this leaks allocations from parseColonList
        }
 #endif
 }
@@ -2273,6 +2259,9 @@ static void getHostInfo(const macho_header* mainExecutableMH, uintptr_t mainExec
 #elif __ARM_ARCH_7S__
        sHostCPU                = CPU_TYPE_ARM;
        sHostCPUsubtype = CPU_SUBTYPE_ARM_V7S;
+#elif __ARM64_ARCH_8_32__
+       sHostCPU                = CPU_TYPE_ARM64_32;
+       sHostCPUsubtype = CPU_SUBTYPE_ARM64_32_V8;
 #elif __arm64e__
        sHostCPU                = CPU_TYPE_ARM64;
        sHostCPUsubtype = CPU_SUBTYPE_ARM64_E;
@@ -2310,27 +2299,18 @@ static void getHostInfo(const macho_header* mainExecutableMH, uintptr_t mainExec
 #endif
 }
 
-static void checkSharedRegionDisable(const mach_header* mainExecutableMH)
+static void checkSharedRegionDisable(const dyld3::MachOLoaded* mainExecutableMH, uintptr_t mainExecutableSlide)
 {
 #if __MAC_OS_X_VERSION_MIN_REQUIRED
        // if main executable has segments that overlap the shared region,
        // then disable using the shared region
-       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 ) {
+       if ( mainExecutableMH->intersectsRange(SHARED_REGION_BASE, SHARED_REGION_SIZE) ) {
                gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion;
                if ( gLinkContext.verboseMapping )
                        dyld::warn("disabling shared region because main executable overlaps\n");
        }
 #if __i386__
-       if ( gLinkContext.processIsRestricted ) {
+       if ( !gLinkContext.allowEnvVarsPath ) {
                // <rdar://problem/15280847> use private or no shared region for suid processes
                gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion;
        }
@@ -2460,8 +2440,10 @@ static const char* getFrameworkPartialPath(const char* path)
                                        if (  gLinkContext.imageSuffix != NULL ) {
                                                // some debug frameworks have install names that end in _debug
                                                if ( strncmp(framework, &leaf[1], len) == 0 ) {
-                                                       if ( strcmp( gLinkContext.imageSuffix, &leaf[len+1]) == 0 )
-                                                               return frameworkStart;
+                                                       for (const char* const* suffix=gLinkContext.imageSuffix; *suffix != NULL; ++suffix) {
+                                                               if ( strcmp(*suffix, &leaf[len+1]) == 0 )
+                                                                       return frameworkStart;
+                                                       }
                                                }
                                        }
                                }
@@ -2547,6 +2529,19 @@ static const cpu_subtype_t kARM64[kARM64_RowCount][4] = {
        // armv64 can run: 64
        {  CPU_SUBTYPE_ARM64_V8, CPU_SUBTYPE_ARM64_ALL, CPU_SUBTYPE_END_OF_LIST },
 };
+
+#if __ARM64_ARCH_8_32__
+const int kARM64_32_RowCount = 2;
+static const cpu_subtype_t kARM64_32[kARM64_32_RowCount][4] = {
+
+       // armv64_32 can run: v8
+       {  CPU_SUBTYPE_ARM64_32_V8, CPU_SUBTYPE_END_OF_LIST },
+
+       // armv64 can run: 64
+       {  CPU_SUBTYPE_ARM64_V8, CPU_SUBTYPE_ARM64_ALL, CPU_SUBTYPE_END_OF_LIST },
+};
+#endif
+       
 #endif
 
 #if __x86_64__
@@ -2585,6 +2580,16 @@ static const cpu_subtype_t* findCPUSubtypeList(cpu_type_t cpu, cpu_subtype_t sub
                                        return kARM64[i];
                        }
                        break;
+
+#if __ARM64_ARCH_8_32__
+               case CPU_TYPE_ARM64_32:
+                       for (int i=0; i < kARM64_32_RowCount ; ++i) {
+                               if ( kARM64_32[i][0] == subtype )
+                                       return kARM64_32[i];
+                       }
+                       break;
+#endif
+
 #endif
 #if __x86_64__
                case CPU_TYPE_X86_64:
@@ -2855,7 +2860,7 @@ static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, uint
 #if SUPPORT_ACCELERATE_TABLES
 static bool dylibsCanOverrideCache()
 {
-       if ( !dyld3::loader::internalInstall() )
+       if ( !dyld3::internalInstall() )
                return false;
        return ( (sSharedCacheLoadInfo.loadAddress != nullptr) && (sSharedCacheLoadInfo.loadAddress->header.cacheType == kDyldSharedCacheTypeDevelopment) );
 }
@@ -2956,9 +2961,34 @@ static bool isSimulatorBinary(const uint8_t* firstPages, const char* path)
                                // grandfather in a few libSystem dylibs
                                if ((strcmp(path, "/usr/lib/system/libsystem_kernel.dylib") == 0) ||
                                    (strcmp(path, "/usr/lib/system/libsystem_platform.dylib") == 0) ||
-                                   (strcmp(path, "/usr/lib/system/libsystem_pthread.dylib") == 0))
+                                   (strcmp(path, "/usr/lib/system/libsystem_pthread.dylib") == 0) ||
+                                   (strcmp(path, "/usr/lib/system/libsystem_platform_debug.dylib") == 0) ||
+                                   (strcmp(path, "/usr/lib/system/libsystem_pthread_debug.dylib")  == 0))
                                        return true;
                                return false;
+                       case LC_BUILD_VERSION:
+                       {
+                               // Same logic as above, but for LC_BUILD_VERSION instead of legacy load commands
+                               const struct build_version_command* buildVersionCmd = (build_version_command*)cmd;
+                               switch(buildVersionCmd->platform) {
+                                       case PLATFORM_IOSSIMULATOR:
+                                       case PLATFORM_TVOSSIMULATOR:
+                                       case PLATFORM_WATCHOSSIMULATOR:
+                                       case PLATFORM_WATCHOS:
+                                               return true;
+       #if TARGET_OS_IOSMAC
+                                       case 6:
+                                               return true;
+       #endif
+                                       case PLATFORM_MACOS:
+                                               if ((strcmp(path, "/usr/lib/system/libsystem_kernel.dylib") == 0) ||
+                                                       (strcmp(path, "/usr/lib/system/libsystem_platform.dylib") == 0) ||
+                                                       (strcmp(path, "/usr/lib/system/libsystem_pthread.dylib") == 0) ||
+                                                       (strcmp(path, "/usr/lib/system/libsystem_platform_debug.dylib") == 0) ||
+                                                       (strcmp(path, "/usr/lib/system/libsystem_pthread_debug.dylib")  == 0))
+                                                       return true;
+                               }
+                       }
                }
                cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
                if ( cmd > cmdsEnd )
@@ -2968,6 +2998,58 @@ static bool isSimulatorBinary(const uint8_t* firstPages, const char* path)
 }
 #endif
 
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+static bool iOSMacWhiteListed(const char* path)
+{
+       static char*  whiteListBuffer   = nullptr;
+       static size_t whiteListSize     = 0;
+       static bool   tried                     = false;
+       if ( !tried ) {
+               // only try to map file once
+               whiteListBuffer = (char*)mapFileReadOnly("/System/iOSSupport/dyld/macOS-whitelist.txt", whiteListSize);
+               tried = true;
+       }
+       __block bool result = false;
+       if ( whiteListBuffer != nullptr ) {
+               dyld3::forEachLineInFile(whiteListBuffer, whiteListSize, ^(const char* line, bool& stop) {
+                       // lines in the file are prefixes.  Any path that starts with one of these lines is allowed to be unzippered
+                       size_t lineLen = strlen(line);
+                       if ( (*line == '/') && strncmp(line, path, lineLen) == 0 ) {
+                               result = true;
+                               stop = true;
+                       }
+               });
+       }
+       return result;
+}
+
+static bool iOSMacBlackListed(const char* path)
+{
+       static char*  blackListBuffer   = nullptr;
+       static size_t blackListSize     = 0;
+       static bool   tried                     = false;
+       if ( !tried ) {
+               // only try to map file once
+               blackListBuffer = (char*)mapFileReadOnly("/System/iOSSupport/dyld/macOS-blacklist.txt", blackListSize);
+               tried = true;
+       }
+       __block bool result = false;
+       if ( blackListBuffer != nullptr ) {
+               dyld3::forEachLineInFile(blackListBuffer, blackListSize, ^(const char* line, bool& stop) {
+                       // lines in the file are prefixes.  Any path that starts with one of these lines is allowed to be unzippered
+                       size_t lineLen = strlen(line);
+                       if ( (*line == '/') && strncmp(line, path, lineLen) == 0 ) {
+                               result = true;
+                               stop = true;
+                       }
+               });
+       }
+       return result;
+}
+
+
+#endif
+
 // map in file and instantiate an ImageLoader
 static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char* path, const LoadContext& context)
 {
@@ -2980,6 +3062,7 @@ static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char*
                throw "not a file";
 
        uint8_t firstPages[MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE];
+       uint8_t *firstPagesPtr = firstPages;
        bool shortPage = false;
        
        // min mach-o file is 4K
@@ -3013,6 +3096,7 @@ static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char*
        // try mach-o loader
        if ( shortPage ) 
                throw "file too short";
+
        if ( isCompatibleMachO(firstPages, path) ) {
 
                // only MH_BUNDLE, MH_DYLIB, and some MH_EXECUTE can be dynamically loaded
@@ -3053,8 +3137,27 @@ static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char*
                }
 #endif
 
-               // instantiate an image
-               ImageLoader* image = ImageLoaderMachO::instantiateFromFile(path, fd, firstPages, headerAndLoadCommandsSize, fileOffset, fileLength, stat_buf, gLinkContext);
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+               if ( gLinkContext.marzipan ) {
+                       const dyld3::MachOFile* mf = (dyld3::MachOFile*)firstPages;
+                       bool isiOSMacBinary = mf->supportsPlatform(dyld3::Platform::iOSMac) || iOSMacWhiteListed(path);
+                       bool isProhibitedMacOSBinary = !isiOSMacBinary && iOSMacBlackListed(path);
+                       if ( (context.enforceIOSMac && !isiOSMacBinary) || isProhibitedMacOSBinary ) {
+                               throw "mach-o, but not built for iOSMac";
+                       }
+               }
+#endif
+
+#if __arm64e__
+               if ( (sMainExecutableMachHeader->cpusubtype == CPU_SUBTYPE_ARM64_E) && (mh->cpusubtype != CPU_SUBTYPE_ARM64_E) )
+                       throw "arm64 dylibs cannot be loaded into arm64e processes";
+#endif
+               ImageLoader* image = nullptr;
+               {
+                       dyld3::ScopedTimer timer(DBG_DYLD_TIMING_MAP_IMAGE, path, 0, 0);
+                       image = ImageLoaderMachO::instantiateFromFile(path, fd, firstPagesPtr, headerAndLoadCommandsSize, fileOffset, fileLength, stat_buf, gLinkContext);
+                       timer.setData4((uint64_t)image->machHeader());
+               }
                
                // validate
                return checkandAddImage(image, context);
@@ -3132,6 +3235,7 @@ static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const
        bool didStat = false;
        bool existsOnDisk;
        dyld3::SharedCacheFindDylibResults shareCacheResults;
+       shareCacheResults.image = nullptr;
        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) {
@@ -3150,7 +3254,7 @@ static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const
                        return NULL;
                }
                bool useCache = false;
-               if ( shareCacheResults.imageData == nullptr ) {
+               if ( shareCacheResults.image == nullptr ) {
                        // HACK to support old caches
                        existsOnDisk = ( my_stat(path, &statBuf) == 0 );
                        didStat = true;
@@ -3160,14 +3264,17 @@ static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const
                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() ) {
+                       if ( shareCacheResults.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;
+                                       uint64_t expectedINode;
+                                       uint64_t expectedMtime;
+                                       if ( shareCacheResults.image->hasFileModTimeAndInode(expectedINode, expectedMtime) ) {
+                                               if ( (expectedMtime == statBuf.st_mtime) && (expectedINode == statBuf.st_ino) )
+                                                       useCache = true;
+                                       }
                                }
                                else {
                                        if ( !existsOnDisk )
@@ -3179,6 +3286,16 @@ static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const
                        }
                }
                if ( useCache ) {
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+                       if ( gLinkContext.marzipan ) {
+                               const dyld3::MachOFile* mf = (dyld3::MachOFile*)shareCacheResults.mhInCache;
+                               bool isiOSMacBinary = mf->supportsPlatform(dyld3::Platform::iOSMac) || iOSMacWhiteListed(path);
+                               bool isProhibitedMacOSBinary = !isiOSMacBinary && iOSMacBlackListed(path);
+                               if ( (context.enforceIOSMac && !isiOSMacBinary) || isProhibitedMacOSBinary ) {
+                                       throw "mach-o, but not built for iOSMac";
+                               }
+                       }
+#endif
                        ImageLoader* imageLoader = ImageLoaderMachO::instantiateFromCache((macho_header*)shareCacheResults.mhInCache, shareCacheResults.pathInCache, shareCacheResults.slideInCache, statBuf, gLinkContext);
                        return checkandAddImage(imageLoader, context);
                }
@@ -3199,8 +3316,13 @@ static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const
                        return NULL;
                // try opening file
                imageLoader = loadPhase5open(path, context, statBuf, exceptions);
-               if ( imageLoader != NULL )
+               if ( imageLoader != NULL ) {
+                       if ( shareCacheResults.image != nullptr ) {
+                               // if image was found in cache, but is overridden by a newer file on disk, record what the image overrides
+                               imageLoader->setOverridesCachedDylib(shareCacheResults.image->imageNum());
+                       }
                        return imageLoader;
+               }
        }
 
        // just return NULL if file not found, but record any other errors
@@ -3282,10 +3404,20 @@ static ImageLoader* loadPhase4(const char* path, const char* orgPath, const Load
 {
        //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
        ImageLoader* image = NULL;
-       if (  gLinkContext.imageSuffix != NULL ) {
-               char pathWithSuffix[strlen(path)+strlen( gLinkContext.imageSuffix)+2];
-               ImageLoader::addSuffix(path,  gLinkContext.imageSuffix, pathWithSuffix);
-               image = loadPhase5(pathWithSuffix, orgPath, context, cacheIndex, exceptions);
+       if ( gLinkContext.imageSuffix != NULL ) {
+               for (const char* const* suffix=gLinkContext.imageSuffix; *suffix != NULL; ++suffix) {
+                       char pathWithSuffix[strlen(path)+strlen(*suffix)+2];
+                       ImageLoader::addSuffix(path, *suffix, pathWithSuffix);
+                       image = loadPhase5(pathWithSuffix, orgPath, context, cacheIndex, exceptions);
+                       if ( image != NULL )
+                               break;
+               }
+               if ( image != NULL ) {
+                       // if original path is in the dyld cache, then mark this one found as an override
+                       dyld3::SharedCacheFindDylibResults shareCacheResults;
+                       if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, path, &shareCacheResults) && (shareCacheResults.image != nullptr) )
+                               image->setOverridesCachedDylib(shareCacheResults.image->imageNum());
+               }
        }
        if ( image == NULL )
                image = loadPhase5(path, orgPath, context, cacheIndex, exceptions);
@@ -3303,11 +3435,9 @@ static ImageLoader* loadPhase3(const char* path, const char* orgPath, const Load
        //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
        ImageLoader* image = NULL;
        if ( strncmp(path, "@executable_path/", 17) == 0 ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
                // executable_path cannot be in used in any binary in a setuid process rdar://problem/4589305
-               if ( gLinkContext.processIsRestricted )
+               if ( !gLinkContext.allowAtPaths )
                        throwf("unsafe use of @executable_path in %s with restricted binary", context.origin);
-#endif
                // handle @executable_path path prefix
                const char* executablePath = sExecPath;
                char newPath[strlen(executablePath) + strlen(path)];
@@ -3337,11 +3467,9 @@ static ImageLoader* loadPhase3(const char* path, const char* orgPath, const Load
                }
        }
        else if ( (strncmp(path, "@loader_path/", 13) == 0) && (context.origin != NULL) ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
                // @loader_path cannot be used from the main executable of a setuid process rdar://problem/4589305
-               if ( gLinkContext.processIsRestricted && (strcmp(context.origin, sExecPath) == 0) )
+               if ( !gLinkContext.allowAtPaths  && (strcmp(context.origin, sExecPath) == 0) )
                        throwf("unsafe use of @loader_path in %s with restricted binary", context.origin);
-#endif
                // handle @loader_path path prefix
                char newPath[strlen(context.origin) + strlen(path)];
                strcpy(newPath, context.origin);
@@ -3405,12 +3533,10 @@ static ImageLoader* loadPhase3(const char* path, const char* orgPath, const Load
                if ( (exceptions != NULL) && (trailingPath != path) )
                        return NULL;
        }
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-       else if ( gLinkContext.processIsRestricted && (path[0] != '/' ) ) {
+       else if ( !gLinkContext.allowEnvVarsPath && (path[0] != '/' ) ) {
                throwf("unsafe use of relative rpath %s in %s with restricted binary", path, context.origin);
        }
-#endif
-       
+
        return loadPhase4(path, orgPath, context, cacheIndex, exceptions);
 }
 
@@ -3453,8 +3579,13 @@ static ImageLoader* loadPhase2(const char* path, const char* orgPath, const Load
                                // Look in the cache if appropriate
                                if ( image == NULL)
                                        image = loadPhase2cache(npath, orgPath, context, cacheIndex, exceptions);
-                               if ( image != NULL )
+                               if ( image != NULL ) {
+                                       // if original path is in the dyld cache, then mark this one found as an override
+                                       dyld3::SharedCacheFindDylibResults shareCacheResults;
+                                       if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, path, &shareCacheResults) && (shareCacheResults.image != nullptr) )
+                                               image->setOverridesCachedDylib(shareCacheResults.image->imageNum());
                                        return image;
+                               }
                        }
                }
        }
@@ -3473,8 +3604,13 @@ static ImageLoader* loadPhase2(const char* path, const char* orgPath, const Load
                        // Look in the cache if appropriate
                        if ( image == NULL)
                                image = loadPhase2cache(libpath, orgPath, context, cacheIndex, exceptions);
-                       if ( image != NULL )
+                       if ( image != NULL ) {
+                               // if original path is in the dyld cache, then mark this one found as an override
+                               dyld3::SharedCacheFindDylibResults shareCacheResults;
+                               if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, path, &shareCacheResults) && (shareCacheResults.image != nullptr) )
+                                       image->setOverridesCachedDylib(shareCacheResults.image->imageNum());
                                return image;
+                       }
                }
        }
        return NULL;
@@ -3523,16 +3659,41 @@ static ImageLoader* loadPhase0(const char* path, const char* orgPath, const Load
 {
        //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
 
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+       // handle macOS dylibs dlopen()ing versioned path which needs to map to flat path in mazipan simulator
+       if ( gLinkContext.marzipan && strstr(path, ".framework/Versions/")) {
+               uintptr_t sourceOffset = 0;
+               uintptr_t destOffset = 0;
+               size_t sourceLangth = strlen(path);
+               char flatPath[sourceLangth];
+               flatPath[0] = 0;
+               const char* frameworkBase = NULL;
+               while ((frameworkBase = strstr(&path[sourceOffset], ".framework/Versions/"))) {
+                       uintptr_t foundLength = (frameworkBase - &path[sourceOffset]) + strlen(".framework/") ;
+                       strlcat(&flatPath[destOffset], &path[sourceOffset], foundLength);
+                       sourceOffset += foundLength + strlen("Versions/") + 1;
+                       destOffset += foundLength - 1;
+               }
+               strlcat(&flatPath[destOffset], &path[sourceOffset], sourceLangth);
+               ImageLoader* image = loadPhase0(flatPath, orgPath, context, cacheIndex, exceptions);
+               if ( image != NULL )
+                       return image;
+       }
+#endif
+       
 #if SUPPORT_ROOT_PATH
        // handle DYLD_ROOT_PATH which forces absolute paths to use a new root
        if ( (gLinkContext.rootPaths != NULL) && (path[0] == '/') ) {
-               for(const char* const* rootPath = gLinkContext.rootPaths ; *rootPath != NULL; ++rootPath) {
-                       char newPath[strlen(*rootPath) + strlen(path)+2];
-                       strcpy(newPath, *rootPath);
-                       strcat(newPath, path);
-                       ImageLoader* image = loadPhase1(newPath, orgPath, context, cacheIndex, exceptions);
-                       if ( image != NULL )
-                               return image;
+               for(const char* const* rootPath = gLinkContext.rootPaths; *rootPath != NULL; ++rootPath) {
+                       size_t rootLen = strlen(*rootPath);
+                       if ( strncmp(path, *rootPath, rootLen) != 0 ) {
+                               char newPath[rootLen + strlen(path)+2];
+                               strcpy(newPath, *rootPath);
+                               strcat(newPath, path);
+                               ImageLoader* image = loadPhase1(newPath, orgPath, context, cacheIndex, exceptions);
+                               if ( image != NULL )
+                                       return image;
+                       }
                }
        }
 #endif
@@ -3572,7 +3733,7 @@ ImageLoader* load(const char* path, const LoadContext& context, unsigned& cacheI
        //dyld::log("%s(%s)\n", __func__ , path);
        char realPath[PATH_MAX];
        // when DYLD_IMAGE_SUFFIX is in used, do a realpath(), otherwise a load of "Foo.framework/Foo" will not match
-       if ( context.useSearchPaths && ( gLinkContext.imageSuffix != NULL) ) {
+       if ( context.useSearchPaths && ( gLinkContext.imageSuffix != NULL && *gLinkContext.imageSuffix != NULL) ) {
                if ( realpath(path, realPath) != NULL )
                        path = realPath;
        }
@@ -3639,6 +3800,8 @@ 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
@@ -3649,6 +3812,7 @@ static void mapSharedCache()
 
        // update global state
        if ( sSharedCacheLoadInfo.loadAddress != nullptr ) {
+               gLinkContext.dyldCache                                                          = sSharedCacheLoadInfo.loadAddress;
                dyld::gProcessInfo->processDetachedFromSharedRegion = opts.forcePrivate;
                dyld::gProcessInfo->sharedCacheSlide                = sSharedCacheLoadInfo.slide;
                dyld::gProcessInfo->sharedCacheBaseAddress          = (unsigned long)sSharedCacheLoadInfo.loadAddress;
@@ -3690,6 +3854,7 @@ ImageLoader* cloneImage(ImageLoader* image)
        context.mustBeBundle            = true;
        context.mustBeDylib                     = false;
        context.canBePIE                        = false;
+       context.enforceIOSMac           = false;
        context.origin                          = NULL;
        context.rpath                           = NULL;
        return loadPhase6(file.getFileDescriptor(), stat_buf, image->getPath(), context);
@@ -3745,20 +3910,47 @@ void registerAddCallback(ImageCallback func)
        // call callback with all existing images
        for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
                ImageLoader* image = *it;
-               if ( image->getState() >= dyld_image_state_bound && image->getState() < dyld_image_state_terminated )
+               if ( image->getState() >= dyld_image_state_bound && image->getState() < dyld_image_state_terminated ) {
+                       dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)image->machHeader(), (uint64_t)(*func), 0);
                        (*func)(image->machHeader(), image->getSlide());
+               }
        }
 #if SUPPORT_ACCELERATE_TABLES
        if ( sAllCacheImagesProxy != NULL ) {
                dyld_image_info infos[allImagesCount()+1];
                unsigned cacheCount = sAllCacheImagesProxy->appendImagesToNotify(dyld_image_state_bound, true, infos);
                for (unsigned i=0; i < cacheCount; ++i) {
+                       dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)infos[i].imageLoadAddress, (uint64_t)(*func), 0);
                        (*func)(infos[i].imageLoadAddress, sSharedCacheLoadInfo.slide);
                }
        }
 #endif
 }
 
+void registerLoadCallback(LoadImageCallback func)
+{
+       // now add to list to get notified when any more images are added
+       sAddLoadImageCallbacks.push_back(func);
+
+       // call callback with all existing images
+       for (ImageLoader* image : sAllImages) {
+               if ( image->getState() >= dyld_image_state_bound && image->getState() < dyld_image_state_terminated ) {
+                       dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)image->machHeader(), (uint64_t)(*func), 0);
+                       (*func)(image->machHeader(), image->getPath(), !image->neverUnload());
+               }
+       }
+#if SUPPORT_ACCELERATE_TABLES
+       if ( sAllCacheImagesProxy != NULL ) {
+               dyld_image_info infos[allImagesCount()+1];
+               unsigned cacheCount = sAllCacheImagesProxy->appendImagesToNotify(dyld_image_state_bound, true, infos);
+               for (unsigned i=0; i < cacheCount; ++i) {
+                       dyld3::ScopedTimer timer(DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, (uint64_t)infos[i].imageLoadAddress, (uint64_t)(*func), 0);
+                       (*func)(infos[i].imageLoadAddress, infos[i].imageFilePath, false);
+               }
+       }
+#endif
+}
+
 void registerRemoveCallback(ImageCallback func)
 {
        // <rdar://problem/15025198> ignore calls to register a notification during a notification
@@ -3925,11 +4117,13 @@ static void undefinedHandler(const char* symboName)
        }
 }
 
-static bool findExportedSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image)
+static bool findExportedSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image, ImageLoader::CoalesceNotifier notifier=NULL)
 {
        // search all images in order
        const ImageLoader* firstWeakImage = NULL;
        const ImageLoader::Symbol* firstWeakSym = NULL;
+       const ImageLoader* firstNonWeakImage = NULL;
+       const ImageLoader::Symbol* firstNonWeakSym = NULL;
        const size_t imageCount = sAllImages.size();
        for(size_t i=0; i < imageCount; ++i) {
                ImageLoader* anImage = sAllImages[i];
@@ -3941,23 +4135,42 @@ static bool findExportedSymbol(const char* name, bool onlyInCoalesced, const Ima
                        else if ( i == sInsertedDylibCount )
                                anImage = sAllImages[0];
                }
+               //dyld::log("findExportedSymbol(%s) looking at %s\n", name, anImage->getPath());
                if ( ! anImage->hasHiddenExports() && (!onlyInCoalesced || anImage->hasCoalescedExports()) ) {
-                       *sym = anImage->findExportedSymbol(name, false, image);
+                       const ImageLoader* foundInImage;
+                       *sym = anImage->findExportedSymbol(name, false, &foundInImage);
+                       //dyld::log("findExportedSymbol(%s) found: sym=%p, anImage=%p, foundInImage=%p\n", name, *sym, anImage, foundInImage /*, (foundInImage ? foundInImage->getPath() : "")*/);
                        if ( *sym != NULL ) {
+                               if ( notifier && (foundInImage == anImage) )
+                                       notifier(*sym, foundInImage, foundInImage->machHeader());
                                // if weak definition found, record first one found
-                               if ( ((*image)->getExportedSymbolInfo(*sym) & ImageLoader::kWeakDefinition) != 0 ) {
+                               if ( (foundInImage->getExportedSymbolInfo(*sym) & ImageLoader::kWeakDefinition) != 0 ) {
                                        if ( firstWeakImage == NULL ) {
-                                               firstWeakImage = *image;
+                                               firstWeakImage = foundInImage;
                                                firstWeakSym = *sym;
                                        }
                                }
                                else {
-                                       // found non-weak, so immediately return with it
-                                       return true;
+                                       // found non-weak
+                                       if ( !onlyInCoalesced ) {
+                                               // for flat lookups, return first found
+                                               *image = foundInImage;
+                                               return true;
+                                       }
+                                       if ( firstNonWeakImage == NULL ) {
+                                               firstNonWeakImage = foundInImage;
+                                               firstNonWeakSym = *sym;
+                                       }
                                }
                        }
                }
        }
+       if ( firstNonWeakImage != NULL ) {
+               // found a weak definition, but no non-weak, so return first weak found
+               *sym = firstNonWeakSym;
+               *image = firstNonWeakImage;
+               return true;
+       }
        if ( firstWeakSym != NULL ) {
                // found a weak definition, but no non-weak, so return first weak found
                *sym = firstWeakSym;
@@ -3966,7 +4179,7 @@ static bool findExportedSymbol(const char* name, bool onlyInCoalesced, const Ima
        }
 #if SUPPORT_ACCELERATE_TABLES
        if ( sAllCacheImagesProxy != NULL ) {
-               if ( sAllCacheImagesProxy->flatFindSymbol(name, onlyInCoalesced, sym, image) )
+               if ( sAllCacheImagesProxy->flatFindSymbol(name, onlyInCoalesced, sym, image, notifier) )
                        return true;
        }
 #endif
@@ -3979,9 +4192,9 @@ bool flatFindExportedSymbol(const char* name, const ImageLoader::Symbol** sym, c
        return findExportedSymbol(name, false, sym, image);
 }
 
-bool findCoalescedExportedSymbol(const char* name, const ImageLoader::Symbol** sym, const ImageLoader** image)
+bool findCoalescedExportedSymbol(const char* name, const ImageLoader::Symbol** sym, const ImageLoader** image, ImageLoader::CoalesceNotifier notifier)
 {
-       return findExportedSymbol(name, true, sym, image);
+       return findExportedSymbol(name, true, sym, image, notifier);
 }
 
 
@@ -4107,6 +4320,7 @@ void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_in
        for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
                ImageLoader* image = *it;
                if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
+                       dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
                        (*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
                }
        }
@@ -4210,7 +4424,7 @@ bool dladdrFromCache(const void* address, Dl_info* info)
 }
 #endif
 
-static ImageLoader* libraryLocator(const char* libraryName, bool search, const char* origin, const ImageLoader::RPathChain* rpaths, unsigned& cacheIndex)
+static ImageLoader* libraryLocator(const char* libraryName, bool search, const char* origin, const ImageLoader::RPathChain* rpaths, bool enforceIOSMac, unsigned& cacheIndex)
 {
        dyld::LoadContext context;
        context.useSearchPaths          = search;
@@ -4222,6 +4436,7 @@ static ImageLoader* libraryLocator(const char* libraryName, bool search, const c
        context.mustBeBundle            = false;
        context.mustBeDylib                     = true;
        context.canBePIE                        = false;
+       context.enforceIOSMac           = enforceIOSMac;
        context.origin                          = origin;
        context.rpath                           = rpaths;
        return load(libraryName, context, cacheIndex);
@@ -4534,30 +4749,32 @@ void garbageCollectImages()
                }
 
                // collect phase: run termination routines for images not marked in-use
-               __cxa_range_t ranges[maxRangeCount];
-               int rangeCount = 0;
-               for (unsigned i=0; i < deadCount; ++i) {
-                       ImageLoader* image = deadImages[i];
-                       for (unsigned int j=0; j < image->segmentCount(); ++j) {
-                               if ( !image->segExecutable(j) )
-                                       continue;
-                               if ( rangeCount < maxRangeCount ) {
-                                       ranges[rangeCount].addr = (const void*)image->segActualLoadAddress(j);
-                                       ranges[rangeCount].length = image->segSize(j);
-                                       ++rangeCount;
+               if ( maxRangeCount != 0 ) {
+                       __cxa_range_t ranges[maxRangeCount];
+                       int rangeCount = 0;
+                       for (unsigned i=0; i < deadCount; ++i) {
+                               ImageLoader* image = deadImages[i];
+                               for (unsigned int j=0; j < image->segmentCount(); ++j) {
+                                       if ( !image->segExecutable(j) )
+                                               continue;
+                                       if ( rangeCount < maxRangeCount ) {
+                                               ranges[rangeCount].addr = (const void*)image->segActualLoadAddress(j);
+                                               ranges[rangeCount].length = image->segSize(j);
+                                               ++rangeCount;
+                                       }
+                               }
+                               try {
+                                       runImageStaticTerminators(image);
+                               }
+                               catch (const char* msg) {
+                                       dyld::warn("problem running terminators for image: %s\n", msg);
                                }
                        }
-                       try {
-                               runImageStaticTerminators(image);
-                       }
-                       catch (const char* msg) {
-                               dyld::warn("problem running terminators for image: %s\n", msg);
-                       }
+
+                       // <rdar://problem/14718598> dyld should call __cxa_finalize_ranges()
+                       if ( (rangeCount > 0) && (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 13) )
+                               (*gLibSystemHelpers->cxa_finalize_ranges)(ranges, rangeCount);
                }
-               
-               // <rdar://problem/14718598> dyld should call __cxa_finalize_ranges()
-               if ( (rangeCount > 0) && (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 13) )
-                       (*gLibSystemHelpers->cxa_finalize_ranges)(ranges, rangeCount);
 
                // collect phase: delete all images which are not marked in-use
                bool mightBeMore;
@@ -4632,21 +4849,16 @@ static void loadInsertedDylib(const char* path)
                context.mustBeBundle            = false;
                context.mustBeDylib                     = true;
                context.canBePIE                        = false;
+               context.enforceIOSMac           = true;
                context.origin                          = NULL; // can't use @loader_path with DYLD_INSERT_LIBRARIES
                context.rpath                           = NULL;
                image = load(path, context, cacheIndex);
        }
        catch (const char* msg) {
-#if TARGET_IPHONE_SIMULATOR
-               dyld::log("dyld: warning: could not load inserted library '%s' because %s\n", path, msg);
-#else
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-               if ( gLinkContext.processUsingLibraryValidation )
-                       dyld::log("dyld: warning: could not load inserted library '%s' into library validated process because %s\n", path, msg);
+               if ( gLinkContext.allowInsertFailures )
+                       dyld::log("dyld: warning: could not load inserted library '%s' into hardened process because %s\n", path, msg);
                else
-#endif
                        halt(dyld::mkstringf("could not load inserted library '%s' because %s\n", path, msg));
-#endif
        }
        catch (...) {
                halt(dyld::mkstringf("could not load inserted library '%s'\n", path));
@@ -4654,78 +4866,66 @@ static void loadInsertedDylib(const char* path)
 }
 
 
-//
-// Sets:
-//     sEnvMode
-//     gLinkContext.requireCodeSignature
-//     gLinkContext.processIsRestricted                                // Mac OS X only
-//     gLinkContext.processUsingLibraryValidation              // Mac OS X only
-//
 static void configureProcessRestrictions(const macho_header* mainExecutableMH)
 {
+       uint64_t amfiInputFlags = 0;
 #if TARGET_IPHONE_SIMULATOR
-       sEnvMode = envAll;
-       gLinkContext.requireCodeSignature = true;
+       amfiInputFlags |= AMFI_DYLD_INPUT_PROC_IN_SIMULATOR;
+#elif __MAC_OS_X_VERSION_MIN_REQUIRED
+       if ( hasRestrictedSegment(mainExecutableMH) )
+               amfiInputFlags |= AMFI_DYLD_INPUT_PROC_HAS_RESTRICT_SEG;
 #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 ) {
-                               // Xcode built app for Debug allowed to use DYLD_* variables
-                               sEnvMode = envAll;
+       if ( isFairPlayEncrypted(mainExecutableMH) )
+               amfiInputFlags |= AMFI_DYLD_INPUT_PROC_IS_ENCRYPTED;
+#endif
+       uint64_t amfiOutputFlags = 0;
+       if ( amfi_check_dyld_policy_self(amfiInputFlags, &amfiOutputFlags) == 0 ) {
+               gLinkContext.allowAtPaths                               = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_AT_PATH);
+               gLinkContext.allowEnvVarsPrint                  = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_PRINT_VARS);
+               gLinkContext.allowEnvVarsPath                   = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_PATH_VARS);
+               gLinkContext.allowEnvVarsSharedCache    = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_CUSTOM_SHARED_CACHE);
+               gLinkContext.allowClassicFallbackPaths  = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_FALLBACK_PATHS);
+               gLinkContext.allowInsertFailures        = (amfiOutputFlags & AMFI_DYLD_OUTPUT_ALLOW_FAILED_LIBRARY_INSERTION);
+       }
+       else {
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+               // support chrooting from old kernel
+               bool isRestricted = false;
+               bool libraryValidation = false;
+               // any processes with setuid or setgid bit set or with __RESTRICT segment is restricted
+               if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) {
+                       isRestricted = 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 ) {
+                               isRestricted = true;
                        }
-                       else {
-                               // Development kernel can use DYLD_PRINT_* variables on any FairPlay encrypted app
-                               uint32_t secureValue = 0;
-                               size_t   secureValueSize = sizeof(secureValue);
-                               if ( (sysctlbyname("kern.secure_kernel", &secureValue, &secureValueSize, NULL, 0) == 0) && (secureValue == 0) && isFairPlayEncrypted(mainExecutableMH) ) {
-                                       sEnvMode = envPrintOnly;
-                               }
+                       // Library Validation loosens searching but requires everything to be code signed
+                       if ( flags & CS_REQUIRE_LV ) {
+                               isRestricted = false;
+                               libraryValidation = true;
                        }
                }
-               else {
-                       // Development kernel can run unsigned code
-                       sEnvMode = envAll;
-                       gLinkContext.requireCodeSignature = false;
-               }
-       }
-       if ( issetugid() ) {
-               sEnvMode = envNone;
-       }
-#elif __MAC_OS_X_VERSION_MIN_REQUIRED
-       sEnvMode = envAll;
-       gLinkContext.requireCodeSignature = false;
-       gLinkContext.processIsRestricted = false;
-       gLinkContext.processUsingLibraryValidation = false;
-       // any processes with setuid or setgid bit set or with __RESTRICT segment is restricted
-    if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) {
-               gLinkContext.processIsRestricted = true;
-       }
-       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 ) {
-                       gLinkContext.processIsRestricted = true;
-               }
-               // Library Validation loosens searching but requires everything to be code signed
-               if ( flags & CS_REQUIRE_LV ) {
-                       gLinkContext.processIsRestricted = false;
-                       //gLinkContext.requireCodeSignature = true;
-                       gLinkContext.processUsingLibraryValidation = true;
-                       sSafeMode = usingSIP;
-               }
-       }
+               gLinkContext.allowAtPaths                = !isRestricted;
+               gLinkContext.allowEnvVarsPrint           = !isRestricted;
+               gLinkContext.allowEnvVarsPath            = !isRestricted;
+               gLinkContext.allowEnvVarsSharedCache     = !libraryValidation || !usingSIP;
+               gLinkContext.allowClassicFallbackPaths   = !isRestricted;
+               gLinkContext.allowInsertFailures         = false;
+#else
+               halt("amfi_check_dyld_policy_self() failed\n");
 #endif
+       }
 }
 
 
 bool processIsRestricted()
 {
 #if __MAC_OS_X_VERSION_MIN_REQUIRED
-       return gLinkContext.processIsRestricted;
+       return !gLinkContext.allowEnvVarsPath;
 #else
        return false;
 #endif
@@ -4784,7 +4984,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 = {
-               8,
+               12,
                // added in version 1
                (open_proc_t)&open, 
                &close, 
@@ -4843,7 +5043,18 @@ static SyscallHelpers sSysCalls = {
                &task_info,
                &thread_info,
                &kdebug_is_enabled,
-               &kdebug_trace
+               &kdebug_trace,
+               // Added in version 9
+               &kdebug_trace_string,
+               // Added in version 10
+               &amfi_check_dyld_policy_self,
+               // Added in version 11
+               &notifyMonitoringDyldMain,
+               &notifyMonitoringDyld,
+               // Add in version 12
+               &mach_msg_destroy,
+               &mach_port_construct,
+               &mach_port_destruct
 };
 
 __attribute__((noinline))
@@ -4854,12 +5065,16 @@ static const char* useSimulatorDyld(int fd, const macho_header* mainExecutableMH
        *startGlue = 0;
        *mainAddr = 0;
 
+       // <rdar://35873436> HACK to allow marzipan dyld_sim to run entitled processes
+       if ( strncmp(dyldPath, "/System/", 8) != 0 ) {
+               uint32_t flags;
+               if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) == -1 )
+                       return "csops() failed";
+               if ( (flags & CS_RESTRICT) == CS_RESTRICT )
+                       return "dyld_sim cannot be loaded in a restricted process";
+       }
+
        // <rdar://problem/25311921> simulator does not support restricted processes
-       uint32_t flags;
-       if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) == -1 )
-               return "csops() failed";
-       if ( (flags & CS_RESTRICT) == CS_RESTRICT )
-               return "dyld_sim cannot be loaded in a restricted process";
        if ( issetugid() )
                return "dyld_sim cannot be loaded in a setuid process";
        if ( hasRestrictedSegment(mainExecutableMH) )
@@ -4901,6 +5116,7 @@ static const char* useSimulatorDyld(int fd, const macho_header* mainExecutableMH
                return "dyld_sim load commands to large";
        if ( (sizeof(macho_header) + mh->sizeofcmds) > 4096 )
                return "dyld_sim load commands to large";
+       struct linkedit_data_command* codeSigCmd = NULL;
        const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header));
        const struct load_command* const endCmds = (struct load_command*)(((char*)mh) + sizeof(macho_header) + mh->sizeofcmds);
        const struct load_command* cmd = cmds;
@@ -4947,21 +5163,48 @@ static const char* useSimulatorDyld(int fd, const macho_header* mainExecutableMH
                                break;
                        case LC_SEGMENT_COMMAND_WRONG:
                                return "dyld_sim wrong load segment load command";
+                       case LC_CODE_SIGNATURE:
+                               codeSigCmd = (struct linkedit_data_command*)cmd;
+                               break;
                }
                cmd = nextCmd;
        }
        // last segment must be named __LINKEDIT and not writable
+       if ( lastSeg == NULL )
+               return "dyld_sim has no segments";
        if ( strcmp(lastSeg->segname, "__LINKEDIT") != 0 )
                return "dyld_sim last segment not __LINKEDIT";
        if ( lastSeg->initprot & VM_PROT_WRITE )
                return "dyld_sim __LINKEDIT segment writable";
 
+       // must have code signature which is contained within LINKEDIT segment
+       if ( codeSigCmd == NULL )
+               return "dyld_sim not code signed";
+       if ( codeSigCmd->dataoff < lastSeg->fileoff )
+               return "dyld_sim code signature not in __LINKEDIT";
+       if ( (codeSigCmd->dataoff + codeSigCmd->datasize) <  codeSigCmd->dataoff )
+               return "dyld_sim code signature size wraps";
+       if ( (codeSigCmd->dataoff + codeSigCmd->datasize) > (lastSeg->fileoff + lastSeg->filesize) )
+               return "dyld_sim code signature extends beyond __LINKEDIT";
+
+       // register code signature with kernel before mmap()ing segments
+       fsignatures_t siginfo;
+       siginfo.fs_file_start=fileOffset;                                                       // start of mach-o slice in fat file
+       siginfo.fs_blob_start=(void*)(long)(codeSigCmd->dataoff);       // start of code-signature in mach-o file
+       siginfo.fs_blob_size=codeSigCmd->datasize;                                      // size of code-signature
+       int result = fcntl(fd, F_ADDFILESIGS_FOR_DYLD_SIM, &siginfo);
+       if ( result == -1 ) {
+               return mkstringf("dyld_sim fcntl(F_ADDFILESIGS_FOR_DYLD_SIM) failed with errno=%d", errno);
+       }
+       // file range covered by code signature must extend up to code signature itself
+       if ( siginfo.fs_file_start < codeSigCmd->dataoff )
+               return mkstringf("dyld_sim code signature does not cover all of dyld_sim. Signature covers up to 0x%08lX. Signature starts at 0x%08X", (unsigned long)siginfo.fs_file_start, codeSigCmd->dataoff);
+
        // reserve space, then mmap each segment
        vm_address_t loadAddress = 0;
        if ( ::vm_allocate(mach_task_self(), &loadAddress, mappingSize, VM_FLAGS_ANYWHERE) != 0 )
                return "dyld_sim cannot allocate space";
        cmd = cmds;
-       struct linkedit_data_command* codeSigCmd = NULL;
        struct source_version_command* dyldVersionCmd = NULL;
        for (uint32_t i = 0; i < cmd_count; ++i) {
                switch (cmd->cmd) {
@@ -4977,38 +5220,13 @@ static const char* useSimulatorDyld(int fd, const macho_header* mainExecutableMH
                                                return "dyld_sim mmap() to wrong location";
                                }
                                break;
-                       case LC_CODE_SIGNATURE:
-                               codeSigCmd = (struct linkedit_data_command*)cmd;
-                               break;
                        case LC_SOURCE_VERSION:
                                dyldVersionCmd = (struct source_version_command*)cmd;
                                break;
                }
                cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
        }
-
-       // must have code signature which is contained within LINKEDIT segment
-       if ( codeSigCmd == NULL )
-               return "dyld_sim not code signed";
-       if ( codeSigCmd->dataoff < lastSeg->fileoff )
-               return "dyld_sim code signature not in __LINKEDIT";
-       if ( (codeSigCmd->dataoff + codeSigCmd->datasize) <  codeSigCmd->dataoff )
-               return "dyld_sim code signature size wraps";
-       if ( (codeSigCmd->dataoff + codeSigCmd->datasize) > (lastSeg->fileoff + lastSeg->filesize) )
-               return "dyld_sim code signature extends beyond __LINKEDIT";
-
-       fsignatures_t siginfo;
-       siginfo.fs_file_start=fileOffset;                                                       // start of mach-o slice in fat file 
-       siginfo.fs_blob_start=(void*)(long)(codeSigCmd->dataoff);       // start of code-signature in mach-o file
-       siginfo.fs_blob_size=codeSigCmd->datasize;                                      // size of code-signature
-       int result = fcntl(fd, F_ADDFILESIGS_FOR_DYLD_SIM, &siginfo);
-       if ( result == -1 ) {
-               return mkstringf("dyld_sim fcntl(F_ADDFILESIGS_FOR_DYLD_SIM) failed with errno=%d", errno);
-       }
        close(fd);
-       // file range covered by code signature must extend up to code signature itself
-       if ( siginfo.fs_file_start < codeSigCmd->dataoff )
-               return mkstringf("dyld_sim code signature does not cover all of dyld_sim. Signature covers up to 0x%08lX. Signature starts at 0x%08X", (unsigned long)siginfo.fs_file_start, codeSigCmd->dataoff);
 
        // walk newly mapped dyld_sim __TEXT load commands to find entry point
        uintptr_t entry = 0;
@@ -5036,6 +5254,8 @@ static const char* useSimulatorDyld(int fd, const macho_header* mainExecutableMH
                }
                cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
        }
+       if ( entry == 0 )
+               return "dyld_sim entry not found";
 
        // notify debugger that dyld_sim is loaded
        dyld_image_info info;
@@ -5072,10 +5292,10 @@ fake_main()
 
 
 
-static bool envVarMatches(dyld3::launch_cache::Closure mainClosure, const char* envp[], const char* varName)
+static bool envVarMatches(const dyld3::closure::LaunchClosure* mainClosure, const char* envp[], const char* varName)
 {
        __block const char* valueFromClosure = nullptr;
-       mainClosure.forEachEnvVar(^(const char* keyEqualValue, bool& stop) {
+       mainClosure->forEachEnvVar(^(const char* keyEqualValue, bool& stop) {
                size_t keyLen = strlen(varName);
                if ( (strncmp(varName, keyEqualValue, keyLen) == 0) && (keyEqualValue[keyLen] == '=') ) {
                        valueFromClosure = &keyEqualValue[keyLen+1];
@@ -5106,12 +5326,12 @@ static const char* const sEnvVarsToCheck[] = {
        "DYLD_ROOT_PATH"
 };
 
-static bool envVarsMatch(dyld3::launch_cache::Closure mainClosure, const char* envp[])
+static bool envVarsMatch(const dyld3::closure::LaunchClosure* 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);
+                               dyld::log("dyld: closure %p not used because %s changed\n", mainClosure, envVar);
                        return false;
                }
        }
@@ -5120,98 +5340,118 @@ static bool envVarsMatch(dyld3::launch_cache::Closure mainClosure, const char* e
        // <rdar://problem/37004660> dyld3: support DYLD_VERSIONED_*_PATHs ?
        if ( sEnv.DYLD_VERSIONED_LIBRARY_PATH != nullptr ) {
                if ( gLinkContext.verboseWarnings )
-                       dyld::log("dyld: closure %p not used because DYLD_VERSIONED_LIBRARY_PATH used\n", mainClosure.binaryData());
+                       dyld::log("dyld: closure %p not used because DYLD_VERSIONED_LIBRARY_PATH used\n", mainClosure);
                return false;
        }
        if ( sEnv.DYLD_VERSIONED_FRAMEWORK_PATH != nullptr ) {
                if ( gLinkContext.verboseWarnings )
-                       dyld::log("dyld: closure %p not used because DYLD_VERSIONED_FRAMEWORK_PATH used\n", mainClosure.binaryData());
+                       dyld::log("dyld: closure %p not used because DYLD_VERSIONED_FRAMEWORK_PATH used\n", mainClosure);
                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[])
+static bool closureValid(const dyld3::closure::LaunchClosure* mainClosure, const dyld3::closure::LoadedFileInfo& mainFileInfo,
+                                                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;
+               // verify current dyld cache is same as expected
+               uuid_t expectedCacheUUID;
+               if ( mainClosure->builtAgainstDyldCache(expectedCacheUUID) ) {
+                       if ( sSharedCacheLoadInfo.loadAddress == nullptr ) {
+                               if ( gLinkContext.verboseWarnings )
+                                       dyld::log("dyld: closure %p dyld cache not loaded\n", mainClosure);
+                               return false;
+                       }
+                       else {
+                               uuid_t actualCacheUUID;
+                               sSharedCacheLoadInfo.loadAddress->getUUID(actualCacheUUID);
+                               if ( memcmp(expectedCacheUUID, actualCacheUUID, sizeof(uuid_t)) != 0 ) {
+                                       if ( gLinkContext.verboseWarnings )
+                                               dyld::log("dyld: closure %p not used because built against different dyld cache\n", mainClosure);
+                                       return false;
+                               }
+                       }
                }
-       }
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-       else {
-               // If the in-memory cache doesn't have the same UUID xattr as the on-disk cache then we must
-               // have built a new cache but not rebooted.  In this case, don't use dyld3.
-               const char* sharedCachePath = getStandardSharedCacheFilePath();
-               uuid_t inMemoryUUID;
-               uuid_t onDiskUUID;
-               sharedCacheUUID(inMemoryUUID);
-               if (getxattr(sharedCachePath, "cacheUUID", (void*)&onDiskUUID, sizeof(uuid_t), 0, 0) != sizeof(uuid_t)) {
-                       if ( gLinkContext.verboseWarnings )
-                               dyld::log("dyld: closure %p on disk cache doesn't have a UUID xattr\n", mainClosureData);
-                       return false;
+               else {
+                       // closure built assume there is no dyld cache
+                       if ( sSharedCacheLoadInfo.loadAddress != nullptr ) {
+                               if ( gLinkContext.verboseWarnings )
+                                       dyld::log("dyld: closure %p built expecting no dyld cache\n", mainClosure);
+                               return false;
+                       }
                }
-               if (memcmp(&inMemoryUUID, &onDiskUUID, sizeof(uuid_t)) != 0) {
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+               // verify this closure is not from a previous reboot
+               const char* expectedBootUUID = mainClosure->bootUUID();
+               char actualBootSessionUUID[256] = { 0 };
+               size_t bootSize = sizeof(actualBootSessionUUID);
+               bool gotActualBootUUID = (sysctlbyname("kern.bootsessionuuid", actualBootSessionUUID, &bootSize, NULL, 0) == 0);
+               if ( !gotActualBootUUID || (expectedBootUUID == nullptr) || (strcmp(expectedBootUUID, actualBootSessionUUID) != 0) ) {
                        if ( gLinkContext.verboseWarnings )
-                               dyld::log("dyld: closure %p not used because current cache on disk and in memory cache have UUID mismatches\n", mainClosureData);
+                               dyld::log("dyld: closure %p built in different boot context\n", mainClosure);
                        return false;
                }
-       }
 #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 all mach-o files have not changed since closure was built
+       __block bool foundFileThatInvalidatesClosure = false;
+       mainClosure->images()->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
+               __block uint64_t expectedInode;
+               __block uint64_t expectedMtime;
+               if ( image->hasFileModTimeAndInode(expectedInode, expectedMtime) ) {
+                       struct stat statBuf;
+                       if ( ::stat(image->path(), &statBuf) == 0 ) {
+                               if ( (statBuf.st_mtime != expectedMtime) || (statBuf.st_ino != expectedInode) ) {
+                                       if ( gLinkContext.verboseWarnings )
+                                               dyld::log("dyld: closure %p not used because mtime/inode for '%s' has changed since closure was built\n", mainClosure, image->path());
+                                       foundFileThatInvalidatesClosure = true;
+                                       stop = true;
+                               }
+                       }
+                       else {
+                               if ( gLinkContext.verboseWarnings )
+                                       dyld::log("dyld: closure %p not used because '%s' is needed by closure but is missing\n", mainClosure, image->path());
+                               foundFileThatInvalidatesClosure = true;
+                               stop = true;
+                       }
+               }
+       });
+       if ( foundFileThatInvalidatesClosure )
+               return false;
 
        // verify cdHash of main executable is same as recorded in closure
-       if ( mainImage.validateUsingCdHash() ) {
+       uint8_t expectedHash[20];
+       const dyld3::closure::Image* mainImage = mainClosure->images()->imageForNum(mainClosure->topImage());
+       if ( mainImage->hasCdHash(expectedHash) ) {
                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);
+                               dyld::log("dyld: closure %p not used because main executable is not code signed but was expected to be\n", mainClosure);
             return false;
                }
-               if ( memcmp(mainExecutableCDHash, mainClosure.cdHash(), 20) != 0 ) {
+               if ( memcmp(mainExecutableCDHash, expectedHash, 20) != 0 ) {
                        if ( gLinkContext.verboseWarnings )
-                               dyld::log("dyld: closure %p not used because main executable cd-hash changed since closure was built\n", mainClosureData);
+                               dyld::log("dyld: closure %p not used because main executable cd-hash changed since closure was built\n", mainClosure);
             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 expectedUUID;
+       bool hasExpect = mainImage->getUuid(expectedUUID);
        uuid_t actualUUID;
-       parser.getUuid(actualUUID);
-       if ( memcmp(actualUUID, closureMainUUID, sizeof(uuid_t)) != 0 ) {
+       const dyld3::MachOLoaded* mainExecutableMH = (const dyld3::MachOLoaded*)mainFileInfo.fileContent;
+       bool hasActual = mainExecutableMH->getUuid(actualUUID);
+       if ( hasExpect != hasActual ) {
                if ( gLinkContext.verboseWarnings )
-                       dyld::log("dyld: closure %p not used because UUID of executable changed since closure was built\n", mainClosureData);
+                       dyld::log("dyld: closure %p not used because UUID of executable changed since closure was built\n", mainClosure);
+               return false;
+       }
+       if ( hasExpect && hasActual && memcmp(actualUUID, expectedUUID, sizeof(uuid_t)) != 0 ) {
+               if ( gLinkContext.verboseWarnings )
+                       dyld::log("dyld: closure %p not used because UUID of executable changed since closure was built\n", mainClosure);
                return false;
        }
 
@@ -5221,37 +5461,27 @@ static bool closureValid(const dyld3::launch_cache::BinaryClosureData* mainClosu
        }
 
        // verify files that are supposed to be missing actually are missing
-       __block bool foundFileThatInvalidatesClosure = false;
-       mainClosure.forEachMustBeMissingFile(^(const char* path, bool& stop) {
+       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);
+                               dyld::log("dyld: closure %p not used because found unexpected file '%s'\n", mainClosure, 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;
-                                       }
-                               }
-                       }
-               });
+       // verify closure did not require anything unavailable
+       if ( mainClosure->usedAtPaths() && !gLinkContext.allowAtPaths ) {
+               if ( gLinkContext.verboseWarnings )
+                       dyld::log("dyld: closure %p not used because is used @paths, but process does not allow that\n", mainClosure);
+               return false;
+       }
+       if ( mainClosure->usedFallbackPaths() && !gLinkContext.allowClassicFallbackPaths ) {
+               if ( gLinkContext.verboseWarnings )
+                       dyld::log("dyld: closure %p not used because is used default fallback paths, but process does not allow that\n", mainClosure);
+               return false;
        }
-#endif
 
        return !foundFileThatInvalidatesClosure;
 }
@@ -5270,67 +5500,50 @@ static bool dolog(const char* format, ...)
        return true;
 }
 
-static bool launchWithClosure(const dyld3::launch_cache::BinaryClosureData* mainClosureData,
+static bool launchWithClosure(const dyld3::closure::LaunchClosure* mainClosure,
                                                          const DyldSharedCache* dyldCache,
-                                                         const mach_header* mainExecutableMH, uintptr_t mainExecutableSlide,
+                                                         const dyld3::MachOLoaded* 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;
+       // build list of all known ImageArrays (at most three: cached dylibs, other OS dylibs, and main prog)
+       STACK_ALLOC_ARRAY(const dyld3::closure::ImageArray*, imagesArrays, 3);
+       const dyld3::closure::ImageArray* mainClosureImages = mainClosure->images();
+       if ( dyldCache != nullptr ) {
+               imagesArrays.push_back(dyldCache->cachedDylibsImageArray());
+               if ( auto others = dyldCache->otherOSImageArray() )
+                       imagesArrays.push_back(others);
+       }
+       imagesArrays.push_back(mainClosureImages);
+
+       // allocate space for Array<LoadedImage>
+       STACK_ALLOC_ARRAY(dyld3::LoadedImage, allImages, mainClosure->initialLoadCount());
+
+       __block dyld3::Loader loader(allImages, dyldCache, imagesArrays,  (gLinkContext.verboseLoading ? &dolog : &nolog),
+                                                                                                                                         (gLinkContext.verboseMapping ? &dolog : &nolog),
+                                                                                                                                         (gLinkContext.verboseBind    ? &dolog : &nolog),
+                                                                                                                                         (gLinkContext.verboseDOF     ? &dolog : &nolog));
+       dyld3::closure::ImageNum mainImageNum = mainClosure->topImage();
+       mainClosureImages->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
+               if ( image->imageNum() == mainImageNum ) {
+                       // add main executable (which is already mapped by kernel) to list
+                       dyld3::LoadedImage mainLoadedImage = dyld3::LoadedImage::make(image, mainExecutableMH);
+                       mainLoadedImage.setState(dyld3::LoadedImage::State::mapped);
+                       mainLoadedImage.markLeaveMapped();
+                       loader.addImage(mainLoadedImage);
+                       stop = true;
                }
-       }
-       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
+               else {
+                       // add inserted library to initial list
+                       loader.addImage(dyld3::LoadedImage::make(image));
+               }
+       });
+
+       // recursively load all dependents and fill in allImages array
        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));
+       loader.completeAllDependents(diag);
+       if ( diag.noError() )
+               loader.mapAndFixupAllImages(diag, dyld3::Loader::dtraceUserProbesEnabled());
        if ( diag.hasError() ) {
                if ( gLinkContext.verboseWarnings )
                        dyld::log("dyld: %s\n", diag.errorMessage());
@@ -5338,27 +5551,24 @@ static bool launchWithClosure(const dyld3::launch_cache::BinaryClosureData* main
        }
 
        //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());
+       //for (const dyld3::LoadedImage& info : allImages) {
+       //      dyld::log("mh=%p, path=%s\n", info.loadedAddress(), info.image()->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;
-       }
+       // find libdyld entry
+       dyld3::closure::Image::ResolvedSymbolTarget dyldEntry;
+       mainClosure->libDyldEntry(dyldEntry);
+       const dyld3::LibDyldEntryVector* libDyldEntry = (dyld3::LibDyldEntryVector*)loader.resolveTarget(dyldEntry);
 
        // 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);
+       if ( libDyldEntry->vectorVersion > 4 )
+               libDyldEntry->setRestrictions(gLinkContext.allowAtPaths, gLinkContext.allowEnvVarsPath);
        libDyldEntry->setHaltFunction(&halt);
+       if ( libDyldEntry->vectorVersion > 5 ) {
+               libDyldEntry->setNotifyMonitoringDyldMain(&notifyMonitoringDyldMain);
+               libDyldEntry->setNotifyMonitoringDyld(&notifyMonitoringDyld);
+       }
        if ( libDyldEntry->vectorVersion > 2 )
                libDyldEntry->setChildForkFunction(&_dyld_fork_child);
 #if !TARGET_IPHONE_SIMULATOR
@@ -5366,30 +5576,41 @@ static bool launchWithClosure(const dyld3::launch_cache::BinaryClosureData* main
                libDyldEntry->setLogFunction(&dyld::vlog);
 #endif
        libDyldEntry->setOldAllImageInfo(gProcessInfo);
-       libDyldEntry->setInitialImageList(mainClosureData, dyldCache, sSharedCacheLoadInfo.path, allImages, libSystemMH, libSystemImage);
+       const dyld3::LoadedImage* libSys = loader.findImage(mainClosure->libSystemImageNum());
+       libDyldEntry->setInitialImageList(mainClosure, dyldCache, sSharedCacheLoadInfo.path, allImages, *libSys);
        // 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();
+       if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE)) {
+               dyld3::kdebug_trace_dyld_duration_end(launchTraceID, DBG_DYLD_TIMING_LAUNCH_EXECUTABLE, 0, 0, 3);
        }
-       else {
+       dyld3::closure::Image::ResolvedSymbolTarget progEntry;
+       if ( mainClosure->mainEntry(progEntry) ) {
                // 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();
+               *entry     = loader.resolveTarget(progEntry);
        }
-       CRSetCrashLogMessage(NULL);
+       else if ( mainClosure->startEntry(progEntry) ) {
+               // old style app linked with crt1.o
+               // entry is "start" function in program
+               *startGlue = 0;
+               *entry     = loader.resolveTarget(progEntry);
+       }
+       else {
+               assert(0);
+       }
+
+       CRSetCrashLogMessage("dyld3 mode");
        return true;
 }
 
+
+#if !TARGET_IPHONE_SIMULATOR
+
 static void putHexNibble(uint8_t value, char*& p)
 {
        if ( value < 10 )
@@ -5405,191 +5626,206 @@ static void putHexByte(uint8_t value, char*& 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);
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+static void makeHashOfProgramAndEnv(const char* mainExecutablePath, const uint8_t* mainExecutableCDHash, const char* envp[], uint8_t hash32[32])
+{
+       // create hash of main path, main cd hash, cache UUID, DYLD_* env vars
+       const struct ccdigest_info* di = ccsha256_di();
+       ccdigest_di_decl(di, hashTemp); // defines hashTemp array in stack
+       ccdigest_init(di, hashTemp);
+       // hash in main executable path
+       ccdigest_update(di, hashTemp, strlen(mainExecutablePath), mainExecutablePath);
+       // hash in cdHash of main executable
+       if ( mainExecutableCDHash != nullptr )
+               ccdigest_update(di, hashTemp, 20, mainExecutableCDHash);
+       // hash in shared cache UUID
+       if ( sSharedCacheLoadInfo.loadAddress != nullptr ) {
+               uuid_t cacheUUID;
+               sSharedCacheLoadInfo.loadAddress->getUUID(cacheUUID);
+               ccdigest_update(di, hashTemp, sizeof(uuid_t), cacheUUID);
+       }
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+       // hash in if process is restricted
+       ccdigest_update(di, hashTemp, sizeof(gLinkContext.allowEnvVarsPath), &gLinkContext.allowEnvVarsPath);
 #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';
+       // include dyld's UUID so changing dyld invalidates closures
+       uuid_t dyldUUID;
+       if ( ((const dyld3::MachOLoaded*)&__dso_handle)->getUuid(dyldUUID) )
+               ccdigest_update(di, hashTemp, sizeof(uuid_t), dyldUUID);
+
+       // hash in DYLD_* env vars
+       for (const char* envVar : sEnvVarsToCheck) {
+               if ( const char* keyValue = _simple_getenv(envp, envVar) )
+                       ccdigest_update(di, hashTemp, strlen(keyValue), keyValue);
+       }
+       // finish SHA256 into 32-byte value
+       ccdigest_final(di, hashTemp, hash32);
+       ccdigest_di_clear(di, hashTemp);
 }
+#endif
 
-#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;
+static void buildClosureCachePath(const char* mainExecutablePath,const dyld3::MachOLoaded* mainExecutableMH,
+                                                                 const uint8_t* mainExecutableCDHash, const char* envp[], char closurePath[])
+{
+       // build base path of $TMPDIR/dyld/<prog-name>-
+       const char* tempDir = _simple_getenv(envp, "TMPDIR");
+       if ( tempDir == nullptr )
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+               tempDir = "/private/tmp/";
+#else
+               tempDir = "/private/var/tmp/";
+#endif
+       strlcpy(closurePath, tempDir, PATH_MAX);
+       strlcat(closurePath, "/com.apple.dyld/", PATH_MAX);
+
+       // make sure dyld sub-dir exists
+       struct stat statbuf;
+       if ( ::stat(closurePath, &statbuf) != 0 ) {
+               ::mkdir(closurePath, S_IRWXU);
+       }
 
+       const char* leafName = strrchr(mainExecutablePath, '/');
+       if ( leafName == nullptr )
+               leafName = mainExecutablePath;
+       else
+               ++leafName;
+       strlcat(closurePath, leafName, PATH_MAX);
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+       // on macOS we allow multiple closures by hashing the env vars into the cache filename
+       strlcat(closurePath, "-", PATH_MAX);
+       uint8_t hash32[32];
+       makeHashOfProgramAndEnv(mainExecutablePath, mainExecutableCDHash, envp, hash32);
+       char hashString[72];
+       char* s = hashString;
+       for (int i=0; i < 32; ++i)
+               putHexByte(hash32[i], s);
+       *s = '\0';
+       strlcat(closurePath, hashString, PATH_MAX);
 #else
-       // get port to closured
-    mach_port_t serverPort = dyld3::loader::lookupClosuredPort();
-       if ( serverPort == MACH_PORT_NULL )
-               return NULL;
+       // on iOS, the file name is the leaf name and UUID
+       uuid_t mainExeUUID;
+       if ( mainExecutableMH->getUuid(mainExeUUID) ) {
+               char mainUuidStr[40];
+               bytesToHex(mainExeUUID, sizeof(uuid_t), mainUuidStr);
+               strlcat(closurePath, "-", PATH_MAX);
+               strlcat(closurePath, mainUuidStr, PATH_MAX);
+       }
+#endif
+       strlcat(closurePath, ".closure", PATH_MAX);
+}
 
-       // 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;
-               }
+static const dyld3::closure::LaunchClosure* mapClosureFile(const char* closurePath)
+{
+       struct stat statbuf;
+       if ( ::stat(closurePath, &statbuf) == -1 )
+               return nullptr;
+
+       int fd = ::open(closurePath, O_RDONLY);
+       if ( fd < 0 )
+               return nullptr;
+
+       const dyld3::closure::LaunchClosure* closure = (dyld3::closure::LaunchClosure*)::mmap(NULL, (size_t)statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       ::close(fd);
+
+       if ( closure == MAP_FAILED )
+               return nullptr;
+
+       return closure;
+}
+
+static const dyld3::closure::LaunchClosure* buildLaunchClosure(const uint8_t* mainExecutableCDHash,
+                                                                                                                          const dyld3::closure::LoadedFileInfo& mainFileInfo, const char* envp[])
+{
+       const dyld3::MachOLoaded* mainExecutableMH = (const dyld3::MachOLoaded*)mainFileInfo.fileContent;
+       dyld3::closure::PathOverrides pathOverrides;
+       pathOverrides.setFallbackPathHandling(gLinkContext.allowClassicFallbackPaths ? dyld3::closure::PathOverrides::FallbackPathMode::classic : dyld3::closure::PathOverrides::FallbackPathMode::restricted);
+       pathOverrides.setEnvVars(envp, mainExecutableMH, mainFileInfo.path);
+       STACK_ALLOC_ARRAY(const dyld3::closure::ImageArray*,  imagesArrays, 3);
+       if ( sSharedCacheLoadInfo.loadAddress != nullptr ) {
+               imagesArrays.push_back(sSharedCacheLoadInfo.loadAddress->cachedDylibsImageArray());
+               if ( auto others = sSharedCacheLoadInfo.loadAddress->otherOSImageArray() )
+                       imagesArrays.push_back(others);
        }
-       *s++ = '\0';
 
-       // get uuid of main executable
-       dyld3::MachOParser mainParser((mach_header*)sMainExecutableMachHeader);
-    uuid_t mainUuid;
-       mainParser.getUuid(mainUuid);
+       dyld3::closure::ClosureBuilder::LaunchErrorInfo* errorInfo = (dyld3::closure::ClosureBuilder::LaunchErrorInfo*)&gProcessInfo->errorKind;
+       dyld3::closure::FileSystemPhysical fileSystem;
+       dyld3::closure::ClosureBuilder::AtPath atPathHanding = (gLinkContext.allowAtPaths ? dyld3::closure::ClosureBuilder::AtPath::all : dyld3::closure::ClosureBuilder::AtPath::none);
+       dyld3::closure::ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, fileSystem, sSharedCacheLoadInfo.loadAddress, true, pathOverrides, atPathHanding, errorInfo, mainExecutableMH->archName());
+       const dyld3::closure::LaunchClosure* result = builder.makeLaunchClosure(mainFileInfo, gLinkContext.allowInsertFailures);
+       if ( builder.diagnostics().hasError() )
+               halt(builder.diagnostics().errorMessage());
 
-       // 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;
+       if ( result == nullptr )
+               return nullptr;
 
-       // release server port
-    mach_port_deallocate(mach_task_self(), serverPort);
+       if ( !closureValid(result, mainFileInfo, mainExecutableCDHash, false, envp) ) {
+               // some how the freshly generated closure is invalid...
+               if ( gLinkContext.verboseWarnings )
+                       dyld::log("dyld: somehow just built closure is invalid\n");
+               return nullptr;
+       }
+       // try to save closure to disk for next launch (atomically)
+       char closurePath[PATH_MAX];
+       buildClosureCachePath(mainFileInfo.path, mainExecutableMH, mainExecutableCDHash, envp, closurePath);
+       char closurePathTemp[PATH_MAX];
+       strlcpy(closurePathTemp, closurePath, PATH_MAX);
+       int mypid = getpid();
+       char pidBuf[16];
+       char* s = pidBuf;
+       *s++ = '.';
+       putHexByte(mypid >> 24, s);
+       putHexByte(mypid >> 16, s);
+       putHexByte(mypid >> 8, s);
+       putHexByte(mypid, s);
+       *s = '\0';
+       strlcat(closurePathTemp, pidBuf, PATH_MAX);
+       int fd = ::open(closurePathTemp, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
+       if ( fd != -1 ) {
+               ::ftruncate(fd, result->size());
+               ::write(fd, result, result->size());
+               ::fchmod(fd, S_IRUSR);
+               ::close(fd);
+               ::rename(closurePathTemp, closurePath);
+               // free built closure and mmap file() to reduce dirty memory
+               result->deallocate();
+               result = mapClosureFile(closurePath);
+       }
+       else if ( gLinkContext.verboseWarnings ) {
+               dyld::log("could not save closure (errno=%d) to: %s\n", errno, closurePathTemp);
+       }
+       
+       if ( gLinkContext.verboseWarnings )
+               dyld::log("dyld: just built closure %p (size=%lu) for %s\n", result, result->size(), sExecPath);
 
-       if ( success )
-               return (const dyld3::launch_cache::BinaryClosureData*)reply;
+       return result;
+}
 
-       dyld::log("closure failed to build: %s\n", (char*)reply);
-       return NULL;
-#endif
+static const dyld3::closure::LaunchClosure* findCachedLaunchClosure(const uint8_t* mainExecutableCDHash,
+                                                                                                                                   const dyld3::closure::LoadedFileInfo& mainFileInfo,
+                                                                                                                                       const char* envp[])
+{
+       char closurePath[PATH_MAX];
+       buildClosureCachePath(mainFileInfo.path, (const dyld3::MachOLoaded*)mainFileInfo.fileContent, mainExecutableCDHash, envp, closurePath);
+       const dyld3::closure::LaunchClosure* closure = mapClosureFile(closurePath);
+       if ( closure == nullptr )
+               return nullptr;
+
+       if ( !closureValid(closure, mainFileInfo, mainExecutableCDHash, false, envp) ) {
+               ::munmap((void*)closure, closure->size());
+               return nullptr;
+       }
+
+       if ( gLinkContext.verboseWarnings )
+               dyld::log("dyld: used cached closure %p (size=%lu) for %s\n", closure, closure->size(), sExecPath);
+
+       return closure;
 }
+
 #endif // !TARGET_IPHONE_SIMULATOR
 
 
+
 #if !__MAC_OS_X_VERSION_MIN_REQUIRED
 static const char* sWhiteListDirs[] = {
        "/bin/",
@@ -5601,7 +5837,7 @@ static const char* sWhiteListDirs[] = {
 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") )
+    if ( dyld3::bootArgsContains("force_dyld2=1") )
                return false;
 
 #if __MAC_OS_X_VERSION_MIN_REQUIRED
@@ -5616,18 +5852,30 @@ static bool inWhiteList(const char* execPath)
 #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 dyld3::bootArgsContains("force_dyld3=1");
+#endif
+}
+
+#if !TARGET_IPHONE_SIMULATOR
+static bool isStagedApp(const dyld3::MachOFile* mainExecutableMH, const char* mainExecutablePath)
+{
+#if !__MAC_OS_X_VERSION_MIN_REQUIRED
+       if ( (strncmp(mainExecutablePath, "/var/containers/Bundle/Application/", 35) == 0)
+         || (strncmp(mainExecutablePath, "/private/var/containers/Bundle/Application/", 43) == 0) ) {
+               // staged apps are built without LC_ENCRYPTION_INFO
+               if ( !mainExecutableMH->canBeFairPlayEncrypted() )
                        return true;
-               }
        }
-       return dyld3::loader::bootArgsContains("force_dyld3=1");
 #endif
+       return false;
 }
+#endif
 
 //
 // Entry point for dyld.  The kernel loads dyld and jumps to __dyld_start which
@@ -5640,9 +5888,11 @@ _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);
+       if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE)) {
+               launchTraceID = dyld3::kdebug_trace_dyld_duration_start(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE, (uint64_t)mainExecutableMH, 0, 0);
+       }
 
-       // Grab the cdHash of the main executable from the environment
+    // 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) )
@@ -5661,8 +5911,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
 #if __MAC_OS_X_VERSION_MIN_REQUIRED
        // if this is host dyld, check to see if iOS simulator is being run
        const char* rootPath = _simple_getenv(envp, "DYLD_ROOT_PATH");
-       if ( rootPath != NULL ) {
-
+       if ( (rootPath != NULL) ) {
                // look to see if simulator has its own dyld
                char simDyldPath[PATH_MAX]; 
                strlcpy(simDyldPath, rootPath, PATH_MAX);
@@ -5710,7 +5959,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
     configureProcessRestrictions(mainExecutableMH);
 
 #if __MAC_OS_X_VERSION_MIN_REQUIRED
-    if ( gLinkContext.processIsRestricted ) {
+    if ( !gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsSharedCache ) {
                pruneEnvironmentVariables(envp, &apple);
                // set again because envp and apple may have changed or moved
                setContext(mainExecutableMH, argc, argv, envp, apple);
@@ -5721,6 +5970,17 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                checkEnvironmentVariables(envp);
                defaultUninitializedFallbackPaths(envp);
        }
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+       if (  ((dyld3::MachOFile*)mainExecutableMH)->supportsPlatform(dyld3::Platform::iOSMac)
+         && !((dyld3::MachOFile*)mainExecutableMH)->supportsPlatform(dyld3::Platform::macOS)) {
+               gLinkContext.rootPaths = parseColonList("/System/iOSSupport", NULL);
+               gLinkContext.marzipan = true;
+               if ( sEnv.DYLD_FALLBACK_LIBRARY_PATH == sLibraryFallbackPaths )
+                       sEnv.DYLD_FALLBACK_LIBRARY_PATH = sRestrictedLibraryFallbackPaths;
+               if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == sFrameworkFallbackPaths )
+                       sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = sRestrictedFrameworkFallbackPaths;
+       }
+#endif
        if ( sEnv.DYLD_PRINT_OPTS )
                printOptions(argv);
        if ( sEnv.DYLD_PRINT_ENV ) 
@@ -5728,7 +5988,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
        getHostInfo(mainExecutableMH, mainExecutableSlide);
 
        // load shared cache
-       checkSharedRegionDisable((mach_header*)mainExecutableMH);
+       checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide);
 #if TARGET_IPHONE_SIMULATOR
        // <HACK> until <rdar://30773711> is fixed
        gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion;
@@ -5737,83 +5997,75 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
        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);
-                                                       }
-                                               }
-                                       }
+       bool cacheCompatible = (sSharedCacheLoadInfo.loadAddress == nullptr) || (sSharedCacheLoadInfo.loadAddress->header.formatVersion == dyld3::closure::kFormatVersion);
+       if ( cacheCompatible && (sEnableClosures || inWhiteList(sExecPath)) ) {
+               const dyld3::closure::LaunchClosure* mainClosure = nullptr;
+               dyld3::closure::LoadedFileInfo mainFileInfo;
+               mainFileInfo.fileContent = mainExecutableMH;
+               mainFileInfo.path = sExecPath;
+               // FIXME: If we are saving this closure, this slice offset/length is probably wrong in the case of FAT files.
+               mainFileInfo.sliceOffset = 0;
+               mainFileInfo.sliceLen = std::numeric_limits<__typeof(mainFileInfo.sliceLen)>::max();
+               struct stat mainExeStatBuf;
+               if ( ::stat(sExecPath, &mainExeStatBuf) == 0 ) {
+                       mainFileInfo.inode = mainExeStatBuf.st_ino;
+                       mainFileInfo.mtime = mainExeStatBuf.st_mtime;
+               }
+               // check for closure in cache first
+               if ( sSharedCacheLoadInfo.loadAddress != nullptr ) {
+                       mainClosure = sSharedCacheLoadInfo.loadAddress->findClosure(sExecPath);
+                       if ( gLinkContext.verboseWarnings && (mainClosure != nullptr) )
+                               dyld::log("dyld: found closure %p (size=%lu) in dyld shared cache\n", mainClosure, mainClosure->size());
+               }
+       #if !TARGET_IPHONE_SIMULATOR
+               if ( (mainClosure == nullptr) || !closureValid(mainClosure, mainFileInfo, mainExecutableCDHash, true, envp) ) {
+                       mainClosure = nullptr;
+                       if ( sEnableClosures || isStagedApp((dyld3::MachOFile*)mainExecutableMH, sExecPath) ) {
+                               // if forcing closures, and no closure in cache, or it is invalid, check for cached closure
+                               mainClosure = findCachedLaunchClosure(mainExecutableCDHash, mainFileInfo, envp);
+                               if ( mainClosure == nullptr ) {
+                                       // if  no cached closure found, build new one
+                                       mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp);
                                }
                        }
+               }
        #endif
-                       if ( gLinkContext.verboseWarnings && (mainClosureData != nullptr) )
-                               dyld::log("dyld: found closure %p in dyld shared cache\n", mainClosureData);
+               // try using launch closure
+               if ( mainClosure != nullptr ) {
+                       CRSetCrashLogMessage("dyld3: launch started");
+                       bool launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH,
+                                                                                         mainExecutableSlide, argc, argv, envp, apple, &result, startGlue);
        #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;
-                                       }
+                       if ( !launched ) {
+                               // closure is out of date, build new one
+                               mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp);
+                               if ( mainClosure != nullptr ) {
+                                       launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH,
+                                                                                                mainExecutableSlide, argc, argv, envp, apple, &result, startGlue);
                                }
                        }
        #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);
-                               }
+                       if ( launched ) {
+#if __has_feature(ptrauth_calls)
+                               // start() calls the result pointer as a function pointer so we need to sign it.
+                               result = (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)result, 0, 0);
+#endif
+                               if (sSkipMain)
+                                       result = (uintptr_t)&fake_main;
+                               return result;
+                       }
+                       else {
+                               if ( gLinkContext.verboseWarnings )
+                                       dyld::log("dyld: unable to use closure %p\n", mainClosure);
                        }
                }
-               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
        }
+       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
@@ -5823,6 +6075,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
        sImageRoots.reserve(16);
        sAddImageCallbacks.reserve(4);
        sRemoveImageCallbacks.reserve(4);
+       sAddLoadImageCallbacks.reserve(4);
        sImageFilesNeedingTermination.reserve(16);
        sImageFilesNeedingDOFUnregistration.reserve(8);
 
@@ -5839,6 +6092,11 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                addDyldImageToUUIDList();
 
 #if SUPPORT_ACCELERATE_TABLES
+#if __arm64e__
+               // Disable accelerator tables when we have threaded rebase/bind, which is arm64e executables only for now.
+               if (sMainExecutableMachHeader->cpusubtype == CPU_SUBTYPE_ARM64_E)
+                       sDisableAcceleratorTables = true;
+#endif
                bool mainExcutableAlreadyRebased = false;
                if ( (sSharedCacheLoadInfo.loadAddress != nullptr) && !dylibsCanOverrideCache() && !sDisableAcceleratorTables && (sSharedCacheLoadInfo.loadAddress->header.accelerateInfoAddr != 0) ) {
                        struct stat statBuf;
@@ -5888,7 +6146,7 @@ reloadAllImages:
        #if __MAC_OS_X_VERSION_MIN_REQUIRED
                // <rdar://problem/22805519> be less strict about old mach-o binaries
                uint32_t mainSDK = sMainExecutable->sdkVersion();
-               gLinkContext.strictMachORequired = (mainSDK >= DYLD_MACOSX_VERSION_10_12) || gLinkContext.processUsingLibraryValidation;
+               gLinkContext.strictMachORequired = (mainSDK >= DYLD_MACOSX_VERSION_10_12) || gLinkContext.allowInsertFailures;
        #else
                // simulators, iOS, tvOS, and watchOS are always strict
                gLinkContext.strictMachORequired = true;
@@ -5962,7 +6220,7 @@ reloadAllImages:
                        // register interposing info after all inserted libraries are bound so chaining works
                        for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
                                ImageLoader* image = sAllImages[i+1];
-                               image->registerInterposing();
+                               image->registerInterposing(gLinkContext);
                        }
                }
 
@@ -5971,7 +6229,7 @@ reloadAllImages:
                        ImageLoader* image = sAllImages[i];
                        if ( image->inSharedCache() )
                                continue;
-                       image->registerInterposing();
+                       image->registerInterposing(gLinkContext);
                }
        #if SUPPORT_ACCELERATE_TABLES
                if ( (sAllCacheImagesProxy != NULL) && ImageLoader::haveInterposingTuples() ) {
@@ -5994,6 +6252,7 @@ reloadAllImages:
                        sImageFilesNeedingDOFUnregistration.clear();
                        sAddImageCallbacks.clear();
                        sRemoveImageCallbacks.clear();
+                       sAddLoadImageCallbacks.clear();
                        sDisableAcceleratorTables = true;
                        sAllCacheImagesProxy = NULL;
                        sMappedRangesStart = NULL;
@@ -6008,7 +6267,23 @@ reloadAllImages:
                for(int i=0; i < sImageRoots.size(); ++i) {
                        sImageRoots[i]->applyInterposing(gLinkContext);
                }
+               ImageLoader::applyInterposingToDyldCache(gLinkContext);
                gLinkContext.linkingMainExecutable = false;
+
+               // Bind and notify for the main executable now that interposing has been registered
+               uint64_t bindMainExecutableStartTime = mach_absolute_time();
+               sMainExecutable->recursiveBindWithAccounting(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true);
+               uint64_t bindMainExecutableEndTime = mach_absolute_time();
+               ImageLoaderMachO::fgTotalBindTime += bindMainExecutableEndTime - bindMainExecutableStartTime;
+               gLinkContext.notifyBatch(dyld_image_state_bound, false);
+
+               // Bind and notify for the inserted images now interposing has been registered
+               if ( sInsertedDylibCount > 0 ) {
+                       for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
+                               ImageLoader* image = sAllImages[i+1];
+                               image->recursiveBind(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true);
+                       }
+               }
                
                // <rdar://problem/12186933> do weak binding only after all inserted images linked
                sMainExecutable->weakBind(gLinkContext);
@@ -6044,13 +6319,15 @@ 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);
+               if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE)) {
+                       dyld3::kdebug_trace_dyld_duration_end(launchTraceID, DBG_DYLD_TIMING_LAUNCH_EXECUTABLE, 0, 0, 2);
+               }
                notifyMonitoringDyldMain();
 
                // find entry point for main executable
-               result = (uintptr_t)sMainExecutable->getThreadPC();
+               result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN();
                if ( result != 0 ) {
-                       // main executable uses LC_MAIN, needs to return to glue in libdyld.dylib
+                       // main executable uses LC_MAIN, we need to use helper in libdyld to call into main()
                        if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 9) )
                                *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
                        else
@@ -6058,9 +6335,13 @@ reloadAllImages:
                }
                else {
                        // main executable uses LC_UNIXTHREAD, dyld needs to let "start" in program set up for main()
-                       result = (uintptr_t)sMainExecutable->getMain();
+                       result = (uintptr_t)sMainExecutable->getEntryFromLC_UNIXTHREAD();
                        *startGlue = 0;
                }
+#if __has_feature(ptrauth_calls)
+               // start() calls the result pointer as a function pointer so we need to sign it.
+               result = (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)result, 0, 0);
+#endif
        }
        catch(const char* message) {
                syncAllImages();
@@ -6070,10 +6351,12 @@ reloadAllImages:
                dyld::log("dyld: launch failed\n");
        }
 
-       CRSetCrashLogMessage(NULL);
+       CRSetCrashLogMessage("dyld2 mode");
 
        if (sSkipMain) {
-               dyld3::kdebug_trace_dyld_signpost(DBG_DYLD_SIGNPOST_START_MAIN, 0, 0);
+               if (dyld3::kdebug_trace_dyld_enabled(DBG_DYLD_TIMING_LAUNCH_EXECUTABLE)) {
+                       dyld3::kdebug_trace_dyld_duration_end(launchTraceID, DBG_DYLD_TIMING_LAUNCH_EXECUTABLE, 0, 0, 2);
+               }
                result = (uintptr_t)&fake_main;
                *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
        }
index de649abd42135f56604564c4fff19f999785bb75..b4f6696ad96fe8e8b0f7d2a76366a80b93d933cd 100644 (file)
@@ -25,6 +25,7 @@
 #include <stdint.h>
 #include <sys/stat.h>
 #include <dlfcn.h>
+#include <uuid/uuid.h>
 
 #include "ImageLoader.h"
 #include "mach-o/dyld_priv.h"
@@ -48,6 +49,7 @@ namespace dyld {
                bool                    mustBeBundle;
                bool                    mustBeDylib;
                bool                    canBePIE;
+        bool            enforceIOSMac;
                const char*                                             origin;                 // path for expanding @loader_path
                const ImageLoader::RPathChain*  rpath;                  // paths for expanding @rpath
        };
@@ -55,6 +57,7 @@ namespace dyld {
 
 
        typedef void             (*ImageCallback)(const struct mach_header* mh, intptr_t slide);
+    typedef void         (*LoadImageCallback)(const mach_header* mh, const char* path, bool unloadable);
        typedef void             (*UndefinedHandler)(const char* symbolName);
        typedef const char*      (*ImageLocator)(const char* dllName);
 
@@ -73,6 +76,7 @@ namespace dyld {
        extern void                                     registerAddCallback(ImageCallback func);
        extern void                                     registerRemoveCallback(ImageCallback func);
        extern void                                     registerUndefinedHandler(UndefinedHandler);
+    extern void                 registerLoadCallback(LoadImageCallback func);
        extern void                                     initializeMainExecutable();
        extern void                                     preflight(ImageLoader* image, const ImageLoader::RPathChain& loaderRPaths, unsigned cacheIndex);
        extern void                                     link(ImageLoader* image, bool forceLazysBound, bool neverUnload, const ImageLoader::RPathChain& loaderRPaths, unsigned cacheIndex);
@@ -134,4 +138,3 @@ namespace dyld {
        const char*                                     getPathFromIndex(unsigned cacheIndex);
 #endif
 }
-
index c77e6527f34d86a8efa7d8599ee7df227bce968b..891a82ec6e774de189c64dc2e0424bce9a4282dc 100644 (file)
 #include "dyld.h"
 #include "dyldLibSystemInterface.h"
 #include "DyldSharedCache.h"
+#include "MachOFile.h"
 
 #undef _POSIX_C_SOURCE
 #include "dlfcn.h"
 
+#if __has_feature(ptrauth_calls)
+  #include <ptrauth.h>
+#endif
+
+#ifndef CPU_SUBTYPE_ARM64_E
+       #define CPU_SUBTYPE_ARM64_E    2
+#endif
+
+// relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables
+#if __LP64__
+#define RELOC_SIZE 3
+#define LC_SEGMENT_COMMAND             LC_SEGMENT_64
+#define LC_ROUTINES_COMMAND            LC_ROUTINES_64
+struct macho_segment_command   : public segment_command_64  {};
+struct macho_section                   : public section_64  {};
+struct macho_routines_command  : public routines_command_64  {};
+#else
+#define RELOC_SIZE 2
+#define LC_SEGMENT_COMMAND             LC_SEGMENT
+#define LC_ROUTINES_COMMAND            LC_ROUTINES
+struct macho_segment_command   : public segment_command {};
+struct macho_section                   : public section  {};
+struct macho_routines_command  : public routines_command  {};
+#endif
+
 
 // this was in dyld_priv.h but it is no longer exported
 extern "C" {
@@ -76,6 +102,16 @@ extern const char* allImagesIndexedPath(uint32_t index);
 
 extern "C" int _dyld_func_lookup(const char* name, void** address);
 
+extern "C" void* dlopen_internal(const char* path, int mode, void* callerAddress);
+extern "C" bool  dlopen_preflight_internal(const char* path, void* callerAddress);
+extern "C" void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress);
+
+extern "C" void* dlopen_compat(const char* path, int mode);
+extern "C" bool  dlopen_preflight_compat(const char* path);
+extern "C" void* dlsym_compat(void* handle, const char* symbolName);
+
+
+
 // deprecated APIs are still availble on Mac OS X, but not on iPhone OS
 #if __IPHONE_OS_VERSION_MIN_REQUIRED   
        #define DEPRECATED_APIS_SUPPORTED 0
@@ -136,9 +172,12 @@ static struct dyld_func dyld_funcs[] = {
     {"__dyld_dladdr",                                                                  (void*)dladdr },
     {"__dyld_dlclose",                                                                 (void*)dlclose },
     {"__dyld_dlerror",                                                                 (void*)dlerror },
-    {"__dyld_dlopen",                                                                  (void*)dlopen },
-    {"__dyld_dlsym",                                                                   (void*)dlsym },
-    {"__dyld_dlopen_preflight",                                                        (void*)dlopen_preflight },
+    {"__dyld_dlopen_internal",                                                 (void*)dlopen_internal },
+    {"__dyld_dlsym_internal",                                                  (void*)dlsym_internal },
+    {"__dyld_dlopen_preflight_internal",                               (void*)dlopen_preflight_internal },
+    {"__dyld_dlopen",                                                                  (void*)dlopen_compat },
+    {"__dyld_dlsym",                                                                   (void*)dlsym_compat },
+    {"__dyld_dlopen_preflight",                                                        (void*)dlopen_preflight_compat },
        {"__dyld_image_count",                                                          (void*)_dyld_image_count },
     {"__dyld_get_image_header",                                                        (void*)_dyld_get_image_header },
     {"__dyld_get_image_vmaddr_slide",                                  (void*)_dyld_get_image_vmaddr_slide },
@@ -167,7 +206,8 @@ static struct dyld_func dyld_funcs[] = {
     {"__dyld_objc_notify_register",                                            (void*)_dyld_objc_notify_register },
     {"__dyld_get_shared_cache_uuid",                                   (void*)_dyld_get_shared_cache_uuid },
     {"__dyld_get_shared_cache_range",                                  (void*)_dyld_get_shared_cache_range },
-
+    {"__dyld_images_for_addresses",                                            (void*)_dyld_images_for_addresses },
+    {"__dyld_register_for_image_loads",                                        (void*)_dyld_register_for_image_loads },
 
        // deprecated
 #if DEPRECATED_APIS_SUPPORTED
@@ -257,6 +297,7 @@ struct __NSObjectFileImage
        const void*             imageBaseAddress;       // not used with OFI created from files
        size_t                  imageLength;            // not used with OFI created from files
 };
+typedef __NSObjectFileImage*  NSObjectFileImage;
 
 
 VECTOR_NEVER_DESTRUCTED(NSObjectFileImage);
@@ -347,10 +388,27 @@ const char* _dyld_get_image_name(uint32_t image_index)
        return allImagesIndexedPath(image_index);
 }
 
+static const void *stripPointer(const void *ptr) {
+#if __has_feature(ptrauth_calls)
+       return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
+#else
+       return ptr;
+#endif
+}
+
+static void *stripPointer(void *ptr) {
+#if __has_feature(ptrauth_calls)
+       return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
+#else
+       return ptr;
+#endif
+}
+
 const struct mach_header * dyld_image_header_containing_address(const void* address)
 {
        if ( dyld::gLogAPIs )
                dyld::log("%s(%p)\n", __func__, address);
+       address = stripPointer(address);
 #if SUPPORT_ACCELERATE_TABLES
        const mach_header* mh;
        const char* path;
@@ -550,6 +608,7 @@ const struct mach_header* addImage(void* callerAddress, const char* path, bool s
                context.mustBeBundle            = false;
                context.mustBeDylib                     = true;
                context.canBePIE                        = false;
+               context.enforceIOSMac           = false;
                context.origin                          = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path
                context.rpath                           = &callersRPaths;       // rpaths from caller and main executable
 
@@ -743,6 +802,7 @@ bool _dyld_bind_fully_image_containing_address(const void* address)
 {
        if ( dyld::gLogAPIs )
                dyld::log("%s(%p)\n", __func__, address);
+       address = stripPointer(address);
        dyld::clearErrorMessage();
        ImageLoader* image = dyld::findImageContainingAddress(address);
        if ( image != NULL ) {
@@ -793,6 +853,7 @@ NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName
                context.mustBeBundle            = true;
                context.mustBeDylib                     = false;
                context.canBePIE                        = false;
+               context.enforceIOSMac           = false;
                context.origin                          = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path
                context.rpath                           = NULL; // support not yet implemented
 
@@ -1299,7 +1360,7 @@ static void dlerrorSet(const char* msg)
 }
 
 
-bool dlopen_preflight(const char* path)
+bool dlopen_preflight_internal(const char* path, void* callerAddress)
 {
        if ( dyld::gLogAPIs )
                dyld::log("%s(%s)\n", __func__, path);
@@ -1344,7 +1405,6 @@ bool dlopen_preflight(const char* path)
        bool result = false;
        std::vector<const char*> rpathsFromCallerImage;
        try {
-               void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
                ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress);
                // for dlopen, use rpath from caller image and from main executable
                if ( callerImage != NULL )
@@ -1365,6 +1425,7 @@ bool dlopen_preflight(const char* path)
                context.mustBeBundle    = false;
                context.mustBeDylib             = false;
                context.canBePIE                = true;
+               context.enforceIOSMac   = false;
                context.origin                  = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path
                context.rpath                   = &callersRPaths;       // rpaths from caller and main executable
 
@@ -1410,14 +1471,13 @@ bool static callerIsNonOSApp(void* callerAddress, const char** shortName)
 }
 #endif
 
-void* dlopen(const char* path, int mode)
+void* dlopen_internal(const char* path, int mode, void* callerAddress)
 {
        if ( dyld::gLogAPIs )
                dyld::log("%s(%s, 0x%08X)\n", __func__, ((path==NULL) ? "NULL" : path), mode);
 
 #if SUPPORT_ACCELERATE_TABLES
        if ( dyld::gLogAppAPIs ) {
-               void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
                const char* shortName;
                if ( callerIsNonOSApp(callerAddress, &shortName) ) {
                        dyld::log("%s: %s(%s, 0x%08X)\n", shortName, __func__, ((path==NULL) ? "NULL" : path), mode);
@@ -1481,7 +1541,6 @@ void* dlopen(const char* path, int mode)
        std::vector<const char*> rpathsFromCallerImage;
        ImageLoader::RPathChain callersRPaths(NULL, &rpathsFromCallerImage);
        try {
-               void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
                ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress);
                if ( (mode & RTLD_NOLOAD) == 0 ) {
                        // for dlopen, use rpath from caller image and from main executable
@@ -1501,6 +1560,7 @@ void* dlopen(const char* path, int mode)
                context.mustBeBundle    = false;
                context.mustBeDylib             = false;
                context.canBePIE                = true;
+               context.enforceIOSMac   = false;
                context.origin                  = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path
                context.rpath                   = &callersRPaths;                               // rpaths from caller and main executable
 
@@ -1527,7 +1587,12 @@ void* dlopen(const char* path, int mode)
                                bool alreadyLinked = image->isLinked();
                                bool forceLazysBound = ( (mode & RTLD_NOW) != 0 );
                                dyld::link(image, forceLazysBound, false, callersRPaths, cacheIndex);
-                               if ( ! alreadyLinked ) {
+                               if ( alreadyLinked ) {
+                                       // upgrade
+                                       if ( ((mode & RTLD_LOCAL) == 0) && image->hasHiddenExports() )
+                                               image->setHideExports(false);
+                               }
+                               else {
                                        // only hide exports if image is not already in use
                                        if ( (mode & RTLD_LOCAL) != 0 )
                                                image->setHideExports(true);
@@ -1603,8 +1668,6 @@ void* dlopen(const char* path, int mode)
        return result;
 }
 
-
-
 int dlclose(void* handle)
 {
        if ( dyld::gLogAPIs )
@@ -1615,7 +1678,14 @@ int dlclose(void* handle)
                return 0;
        if ( handle == RTLD_DEFAULT )
                return 0;
-       
+
+#if SUPPORT_ACCELERATE_TABLES
+       if ( dyld::isCacheHandle(handle) ) {
+               dlerrorClear();
+               return 0;
+       }
+#endif
+
        ImageLoader* image = (ImageLoader*)(((uintptr_t)handle) & (-4));        // clear mode bits
        if ( dyld::validImage(image) ) {
                dlerrorClear();
@@ -1642,6 +1712,12 @@ int dladdr(const void* address, Dl_info* info)
        if ( dyld::gLogAPIs )
                dyld::log("%s(%p, %p)\n", __func__, address, info);
 
+       // <rdar://problem/42171466> calling dladdr(xx,NULL) crashes
+       if ( info == NULL )
+               return 0; // failure
+
+       address = stripPointer(address);
+
        CRSetCrashLogMessage("dyld: in dladdr()");
 #if SUPPORT_ACCELERATE_TABLES
        if ( dyld::dladdrFromCache(address, info) ) {
@@ -1709,14 +1785,13 @@ char* dlerror()
        return NULL;
 }
 
-void* dlsym(void* handle, const char* symbolName)
+void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress)
 {
        if ( dyld::gLogAPIs )
                dyld::log("%s(%p, %s)\n", __func__, handle, symbolName);
 
 #if SUPPORT_ACCELERATE_TABLES
        if ( dyld::gLogAppAPIs ) {
-               void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
                const char* shortName;
                if ( callerIsNonOSApp(callerAddress, &shortName) ) {
                        dyld::log("%s: %s(%p, %s)\n", shortName, __func__, handle, symbolName);
@@ -1742,6 +1817,20 @@ void* dlsym(void* handle, const char* symbolName)
                if ( dyld::flatFindExportedSymbol(underscoredName, &sym, &image) ) {
                        CRSetCrashLogMessage(NULL);
                        result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, NULL, false, underscoredName);
+#if __has_feature(ptrauth_calls)
+                       // Sign the pointer if it points to a function
+                       // Note we only do this if the main executable is arm64e as otherwise we
+                       // may end up calling containsAddress on the accelerator tables.
+                       if ( result && (dyld::gLinkContext.mainExecutable->machHeader()->cpusubtype == CPU_SUBTYPE_ARM64_E) ) {
+                               const ImageLoader* symbolImage = image;
+                               if (!symbolImage->containsAddress(result)) {
+                                       symbolImage = dyld::findImageContainingAddress(result);
+                               }
+                               const macho_section *sect = symbolImage ? symbolImage->findSection(result) : NULL;
+                               if ( sect && ((sect->flags & S_ATTR_PURE_INSTRUCTIONS) || (sect->flags & S_ATTR_SOME_INSTRUCTIONS)) )
+                                       result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
+                       }
+#endif
                        if ( dyld::gLogAPIs )
                                dyld::log("  %s(RTLD_DEFAULT, %s) ==> %p\n", __func__, symbolName, result);
                        return result;
@@ -1762,6 +1851,20 @@ void* dlsym(void* handle, const char* symbolName)
                if ( sym != NULL ) {
                        CRSetCrashLogMessage(NULL);
                        result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, NULL, false, underscoredName);
+#if __has_feature(ptrauth_calls)
+                       // Sign the pointer if it points to a function
+                       // Note we only do this if the main executable is arm64e as otherwise we
+                       // may end up calling containsAddress on the accelerator tables.
+                       if ( result && (dyld::gLinkContext.mainExecutable->machHeader()->cpusubtype == CPU_SUBTYPE_ARM64_E) ) {
+                               const ImageLoader* symbolImage = image;
+                               if (!symbolImage->containsAddress(result)) {
+                                       symbolImage = dyld::findImageContainingAddress(result);
+                               }
+                               const macho_section *sect = symbolImage ? symbolImage->findSection(result) : NULL;
+                               if ( sect && ((sect->flags & S_ATTR_PURE_INSTRUCTIONS) || (sect->flags & S_ATTR_SOME_INSTRUCTIONS)) )
+                                       result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
+                       }
+#endif
                        if ( dyld::gLogAPIs )
                                dyld::log("  %s(RTLD_MAIN_ONLY, %s) ==> %p\n", __func__, symbolName, result);
                        return result;
@@ -1777,7 +1880,6 @@ void* dlsym(void* handle, const char* symbolName)
        
        // magic "search what I would see" handle
        else if ( handle == RTLD_NEXT ) {
-               void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
 #if SUPPORT_ACCELERATE_TABLES
                const mach_header* mh;
                const char* path;
@@ -1795,6 +1897,20 @@ void* dlsym(void* handle, const char* symbolName)
                if ( sym != NULL ) {
                        CRSetCrashLogMessage(NULL);
                        result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext , callerImage, false, underscoredName);
+#if __has_feature(ptrauth_calls)
+                       // Sign the pointer if it points to a function
+                       // Note we only do this if the main executable is arm64e as otherwise we
+                       // may end up calling containsAddress on the accelerator tables.
+                       if ( result && (dyld::gLinkContext.mainExecutable->machHeader()->cpusubtype == CPU_SUBTYPE_ARM64_E) ) {
+                               const ImageLoader* symbolImage = image;
+                               if (!symbolImage->containsAddress(result)) {
+                                       symbolImage = dyld::findImageContainingAddress(result);
+                               }
+                               const macho_section *sect = symbolImage ? symbolImage->findSection(result) : NULL;
+                               if ( sect && ((sect->flags & S_ATTR_PURE_INSTRUCTIONS) || (sect->flags & S_ATTR_SOME_INSTRUCTIONS)) )
+                                       result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
+                       }
+#endif
                        if ( dyld::gLogAPIs )
                                dyld::log("  %s(RTLD_NEXT, %s) ==> %p\n", __func__, symbolName, result);
                        return result;
@@ -1809,7 +1925,6 @@ void* dlsym(void* handle, const char* symbolName)
        }
        // magic "search me, then what I would see" handle
        else if ( handle == RTLD_SELF ) {
-               void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
 #if SUPPORT_ACCELERATE_TABLES
                const mach_header* mh;
                const char* path;
@@ -1827,6 +1942,20 @@ void* dlsym(void* handle, const char* symbolName)
                if ( sym != NULL ) {
                        CRSetCrashLogMessage(NULL);
                        result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, callerImage, false, underscoredName);
+#if __has_feature(ptrauth_calls)
+                       // Sign the pointer if it points to a function
+                       // Note we only do this if the main executable is arm64e as otherwise we
+                       // may end up calling containsAddress on the accelerator tables.
+                       if ( result && (dyld::gLinkContext.mainExecutable->machHeader()->cpusubtype == CPU_SUBTYPE_ARM64_E) ) {
+                               const ImageLoader* symbolImage = image;
+                               if (!symbolImage->containsAddress(result)) {
+                                       symbolImage = dyld::findImageContainingAddress(result);
+                               }
+                               const macho_section *sect = symbolImage ? symbolImage->findSection(result) : NULL;
+                               if ( sect && ((sect->flags & S_ATTR_PURE_INSTRUCTIONS) || (sect->flags & S_ATTR_SOME_INSTRUCTIONS)) )
+                                       result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
+                       }
+#endif
                        if ( dyld::gLogAPIs )
                                dyld::log("  %s(RTLD_SELF, %s) ==> %p\n", __func__, symbolName, result);
                        return result;
@@ -1861,10 +1990,23 @@ void* dlsym(void* handle, const char* symbolName)
                        ImageLoader* callerImage = NULL;
                        if ( sDynamicInterposing ) {
                                // only take time to look up caller, if dynamic interposing in use
-                               void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
                                callerImage = dyld::findImageContainingAddress(callerAddress);
                        }
                        result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, callerImage, false, underscoredName);
+#if __has_feature(ptrauth_calls)
+                       // Sign the pointer if it points to a function
+                       // Note we only do this if the main executable is arm64e as otherwise we
+                       // may end up calling containsAddress on the accelerator tables.
+                       if ( result && (dyld::gLinkContext.mainExecutable->machHeader()->cpusubtype == CPU_SUBTYPE_ARM64_E) ) {
+                               const ImageLoader* symbolImage = image;
+                               if (!symbolImage->containsAddress(result)) {
+                                       symbolImage = dyld::findImageContainingAddress(result);
+                               }
+                               const macho_section *sect = symbolImage ? symbolImage->findSection(result) : NULL;
+                               if ( sect && ((sect->flags & S_ATTR_PURE_INSTRUCTIONS) || (sect->flags & S_ATTR_SOME_INSTRUCTIONS)) )
+                                       result = __builtin_ptrauth_sign_unauthenticated(result, ptrauth_key_asia, 0);
+                       }
+#endif
                        if ( dyld::gLogAPIs )
                                dyld::log("  %s(%p, %s) ==> %p\n", __func__, handle, symbolName, result);
                        return result;
@@ -1882,11 +2024,25 @@ void* dlsym(void* handle, const char* symbolName)
        return NULL;
 }
 
+// Note this is only here to support ___pthread_abort in libpthread.a
+void* dlsym(void* handle, const char* symbolName) {
+       return dlsym_internal(handle, symbolName, __builtin_return_address(1));
+}
 
 
-
-
-
+// <rdar://problem/40352925> *_compat functions are for old binaries that have __dyld section and use it to bypass libdyld.dylib
+void* dlopen_compat(const char* path, int mode)
+{
+       return dlopen_internal(path, mode, (void*)dyld::mainExecutable()->machHeader());
+}
+bool  dlopen_preflight_compat(const char* path)
+{
+       return dlopen_preflight_internal(path, (void*)dyld::mainExecutable()->machHeader());
+}
+void* dlsym_compat(void* handle, const char* symbolName)
+{
+       return dlsym_internal(handle, symbolName, (void*)dyld::mainExecutable()->machHeader());
+}
 
 
 
@@ -1902,6 +2058,8 @@ static bool client_dyld_find_unwind_sections(void* addr, dyld_unwind_sections* i
 {
        //if ( dyld::gLogAPIs )
        //      dyld::log("%s(%p, %p)\n", __func__, addr, info);
+
+       addr = stripPointer(addr);
        
 #if SUPPORT_ACCELERATE_TABLES
        if ( dyld::findUnwindSections(addr, info) )
@@ -1922,6 +2080,8 @@ const char* dyld_image_path_containing_address(const void* address)
        if ( dyld::gLogAPIs )
                dyld::log("%s(%p)\n", __func__, address);
 
+    address = (void*)stripPointer(address);
+    
 #if SUPPORT_ACCELERATE_TABLES
        const mach_header* mh;
        const char* path;
@@ -2030,5 +2190,38 @@ const void* _dyld_get_shared_cache_range(size_t* length)
        return nullptr;
 }
 
+void _dyld_images_for_addresses(unsigned count, const void* addresses[], struct dyld_image_uuid_offset infos[])
+{
+       for (unsigned i=0; i < count; ++i) {
+        const void* addr = addresses[i];
+               addr = stripPointer(addr);
+        bzero(&infos[i], sizeof(dyld_image_uuid_offset));
+#if SUPPORT_ACCELERATE_TABLES
+               const mach_header*      mh;
+               const char*             path;
+               if ( dyld::addressInCache(addr, &mh, &path) ) {
+                       infos[i].image         = mh;
+                       infos[i].offsetInImage = (uintptr_t)addr - (uintptr_t)mh;
+                       ((dyld3::MachOFile*)mh)->getUuid(infos[i].uuid);
+                       break;
+               }
+#endif
+               ImageLoader* image = dyld::findImageContainingAddress(addr);
+        if ( image != nullptr ) {
+            infos[i].image         = image->machHeader();
+            infos[i].offsetInImage = (uintptr_t)addr - (uintptr_t)(image->machHeader());
+            image->getUUID(infos[i].uuid);
+        }
+    }
+}
+
+void _dyld_register_for_image_loads(void (*func)(const mach_header* mh, const char* path, bool unloadable))
+{
+       if ( dyld::gLogAPIs )
+               dyld::log("%s(%p)\n", __func__, (void *)func);
+       dyld::registerLoadCallback(func);
+}
+
+
 
 
index 6d7247297fe4c788eca89843010d623c55706cfd..d556e9d7f2e55689d802d9fb7cb3f9ffd071bba1 100644 (file)
@@ -26,7 +26,9 @@
 #include <string.h>
 #include <malloc/malloc.h>
 #include <sys/mman.h>
+#include <execinfo.h>
 
+#include <System/sys/csr.h>
 #include <crt_externs.h>
 #include <Availability.h>
 #include <vproc_priv.h>
 
 #include "ImageLoader.h"
 #include "dyldLock.h"
-#include "start_glue.h"
 
 #include "../dyld3/APIs.h"
 #include "../dyld3/AllImages.h"
+#include "../dyld3/StartGlue.h"
+#include "../dyld3/Tracing.h"
 
 
 // this was in dyld_priv.h but it is no longer exported
@@ -129,7 +132,6 @@ extern bool gUseDyld3;
        #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   
        #define DEPRECATED_APIS_SUPPORTED 0
@@ -532,262 +534,84 @@ int32_t NSVersionOfRunTimeLibrary(const char* libraryName)
        return (-1);
 }
 
-
-#define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff))
-
-
-static bool getVersionLoadCommandInfo(const mach_header* mh, uint32_t* platform, uint32_t* minOS, uint32_t* sdk)
-{
-       const load_command* startCmds = NULL;
-       if ( mh->magic == MH_MAGIC_64 )
-               startCmds = (load_command*)((char *)mh + sizeof(mach_header_64));
-       else if ( mh->magic == MH_MAGIC )
-               startCmds = (load_command*)((char *)mh + sizeof(mach_header));
-       else
-               return false;  // not a mach-o file, or wrong endianness
-               
-       const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds);
-       const load_command* cmd = startCmds;
-       for(uint32_t i = 0; i < mh->ncmds; ++i) {
-           const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize);
-               if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds)) {
-                       return 0;
-               }
-               const version_min_command* versCmd;
-               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;
-                               *platform       = PLATFORM_WATCHOS;
-                               *minOS          = versCmd->version;
-                               *sdk            = versCmd->sdk;
-                               return true;
-                       case LC_BUILD_VERSION:
-                               buildVersCmd = (build_version_command*)cmd;
-                               *platform        = buildVersCmd->platform;
-                               *minOS           = buildVersCmd->minos;
-                               *sdk             = buildVersCmd->sdk;
-                               return true;
-               }
-               cmd = nextCmd;
-       }
-       return false;
-}
-
-#if !__WATCH_OS_VERSION_MIN_REQUIRED && !__TV_OS_VERSION_MIN_REQUIRED
-static uint32_t deriveSDKVersFromDylibs(const mach_header* mh)
-{
-       const load_command* startCmds = NULL;
-       if ( mh->magic == MH_MAGIC_64 )
-               startCmds = (load_command*)((char *)mh + sizeof(mach_header_64));
-       else if ( mh->magic == MH_MAGIC )
-               startCmds = (load_command*)((char *)mh + sizeof(mach_header));
-       else
-               return 0;  // not a mach-o file, or wrong endianness
-               
-       const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds);
-       const dylib_command* dylibCmd;
-       const load_command* cmd = startCmds;
-       const char* dylibName;
-  #if __IPHONE_OS_VERSION_MIN_REQUIRED
-       uint32_t foundationVers = 0;
-  #else
-       uint32_t libSystemVers = 0;
-  #endif
-       for(uint32_t i = 0; i < mh->ncmds; ++i) {
-           const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize);
-               // <rdar://problem/14381579&16050962> sanity check size of command
-               if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds)) {
-                       return 0;
-               }
-               switch ( cmd->cmd ) {
-                       case LC_LOAD_DYLIB:
-                       case LC_LOAD_WEAK_DYLIB:
-                       case LC_LOAD_UPWARD_DYLIB:
-                               dylibCmd = (dylib_command*)cmd;
-                               // sanity check dylib command layout
-                               if ( dylibCmd->dylib.name.offset > cmd->cmdsize )
-                                       return 0;
-                               dylibName = (char*)dylibCmd + dylibCmd->dylib.name.offset;
-  #if __IPHONE_OS_VERSION_MIN_REQUIRED
-                               if ( strcmp(dylibName, "/System/Library/Frameworks/Foundation.framework/Foundation") == 0 )
-                                       foundationVers = dylibCmd->dylib.current_version;
-  #else
-                               if ( strcmp(dylibName, "/usr/lib/libSystem.B.dylib") == 0 )
-                                       libSystemVers = dylibCmd->dylib.current_version;
-  #endif
-                               break;
-               }
-               cmd = nextCmd;
-       }
-
-       struct DylibToOSMapping {
-               uint32_t dylibVersion;
-               uint32_t osVersion;
-       };
-       
-  #if __IPHONE_OS_VERSION_MIN_REQUIRED
-       static const DylibToOSMapping foundationMapping[] = {
-               { PACKED_VERSION(678,24,0), 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
-
-
-#if __WATCH_OS_VERSION_MIN_REQUIRED
-static uint32_t watchVersToIOSVers(uint32_t vers)
-{
-       return vers + 0x00070000;
-}
-
+#if TARGET_OS_WATCH
 uint32_t dyld_get_program_sdk_watch_os_version()
 {
-       if ( gUseDyld3 )
-               return dyld3::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 platform;
-       uint32_t minOS;
-       uint32_t sdk;
+    __block uint32_t retval = 0;
+    __block bool versionFound = false;
+    dyld3::dyld_get_image_versions((mach_header*)_NSGetMachExecuteHeader(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+        if (versionFound) return;
 
-       if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) {
-               if ( platform == PLATFORM_WATCHOS )
-                               return sdk;
-       }
-       return 0;
+        if (dyld_get_base_platform(platform) == PLATFORM_WATCHOS) {
+            versionFound = true;
+            retval = sdk_version;
+        }
+    });
+
+    return retval;
 }
 
 uint32_t dyld_get_program_min_watch_os_version()
 {
-       if ( gUseDyld3 )
-               return dyld3::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;
+    __block uint32_t retval = 0;
+    __block bool versionFound = false;
+    dyld3::dyld_get_image_versions((mach_header*)_NSGetMachExecuteHeader(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+        if (versionFound) return;
 
-       if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) {
-               if ( platform == PLATFORM_WATCHOS )
-                               return minOS;  // return raw minOS (not mapped to iOS version)
-       }
-       return 0;
-}
+        if (dyld_get_base_platform(platform) == PLATFORM_WATCHOS) {
+            versionFound = true;
+            retval = min_version;
+        }
+    });
 
+    return retval;
+}
 #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 (gUseDyld3)
+        return dyld3::dyld_get_program_sdk_bridge_os_version();
 
-       if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) {
-               if ( platform == PLATFORM_BRIDGEOS )
-                               return sdk;
-       }
-       return 0;
+    __block uint32_t retval = 0;
+    __block bool versionFound = false;
+    dyld3::dyld_get_image_versions((mach_header*)_NSGetMachExecuteHeader(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+        if (versionFound) return;
+
+        if (dyld_get_base_platform(platform) == PLATFORM_BRIDGEOS) {
+            versionFound = true;
+            retval = sdk_version;
+        }
+    });
+
+    return retval;
 }
 
 uint32_t dyld_get_program_min_bridge_os_version()
 {
-       const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader();
-       uint32_t platform;
-       uint32_t minOS;
-       uint32_t sdk;
+    if (gUseDyld3)
+        return dyld3::dyld_get_program_min_bridge_os_version();
 
-       if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) {
-               if ( platform == PLATFORM_BRIDGEOS )
-                               return minOS;  // return raw minOS (not mapped to iOS version)
-       }
-       return 0;
-}
+    __block uint32_t retval = 0;
+    __block bool versionFound = false;
+    dyld3::dyld_get_image_versions((mach_header*)_NSGetMachExecuteHeader(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+        if (versionFound) return;
+
+        if (dyld_get_base_platform(platform) == PLATFORM_BRIDGEOS) {
+            versionFound = true;
+            retval = min_version;
+        }
+    });
 
+    return retval;
+}
 #endif
 
 /*
@@ -801,112 +625,23 @@ uint32_t dyld_get_program_min_bridge_os_version()
  */
 uint32_t dyld_get_sdk_version(const mach_header* mh)
 {
-       if ( gUseDyld3 )
-               return dyld3::dyld_get_sdk_version(mh);
-
-    uint32_t platform;
-       uint32_t minOS;
-       uint32_t sdk;
-
-       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 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
-               }
-       }
-
-#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
-       // MacOSX and iOS have old binaries without version load commmand.
-       return deriveSDKVersFromDylibs(mh);
-#endif
+    return dyld3::dyld_get_sdk_version(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());
+    return dyld3::dyld_get_sdk_version((mach_header*)_NSGetMachExecuteHeader());
 }
 
 uint32_t dyld_get_min_os_version(const struct mach_header* mh)
 {
-       if ( gUseDyld3 )
-               return dyld3::dyld_get_min_os_version(mh);
-
-       uint32_t platform;
-       uint32_t minOS;
-       uint32_t sdk;
-
-       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 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
-               }
-       }
-       return 0;
+    return dyld3::dyld_get_min_os_version(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());
+    return dyld3::dyld_get_min_os_version((mach_header*)_NSGetMachExecuteHeader());
 }
 
 
@@ -941,6 +676,55 @@ bool _dyld_get_image_uuid(const struct mach_header* mh, uuid_t uuid)
        return false;
 }
 
+dyld_platform_t dyld_get_active_platform(void) {
+    if (gUseDyld3)
+        return dyld3::dyld_get_active_platform();
+
+    // HACK
+    // Most of the new version SPIs have pure dyld3 implementations, but
+    // They cannot get to the main executable, so we implement this here
+    // and they can use this by calling ::dyld_get_active_platform() in the root namespace
+    static dyld_platform_t sActivePlatform = 0;
+    if (sActivePlatform) return sActivePlatform;
+
+    dyld3::dyld_get_image_versions((mach_header*)_NSGetMachExecuteHeader(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+        sActivePlatform = platform;
+        //FIXME assert there is only one?
+    });
+    return sActivePlatform;
+}
+
+dyld_platform_t dyld_get_base_platform(dyld_platform_t platform) {
+    return dyld3::dyld_get_base_platform(platform);
+}
+
+bool dyld_is_simulator_platform(dyld_platform_t platform) {
+    return dyld3::dyld_is_simulator_platform(platform);
+}
+
+bool dyld_sdk_at_least(const struct mach_header* mh, dyld_build_version_t version) {
+    return dyld3::dyld_sdk_at_least(mh, version);
+}
+
+bool dyld_minos_at_least(const struct mach_header* mh, dyld_build_version_t version) {
+    return dyld3::dyld_minos_at_least(mh, version);
+}
+
+bool dyld_program_sdk_at_least(dyld_build_version_t version) {
+    return dyld3::dyld_sdk_at_least((mach_header*)_NSGetMachExecuteHeader(),version);
+}
+
+bool dyld_program_minos_at_least(dyld_build_version_t version) {
+    return dyld3::dyld_minos_at_least((mach_header*)_NSGetMachExecuteHeader(), version);
+}
+
+// Function that walks through the load commands and calls the internal block for every version found
+// Intended as a fallback for very complex (and rare) version checks, or for tools that need to
+// print our everything for diagnostic reasons
+void dyld_get_image_versions(const struct mach_header* mh, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version)) {
+    dyld3::dyld_get_image_versions(mh, callback);
+}
+
 
 
 #if DEPRECATED_APIS_SUPPORTED
@@ -1654,9 +1438,15 @@ static vswapproc swapProc = &vproc_swap_integer;
 
 static bool isLaunchdOwned()
 {
-       int64_t val = 0;
-       (*swapProc)(NULL, VPROC_GSK_IS_MANAGED, NULL, &val);
-       return ( val != 0 );
+    static bool checked = false;
+    static bool result = false;
+    if ( !checked ) {
+        checked = true;
+           int64_t val = 0;
+           (*swapProc)(NULL, VPROC_GSK_IS_MANAGED, NULL, &val);
+           result = ( val != 0 );
+    }
+    return result;
 }
 
 static void shared_cache_missing()
@@ -1685,7 +1475,7 @@ static dyld::LibSystemHelpers sHelpers = { 13, &dyldGlobalLockAcquire, &dyldGlob
                                                                        &vm_allocate,
                                                                        &mmap,
                                                                        &__cxa_finalize_ranges
-                                                                       };
+                                    };
 
 
 //
@@ -1725,6 +1515,8 @@ char* dlerror()
 
 int dladdr(const void* addr, Dl_info* info)
 {
+    dyld3::ScopedTimer timer(DBG_DYLD_TIMING_DLADDR, (uint64_t)addr, 0, 0);
+    int result = 0;
        if ( gUseDyld3 )
                return dyld3::dladdr(addr, info);
 
@@ -1733,11 +1525,17 @@ int dladdr(const void* addr, Dl_info* info)
 
        if(p == NULL)
            _dyld_func_lookup("__dyld_dladdr", (void**)&p);
-       return(p(addr, info));
+    result = p(addr, info);
+    timer.setData4(result);
+    timer.setData5(info != NULL ? info->dli_fbase : 0);
+    timer.setData6(info != NULL ? info->dli_saddr : 0);
+       return result;
 }
 
 int dlclose(void* handle)
 {
+    dyld3::ScopedTimer timer(DBG_DYLD_TIMING_DLCLOSE, (uint64_t)handle, 0, 0);
+    int result = 0;
        if ( gUseDyld3 )
                return dyld3::dlclose(handle);
 
@@ -1746,54 +1544,101 @@ int dlclose(void* handle)
 
        if(p == NULL)
            _dyld_func_lookup("__dyld_dlclose", (void**)&p);
-       return(p(handle));
+    result = p(handle);
+       return result;
 }
 
 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;
-       
-    static void* (*p)(const char* path, int) = NULL;
+{
+    dyld3::ScopedTimer timer(DBG_DYLD_TIMING_DLOPEN, path, mode, 0);
+    void* result = nullptr;
+
+    if ( gUseDyld3 ) {
+        result = dyld3::dlopen_internal(path, mode, __builtin_return_address(0));
+        return result;
+    }
+
+    // dlopen is special. locking is done inside dyld to allow initializer to run without lock
+    DYLD_NO_LOCK_THIS_BLOCK;
+
+    static void* (*p)(const char* path, int, void*) = NULL;
+
+    if(p == NULL)
+        _dyld_func_lookup("__dyld_dlopen_internal", (void**)&p);
+    result = p(path, mode, __builtin_return_address(0));
+    // use asm block to prevent tail call optimization
+    // this is needed because dlopen uses __builtin_return_address() and depends on this glue being in the frame chain
+    // <rdar://problem/5313172 dlopen() looks too far up stack, can cause crash>
+    __asm__ volatile("");
+    timer.setData4(result);
+
+#if TARGET_OS_OSX
+    // HACK for iOSMac bringup rdar://40945421
+    if ( result == nullptr  && dyld_get_active_platform() == PLATFORM_IOSMAC && csr_check(CSR_ALLOW_APPLE_INTERNAL) == 0) {
+        if (hasPerThreadBufferFor_dlerror()) {
+            // first char of buffer is flag whether string (starting at second char) is valid
+            char* buffer = getPerThreadBufferFor_dlerror(2);
+
+            if ( buffer[0] != '\0' && (strstr(&buffer[1], "macOS dylib cannot be loaded into iOSMac process")
+                                       || strstr(&buffer[1], "mach-o, but not built for iOSMac")) ) {
+                // if valid buffer and contains an iOSMac issue
+                fprintf(stderr, "dyld: iOSMac ERROR: process attempted to dlopen() dylib with macOS dependency: \n");
+                fprintf(stderr, "\tdlerror: %s\n", &buffer[1]);
+                fprintf(stderr, "\tBacktrace:\n");
+
+                void* stackPointers[128];
+                int stackPointersCnt = backtrace(stackPointers, 128);
+                char** symbolicatedStack = backtrace_symbols(stackPointers, stackPointersCnt);
+                for (int32_t i = 0; i < stackPointersCnt; ++i) {
+                    fprintf(stderr, "\t\t%s\n", symbolicatedStack[i]);
+                }
+                free(symbolicatedStack);
+            }
+        }
+    }
+#endif
 
-       if(p == NULL)
-           _dyld_func_lookup("__dyld_dlopen", (void**)&p);
-       void* result = p(path, mode);
-       // use asm block to prevent tail call optimization
-       // this is needed because dlopen uses __builtin_return_address() and depends on this glue being in the frame chain
-       // <rdar://problem/5313172 dlopen() looks too far up stack, can cause crash>
-       __asm__ volatile(""); 
-       
        return result;
 }
 
 bool dlopen_preflight(const char* path)
 {
-       if ( gUseDyld3 )
-               return dyld3::dlopen_preflight(path);
+    dyld3::ScopedTimer timer(DBG_DYLD_TIMING_DLOPEN_PREFLIGHT, path, 0, 0);
+    bool result = false;
 
-       DYLD_LOCK_THIS_BLOCK;
-    static bool (*p)(const char* path) = NULL;
+    if ( gUseDyld3 ) {
+        result = dyld3::dlopen_preflight_internal(path);
+        return result;
+    }
 
-       if(p == NULL)
-           _dyld_func_lookup("__dyld_dlopen_preflight", (void**)&p);
-       return(p(path));
+    DYLD_LOCK_THIS_BLOCK;
+    static bool (*p)(const char* path, void* callerAddress) = NULL;
+
+    if(p == NULL)
+        _dyld_func_lookup("__dyld_dlopen_preflight_internal", (void**)&p);
+    result = p(path, __builtin_return_address(0));
+    timer.setData4(result);
+    return result;
 }
 
 void* dlsym(void* handle, const char* symbol)
 {
-       if ( gUseDyld3 )
-               return dyld3::dlsym(handle, symbol);
+    dyld3::ScopedTimer timer(DBG_DYLD_TIMING_DLSYM, handle, symbol, 0);
+    void* result = nullptr;
 
-       DYLD_LOCK_THIS_BLOCK;
-    static void* (*p)(void* handle, const char* symbol) = NULL;
+    if ( gUseDyld3 ) {
+        result = dyld3::dlsym_internal(handle, symbol, __builtin_return_address(0));
+        return result;
+    }
 
-       if(p == NULL)
-           _dyld_func_lookup("__dyld_dlsym", (void**)&p);
-       return(p(handle, symbol));
+    DYLD_LOCK_THIS_BLOCK;
+    static void* (*p)(void* handle, const char* symbol, void *callerAddress) = NULL;
+
+    if(p == NULL)
+        _dyld_func_lookup("__dyld_dlsym_internal", (void**)&p);
+    result = p(handle, symbol, __builtin_return_address(0));
+    timer.setData4(result);
+    return result;
 }
 
 const struct dyld_all_image_infos* _dyld_get_all_image_infos()
@@ -1905,6 +1750,31 @@ const void* _dyld_get_shared_cache_range(size_t* length)
        return p(length);
 }
 
+void _dyld_images_for_addresses(unsigned count, const void* addresses[], struct dyld_image_uuid_offset infos[])
+{
+    if ( gUseDyld3 )
+        return dyld3::_dyld_images_for_addresses(count, addresses, infos);
+
+    DYLD_NO_LOCK_THIS_BLOCK;
+    static const void (*p)(unsigned, const void*[], struct dyld_image_uuid_offset[]) = NULL;
+
+    if(p == NULL)
+        _dyld_func_lookup("__dyld_images_for_addresses", (void**)&p);
+    return p(count, addresses, infos);
+}
+
+void _dyld_register_for_image_loads(void (*func)(const mach_header* mh, const char* path, bool unloadable))
+{
+    if ( gUseDyld3 )
+        return dyld3::_dyld_register_for_image_loads(func);
+
+    DYLD_NO_LOCK_THIS_BLOCK;
+    static const void (*p)(void (*)(const mach_header* mh, const char* path, bool unloadable)) = NULL;
+
+    if(p == NULL)
+        _dyld_func_lookup("__dyld_register_for_image_loads", (void**)&p);
+    return p(func);
+}
 
 bool dyld_process_is_restricted()
 {
@@ -1968,7 +1838,7 @@ static void* mapStartOfCache(const char* path, size_t length)
        if ( ::stat(path, &statbuf) == -1 )
                return NULL;
 
-       if ( statbuf.st_size < length )
+       if ( (size_t)statbuf.st_size < length )
                return NULL;
 
        int cache_fd = ::open(path, O_RDONLY);
@@ -2126,5 +1996,3 @@ void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
 
 
 
-
-
index 487835e4db9e55baa68b6606fb164fed83c70f5e..96db62e5167cdf8ce6c76ae1458ad7274eab3ed9 100644 (file)
@@ -78,7 +78,8 @@ char* __cxa_get_globals()
        }
        char* data = (char*)_ZN4dyld17gLibSystemHelpersE->pthread_getspecific(sCxaKey);
        if ( data == NULL ) {
-               data = calloc(2,sizeof(void*));
+        long* t = (long*)calloc(2,sizeof(long));
+               data = (char*)t;
                _ZN4dyld17gLibSystemHelpersE->pthread_setspecific(sCxaKey, data);
        }
        return data; 
index 692b271ec703d06e4660ebc4f5e9fe161ce420c6..e58d08e65f5d41066650a93ab112a2583685ce72 100644 (file)
@@ -67,6 +67,23 @@ extern void syncProcessInfo();
        #define POINTER_RELOC GENERIC_RELOC_VANILLA
 #endif
 
+#ifndef BIND_OPCODE_THREADED
+#define BIND_OPCODE_THREADED    0xD0
+#endif
+
+#ifndef BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB
+#define BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB    0x00
+#endif
+
+#ifndef BIND_SUBOPCODE_THREADED_APPLY
+#define BIND_SUBOPCODE_THREADED_APPLY                                0x01
+#endif
+
+
+#if __has_feature(ptrauth_calls)
+#include <ptrauth.h>
+#endif
+
 
 #if TARGET_IPHONE_SIMULATOR
 const dyld::SyscallHelpers* gSyscallHelpers = NULL;
@@ -124,6 +141,43 @@ static uintptr_t slideOfMainExecutable(const struct macho_header* mh)
        return 0;
 }
 
+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 |= (~0ULL) << bit;
+    return result;
+}
+
 
 //
 // If the kernel does not load dyld at its preferred address, we need to apply 
@@ -136,7 +190,215 @@ static void rebaseDyld(const struct macho_header* mh, intptr_t slide)
        const uint32_t cmd_count = mh->ncmds;
        const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header));
        const struct load_command* cmd = cmds;
-       const struct macho_segment_command* linkEditSeg = NULL;
+
+    // First look for compressed info and use it if it exists.
+    const struct macho_segment_command* linkEditSeg = NULL;
+    const dyld_info_command* dyldInfoCmd = NULL;
+    for (uint32_t i = 0; i < cmd_count; ++i) {
+        switch (cmd->cmd) {
+            case LC_SEGMENT_COMMAND:
+            {
+                const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
+                if ( strcmp(seg->segname, "__LINKEDIT") == 0 )
+                    linkEditSeg = seg;
+                break;
+            }
+            case LC_DYLD_INFO_ONLY:
+                dyldInfoCmd = (struct dyld_info_command*)cmd;
+                break;
+        }
+        if (dyldInfoCmd && linkEditSeg)
+            break;
+        cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+    }
+    if ( linkEditSeg == NULL )
+        throw "dyld missing LINKEDIT";
+
+    // Reset the iterator.
+    cmd = cmds;
+
+    auto getSegmentAtIndex = [cmd_count, cmds](unsigned segIndex) -> const struct macho_segment_command* {
+        const struct load_command* cmd = cmds;
+        for (uint32_t i = 0; i < cmd_count; ++i) {
+            switch (cmd->cmd) {
+                case LC_SEGMENT_COMMAND:
+                    if (!segIndex) {
+                        const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
+                        return seg;
+                    }
+                    --segIndex;
+                    break;
+            }
+            cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+        }
+        throw "out of bounds command";
+        return 0;
+    };
+
+    auto segActualLoadAddress = [&](unsigned segIndex) -> uintptr_t {
+        const struct macho_segment_command* seg = getSegmentAtIndex(segIndex);
+        return seg->vmaddr + slide;
+    };
+
+#if __has_feature(ptrauth_calls)
+    auto imageBaseAddress = [cmds, cmd_count]() -> uintptr_t {
+        const struct load_command* cmd = cmds;
+        for (uint32_t i = 0; i < cmd_count; ++i) {
+            switch (cmd->cmd) {
+                case LC_SEGMENT_COMMAND: {
+                    const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
+                    if ( (seg->fileoff == 0) && (seg->filesize != 0) )
+                        return seg->vmaddr;
+                    break;
+                }
+            }
+            cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+        }
+        return 0;
+    };
+#endif
+
+    if (dyldInfoCmd && (dyldInfoCmd->bind_size != 0) ) {
+        if ( dyldInfoCmd->rebase_size != 0 )
+            throw "unthreaded rebases are not supported";
+
+        const uint8_t* linkEditBase = (uint8_t*)(linkEditSeg->vmaddr + slide - linkEditSeg->fileoff);
+
+        const uint8_t* const start = linkEditBase + dyldInfoCmd->bind_off;
+        const uint8_t* const end = &start[dyldInfoCmd->bind_size];
+        const uint8_t* p = start;
+
+        uintptr_t segmentStartAddress = 0;
+        uint64_t segOffset = 0;
+        int segIndex = 0;
+#if __has_feature(ptrauth_calls)
+        uintptr_t fBaseAddress = imageBaseAddress();
+#endif
+        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:
+                    break;
+                case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
+                    break;
+                case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
+                    break;
+                case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
+                    while (*p != '\0')
+                        ++p;
+                    ++p;
+                    break;
+                case BIND_OPCODE_SET_TYPE_IMM:
+                    break;
+                case BIND_OPCODE_SET_ADDEND_SLEB:
+                    read_sleb128(p, end);
+                    break;
+                case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
+                    segIndex = immediate;
+                    segmentStartAddress = segActualLoadAddress(segIndex);
+                    segOffset = read_uleb128(p, end);
+                    break;
+                case BIND_OPCODE_ADD_ADDR_ULEB:
+                    segOffset += read_uleb128(p, end);
+                    break;
+                case BIND_OPCODE_DO_BIND:
+                    break;
+                case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
+                    break;
+                case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
+                    break;
+                case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
+                    read_uleb128(p, end);
+                    read_uleb128(p, end);
+                    break;
+                case BIND_OPCODE_THREADED:
+                    // Note the immediate is a sub opcode
+                    switch (immediate) {
+                        case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB:
+                            read_uleb128(p, end);
+                            break;
+                        case BIND_SUBOPCODE_THREADED_APPLY: {
+                            uint64_t delta = 0;
+                            do {
+                                uintptr_t address = segmentStartAddress + (uintptr_t)segOffset;
+                                uint64_t value = *(uint64_t*)address;
+
+#if __has_feature(ptrauth_calls)
+                                uint16_t diversity = (uint16_t)(value >> 32);
+                                bool hasAddressDiversity = (value & (1ULL << 48)) != 0;
+                                ptrauth_key key = (ptrauth_key)((value >> 49) & 0x3);
+                                bool isAuthenticated = (value & (1ULL << 63)) != 0;
+#endif
+                                bool isRebase = (value & (1ULL << 62)) == 0;
+                                if (isRebase) {
+
+#if __has_feature(ptrauth_calls)
+                                    if (isAuthenticated) {
+                                        // The new value for a rebase is the low 32-bits of the threaded value plus the slide.
+                                        uint64_t newValue = (value & 0xFFFFFFFF) + slide;
+                                        // Add in the offset from the mach_header
+                                        newValue += fBaseAddress;
+                                        // We have bits to merge in to the discriminator
+                                        uintptr_t discriminator = diversity;
+                                        if (hasAddressDiversity) {
+                                            // First calculate a new discriminator using the address of where we are trying to store the value
+                                            discriminator = __builtin_ptrauth_blend_discriminator((void*)address, discriminator);
+                                        }
+                                        switch (key) {
+                                            case ptrauth_key_asia:
+                                                newValue = (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)newValue, ptrauth_key_asia, discriminator);
+                                                break;
+                                            case ptrauth_key_asib:
+                                                newValue = (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)newValue, ptrauth_key_asib, discriminator);
+                                                break;
+                                            case ptrauth_key_asda:
+                                                newValue = (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)newValue, ptrauth_key_asda, discriminator);
+                                                break;
+                                            case ptrauth_key_asdb:
+                                                newValue = (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)newValue, ptrauth_key_asdb, discriminator);
+                                                break;
+                                        }
+                                        *(uint64_t*)address = newValue;
+                                    } else
+#endif
+                                    {
+                                        // Regular pointer which needs to fit in 51-bits of value.
+                                        // C++ RTTI uses the top bit, so we'll allow the whole top-byte
+                                        // and the signed-extended bottom 43-bits to be fit in to 51-bits.
+                                        uint64_t top8Bits = value & 0x0007F80000000000ULL;
+                                        uint64_t bottom43Bits = value & 0x000007FFFFFFFFFFULL;
+                                        uint64_t targetValue = ( top8Bits << 13 ) | (((intptr_t)(bottom43Bits << 21) >> 21) & 0x00FFFFFFFFFFFFFF);
+                                        targetValue = targetValue + slide;
+                                        *(uint64_t*)address = targetValue;
+                                    }
+                                }
+
+                                // The delta is bits [51..61]
+                                // And bit 62 is to tell us if we are a rebase (0) or bind (1)
+                                value &= ~(1ULL << 62);
+                                delta = ( value & 0x3FF8000000000000 ) >> 51;
+                                segOffset += delta * sizeof(uintptr_t);
+                            } while ( delta != 0 );
+                            break;
+                        }
+                        default:
+                            throw "unknown threaded bind subopcode";
+                    }
+                    break;
+                default:
+                    throw "unknown bind opcode";
+            }
+        }
+        return;
+    }
+
 #if __x86_64__
        const struct macho_segment_command* firstWritableSeg = NULL;
 #endif
@@ -146,8 +408,6 @@ static void rebaseDyld(const struct macho_header* mh, intptr_t slide)
                        case LC_SEGMENT_COMMAND:
                                {
                                        const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
-                                       if ( strcmp(seg->segname, "__LINKEDIT") == 0 )
-                                               linkEditSeg = seg;
                                        const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
                                        const struct macho_section* const sectionsEnd = &sectionsStart[seg->nsects];
                                        for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
@@ -176,6 +436,8 @@ static void rebaseDyld(const struct macho_header* mh, intptr_t slide)
        
        // use reloc's to rebase all random data pointers
 #if __x86_64__
+    if ( firstWritableSeg == NULL )
+        throw "no writable segment in dyld";
        const uintptr_t relocBase = firstWritableSeg->vmaddr + slide;
 #else
        const uintptr_t relocBase = (uintptr_t)mh;
@@ -209,9 +471,14 @@ uintptr_t start(const struct macho_header* appsMachHeader, int argc, const char*
 {
        // if kernel had to slide dyld, we need to fix up load sensitive locations
        // we have to do this before using any global variables
-       if ( slide != 0 ) {
-               rebaseDyld(dyldsMachHeader, slide);
-       }
+    slide = slideOfMainExecutable(dyldsMachHeader);
+    bool shouldRebase = slide != 0;
+#if __has_feature(ptrauth_calls)
+    shouldRebase = true;
+#endif
+    if ( shouldRebase ) {
+        rebaseDyld(dyldsMachHeader, slide);
+    }
 
        // allow dyld to use mach messaging
        mach_init();
index f39a275d3b453d14659662f43e834d718cee8c64..69da7884ca3767555ce691f5db053bfafb557fe1 100644 (file)
@@ -41,19 +41,19 @@ namespace dyld {
        struct LibSystemHelpers
        {
                uintptr_t       version;
-               void            (*acquireGlobalDyldLock)();
-               void            (*releaseGlobalDyldLock)();
+               void            (*acquireGlobalDyldLock)(void);
+               void            (*releaseGlobalDyldLock)(void);
                char*           (*getThreadBufferFor_dlerror)(size_t sizeRequired);
                // addded in version 2
                void*           (*malloc)(size_t);
                void            (*free)(void*);
                int                     (*cxa_atexit)(void (*)(void*), void*, void*);
                // addded in version 3
-               void            (*dyld_shared_cache_missing)();
-               void            (*dyld_shared_cache_out_of_date)();
+               void            (*dyld_shared_cache_missing)(void);
+               void            (*dyld_shared_cache_out_of_date)(void);
                // addded in version 4
-               void            (*acquireDyldInitializerLock)();
-               void            (*releaseDyldInitializerLock)();
+               void            (*acquireDyldInitializerLock)(void);
+               void            (*releaseDyldInitializerLock)(void);
                // added in version 5
                int                     (*pthread_key_create)(pthread_key_t*, void (*destructor)(void*));
                int                     (*pthread_setspecific)(pthread_key_t, const void*);
@@ -66,9 +66,9 @@ namespace dyld {
                // added in version 9
                void*           startGlueToCallExit;
                // added in version 10
-               bool            (*hasPerThreadBufferFor_dlerror)();
+               bool            (*hasPerThreadBufferFor_dlerror)(void);
                // added in version 11
-               bool            (*isLaunchdOwned)();
+               bool            (*isLaunchdOwned)(void);
                // added in version 12
                kern_return_t (*vm_alloc)(vm_map_t task, vm_address_t* addr, vm_size_t size, int flags);
                void*           (*mmap)(void* addr, size_t len, int prot, int flags, int fd, off_t offset);
index fb393fa0b45c15c8dfc3b1edbf6e3899f9a05e73..1511a517ba0a0e3d08f1eda3abce4c94e82efe38 100644 (file)
@@ -69,8 +69,8 @@ void* malloc(size_t size)
        }
        else {
                if ( size > DYLD_POOL_CHUNK_SIZE ) {
-                       dyld::log("dyld malloc overflow: size=%zu\n", size);
-                       exit(1);
+                       dyld::log("dyld malloc overflow: size=%lu\n", size);
+                       dyld::halt("dyld malloc overflow\n");
                }
                size = (size+sizeof(void*)-1) & (-sizeof(void*)); // pointer align
                uint8_t* result = currentPool->current;
@@ -79,8 +79,7 @@ void* malloc(size_t size)
                        vm_address_t addr = 0;
                        kern_return_t r = vm_allocate(mach_task_self(), &addr, DYLD_POOL_CHUNK_SIZE, VM_FLAGS_ANYWHERE);
                        if ( r != KERN_SUCCESS ) {
-                               dyld::log("out of address space for dyld memory pool\n");
-                               exit(1);
+                               dyld::halt("out of address space for dyld memory pool\n");
                        }
                        dyld_static_pool* newPool = (dyld_static_pool*)addr;
                        newPool->previousPool = NULL;
@@ -90,7 +89,7 @@ void* malloc(size_t size)
                        currentPool = newPool;
                        if ( (currentPool->current + size) > currentPool->end ) {
                                dyld::log("dyld memory pool exhausted: size=%lu\n", size);
-                               exit(1);
+                               dyld::halt("dyld memory pool exhausted\n");
                        }
                        result = currentPool->current;
                        currentPool->current += size;
index 8f71e0c8e42b2bed70a39566f810cc1595fe86f3..34522bfd6c2f0001b008e33e11a665d61da46c6f 100644 (file)
@@ -2,14 +2,14 @@
  * Copyright (c) 1999-2011 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
- * 
+ *
  * This file contains Original Code and/or Modifications of Original Code
  * as defined in and that are subject to the Apple Public Source License
  * Version 2.0 (the 'License'). You may not use this file except in
  * compliance with the License. Please obtain a copy of the License at
  * http://www.opensource.apple.com/apsl/ and read it before using this
  * file.
- * 
+ *
  * The Original Code and all software distributed under the License are
  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
@@ -17,7 +17,7 @@
  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  * Please see the License for the specific language governing rights and
  * limitations under the License.
- * 
+ *
  * @APPLE_LICENSE_HEADER_END@
  */
 /*
  *
  *     | STRING AREA |
  *     +-------------+
- *     |      0      | 
+ *     |      0      |
 *      +-------------+
  *     |  apple[n]   |
  *     +-------------+
  *            :
  *     +-------------+
- *     |  apple[0]   | 
- *     +-------------+ 
+ *     |  apple[0]   |
+ *     +-------------+
  *     |      0      |
  *     +-------------+
  *     |    env[n]   |
@@ -80,9 +80,9 @@ __dyld_start:
        movl    %esp,%ebp       # pointer to base of kernel frame
        andl    $-16,%esp       # force SSE alignment
        subl    $32,%esp        # room for locals and outgoing parameters
-       
+
        call    L__dyld_start_picbase
-L__dyld_start_picbase: 
+L__dyld_start_picbase:
        popl    %ebx            # set %ebx to runtime value of picbase
 
        movl    Lmh-L__dyld_start_picbase(%ebx), %ecx # ecx = prefered load address
@@ -91,15 +91,15 @@ L__dyld_start_picbase:
        addl    %ebx, %ecx      # ecx = actual load address
        # call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh, &startGlue)
        movl    %edx,(%esp)     # param1 = app_mh
-       movl    4(%ebp),%eax    
+       movl    4(%ebp),%eax
        movl    %eax,4(%esp)    # param2 = argc
-       lea     8(%ebp),%eax    
+       lea     8(%ebp),%eax
        movl    %eax,8(%esp)    # param3 = argv
        movl    %ebx,12(%esp)   # param4 = slide
        movl    %ecx,16(%esp)   # param5 = actual load address
        lea     28(%esp),%eax
        movl    %eax,20(%esp)   # param6 = &startGlue
-       call    __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_Pm   
+       call    __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_Pm
        movl    28(%esp),%edx
        cmpl    $0,%edx
        jne     Lnew
@@ -110,7 +110,7 @@ L__dyld_start_picbase:
        movl    $0,%ebp         # restore ebp back to zero
        jmp     *%eax           # jump to the entry point
 
-       # LC_MAIN case, set up stack for call to main() 
+       # LC_MAIN case, set up stack for call to main()
 Lnew:  movl    4(%ebp),%ebx
        movl    %ebx,(%esp)     # main param1 = argc
        leal    8(%ebp),%ecx
@@ -124,11 +124,11 @@ Lapple:   movl    (%ebx),%ecx     # look for NULL ending env[] array
        movl    %ebx,12(%esp)   # main param4 = apple
        pushl   %edx            # simulate return address into _start in libdyld
        jmp     *%eax           # jump to main(argc,argv,env,apple) with return address set to _start
-#endif 
+#endif
 
 #if !TARGET_IPHONE_SIMULATOR
        .data
-__dyld_start_static_picbase: 
+__dyld_start_static_picbase:
        .long   L__dyld_start_picbase
 Lmh:   .long   ___dso_handle
 #endif
@@ -142,7 +142,7 @@ Lmh:        .long   ___dso_handle
 #if !TARGET_IPHONE_SIMULATOR
        .data
        .align 3
-__dyld_start_static: 
+__dyld_start_static:
        .quad   __dyld_start
 #endif
 
@@ -157,7 +157,7 @@ __dyld_start:
        movq    %rsp,%rbp       # pointer to base of kernel frame
        andq    $-16,%rsp       # force SSE alignment
        subq    $16,%rsp        # room for local variables
-       
+
        # call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh, &startGlue)
        movl    8(%rbp),%esi    # param2 = argc into %esi
        leaq    16(%rbp),%rdx   # param3 = &argv[0] into %rdx
@@ -176,8 +176,8 @@ __dyld_start:
        addq    $8,%rsp         # remove the mh argument, and debugger end frame marker
        movq    $0,%rbp         # restore ebp back to zero
        jmp     *%rax           # jump to the entry point
-       
-       # LC_MAIN case, set up stack for call to main() 
+
+       # LC_MAIN case, set up stack for call to main()
 Lnew:  addq    $16,%rsp        # remove local variables
        pushq   %rdi            # simulate return address into _start in libdyld
        movq    8(%rbp),%rdi    # main param1 = argc into %rdi
@@ -199,10 +199,10 @@ Lapple: movq      (%rcx),%r8
        .syntax unified
        .data
        .align 2
-__dyld_start_static_picbase: 
+__dyld_start_static_picbase:
        .long   L__dyld_start_picbase
 
-    
+
        // Hack to make ___dso_handle work
        // Without this local symbol, assembler will error out about in subtraction expression
        // The real ___dso_handle (non-weak) sythesized by the linker
@@ -232,16 +232,16 @@ L__dyld_start_picbase:
        add     r2, r8, #8      // r2 = argv
 
        ldr     r4, Lmh
-L3:    add     r4, r4, pc      
+L3:    add     r4, r4, pc
        str     r4, [sp, #0]    // [sp] = dyld_mh
        add     r4, sp, #12
        str     r4, [sp, #4]    // [sp+4] = &startGlue
-       
+
        bl      __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_Pm
        ldr     r5, [sp, #12]
        cmp     r5, #0
        bne     Lnew
-       
+
        // traditional case, clean up stack and jump to result
        add     sp, r8, #4      // remove the mach_header argument.
        bx      r0              // jump to the program's entry point
@@ -251,7 +251,7 @@ Lnew:       mov     lr, r5              // simulate return address into _start in libdyld
        mov     r5, r0              // save address of main() for later use
        ldr     r0, [r8, #4]        // main param1 = argc
        add     r1, r8, #8          // main param2 = argv
-       add     r2, r1, r0, lsl #2  
+       add     r2, r1, r0, lsl #2
        add     r2, r2, #4          // main param3 = &env[0]
        mov     r3, r2
 Lapple:        ldr     r4, [r3]
@@ -273,7 +273,7 @@ Lmh:        .long   ___dso_handle-L3-8
 #if __arm64__
        .data
        .align 3
-__dso_static: 
+__dso_static:
        .quad   ___dso_handle
 
        .text
@@ -287,39 +287,71 @@ __dyld_start:
        stp     x1, x0, [sp, #-16]!     // make aligned terminating frame
        mov     fp, sp                  // set up fp to point to terminating frame
        sub     sp, sp, #16             // make room for local variables
-       ldr     x0, [x28]               // get app's mh into x0
-       ldr     x1, [x28, #8]           // get argc into x1 (kernel passes 32-bit int argc as 64-bits on stack to keep alignment)
-       add     x2, x28, #16            // get argv into x2
+#if __LP64__
+       ldr     x0, [x28]               // get app's mh into x0
+       ldr     x1, [x28, #8]           // get argc into x1 (kernel passes 32-bit int argc as 64-bits on stack to keep alignment)
+       add     x2, x28, #16            // get argv into x2
+#else
+       ldr     w0, [x28]               // get app's mh into x0
+       ldr     w1, [x28, #4]           // get argc into x1 (kernel passes 32-bit int argc as 64-bits on stack to keep alignment)
+       add     w2, w28, #8             // get argv into x2
+#endif
        adrp    x4,___dso_handle@page
        add     x4,x4,___dso_handle@pageoff // get dyld's mh in to x4
        adrp    x3,__dso_static@page
        ldr     x3,[x3,__dso_static@pageoff] // get unslid start of dyld
        sub     x3,x4,x3                // x3 now has slide of dyld
        mov     x5,sp                   // x5 has &startGlue
-       
+
        // call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh, &startGlue)
        bl      __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_Pm
        mov     x16,x0                  // save entry point address in x16
+#if __LP64__
        ldr     x1, [sp]
+#else
+       ldr     w1, [sp]
+#endif
        cmp     x1, #0
        b.ne    Lnew
 
        // LC_UNIXTHREAD way, clean up stack and jump to result
-       add     sp, x28, #8             // restore unaligned stack pointer without app mh
-       br      x16                     // jump to the program's entry point
+#if __LP64__
+       add     sp, x28, #8             // restore unaligned stack pointer without app mh
+#else
+       add     sp, x28, #4             // restore unaligned stack pointer without app mh
+#endif
+#if __arm64e__
+       braaz   x16                     // jump to the program's entry point
+#else
+       br      x16                     // jump to the program's entry point
+#endif
 
        // LC_MAIN case, set up stack for call to main()
 Lnew:  mov     lr, x1              // simulate return address into _start in libdyld.dylib
-       ldr     x0, [x28, #8]       // main param1 = argc
-       add     x1, x28, #16        // main param2 = argv
-       add     x2, x1, x0, lsl #3  
-       add     x2, x2, #8          // main param3 = &env[0]
+#if __LP64__
+       ldr     x0, [x28, #8]       // main param1 = argc
+       add     x1, x28, #16        // main param2 = argv
+       add     x2, x1, x0, lsl #3
+       add     x2, x2, #8          // main param3 = &env[0]
        mov     x3, x2
 Lapple:        ldr     x4, [x3]
        add     x3, x3, #8
+#else
+       ldr     w0, [x28, #4]       // main param1 = argc
+       add     x1, x28, #8         // main param2 = argv
+       add     x2, x1, x0, lsl #2
+       add     x2, x2, #4          // main param3 = &env[0]
+       mov     x3, x2
+Lapple:        ldr     w4, [x3]
+       add     x3, x3, #4
+#endif
        cmp     x4, #0
        b.ne    Lapple              // main param4 = apple
-       br      x16
+#if __arm64e__
+       braaz   x16
+#else
+       br      x16
+#endif
 
 #endif // __arm64__
 
index c8b90d33c9b449e16103090d229d26b3dbd4bc17..c8ea4dfadaddb1b7f54e7052693a5777cf4d1154 100644 (file)
@@ -67,8 +67,8 @@ namespace dyld {
                bool                    (*OSAtomicCompareAndSwapPtrBarrier)(void* old, void* nw, void * volatile *value);
                void                    (*OSMemoryBarrier)(void);
                void*                   (*getProcessInfo)(void); // returns dyld_all_image_infos*;
-               int*                    (*errnoAddress)();
-               uint64_t                (*mach_absolute_time)();
+               int*                    (*errnoAddress)(void);
+               uint64_t                (*mach_absolute_time)(void);
                // Added in version 2
                kern_return_t   (*thread_switch)(mach_port_name_t, int, mach_msg_timeout_t);
                // Added in version 3
@@ -80,7 +80,7 @@ namespace dyld {
                void                    (*coresymbolication_unload_notifier)(void *connection, uint64_t unload_timestamp, const char *image_path, const struct mach_header *mach_header);
                // Added in version 5
                int                             (*proc_regionfilename)(int pid, uint64_t address, void* buffer, uint32_t buffersize);
-               int                             (*getpid)();
+               int                             (*getpid)(void);
                kern_return_t   (*mach_port_insert_right)(ipc_space_t task, mach_port_name_t name, mach_port_t poly, mach_msg_type_name_t polyPoly);
                kern_return_t   (*mach_port_allocate)(ipc_space_t, mach_port_right_t, mach_port_name_t*);
                kern_return_t   (*mach_msg)(mach_msg_header_t *, mach_msg_option_t , mach_msg_size_t , mach_msg_size_t , mach_port_name_t , mach_msg_timeout_t , mach_port_name_t);
@@ -95,8 +95,20 @@ namespace dyld {
         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);
+        // Add in version 8
         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);
+        // Add in version 9
+        uint64_t        (*kdebug_trace_string)(uint32_t debugid, uint64_t str_id, const char *str);
+        // Add in version 10
+        int             (*amfi_check_dyld_policy_self)(uint64_t input_flags, uint64_t* output_flags);
+        // Add in version 11
+        void            (*notifyMonitoringDyldMain)(void);
+        void            (*notifyMonitoringDyld)(bool unloading, unsigned imageCount, const struct mach_header* loadAddresses[], const char* imagePaths[]);
+        // Add in version 12
+        void              (*mach_msg_destroy)(mach_msg_header_t *msg);
+        kern_return_t     (*mach_port_construct)(ipc_space_t task, mach_port_options_ptr_t options, mach_port_context_t context, mach_port_name_t *name);
+        kern_return_t     (*mach_port_destruct)(ipc_space_t task, mach_port_name_t name, mach_port_delta_t srdelta, mach_port_context_t guard);
     };
        extern const struct SyscallHelpers* gSyscallHelpers;
 
index 5f4e7b5ccea3074131214642e4c9eb910e412040..7c3ef2a796565b4aa63cc87c307587216aea8f4e 100644 (file)
@@ -33,6 +33,7 @@
 #include "mach-o/dyld_gdb.h"
 #include "mach-o/dyld_images.h"
 #include "mach-o/dyld_process_info.h"
+#include "Tracing.h"
 #include "ImageLoader.h"
 #include "dyld.h"
 
@@ -191,6 +192,7 @@ void removeImageFromAllImages(const struct mach_header* loadAddress)
 
        static void gdb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, const dyld_image_info info[])
        {
+               dyld3::ScopedTimer(DBG_DYLD_GDB_IMAGE_NOTIFIER, 0, 0, 0);
                uint64_t machHeaders[infoCount];
                for (uint32_t i=0; i < infoCount; ++i) {
                        machHeaders[i] = (uintptr_t)(info[i].imageLoadAddress);
@@ -228,10 +230,10 @@ void removeImageFromAllImages(const struct mach_header* loadAddress)
 
        struct dyld_all_image_infos  dyld_all_image_infos __attribute__ ((section ("__DATA,__all_image_info"))) 
                                                                = { 
-                                                                       15, 0, NULL, &gdb_image_notifier, false, false, (const mach_header*)&__dso_handle, NULL,
-                                                                       XSTR(DYLD_VERSION), NULL, 0, NULL, 0, 0, NULL, &dyld_all_image_infos, 
+                                                                       15, 0, {NULL}, &gdb_image_notifier, false, false, (const mach_header*)&__dso_handle, NULL,
+                                                                       XSTR(DYLD_VERSION), NULL, 0, NULL, 0, 0, NULL, &dyld_all_image_infos,
                                                                        0, 0, NULL, NULL, NULL, 0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},
-                                                                       0, 0, "/usr/lib/dyld", {0}, {0}
+                                                                       0, {0}, "/usr/lib/dyld", {0}, {0}
                                                                        };
 
        struct dyld_shared_cache_ranges dyld_shared_cache_ranges;
index fc47bcecafa26257edb7bf0e7b32714117624a69..e373fa9a32fbabd7933d5703a114cbb92c8a12de 100644 (file)
 #include "dyld_images.h"
 #include "dyld_priv.h"
 
+#include "Tracing.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, size_t size)) {
-    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), local_size);
-            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;
+RemoteBuffer::RemoteBuffer() : _localAddress(0), _size(0) {}
+bool RemoteBuffer::map(task_t task, mach_vm_address_t remote_address, bool shared) {
+    vm_prot_t cur_protection = VM_PROT_NONE;
+    vm_prot_t max_protection = VM_PROT_NONE;
+    if (_size == 0) {
+        _kr = KERN_NO_SPACE;
+        return false;
+    }
+    _localAddress = 0;
+    _kr = mach_vm_remap(mach_task_self(),
+                        &_localAddress,
+                        _size,
+                        0,  // mask
+                        VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR | (shared ? 0 : VM_FLAGS_RESILIENT_CODESIGN),
+                        task,
+                        remote_address,
+                        !shared,
+                        &cur_protection,
+                        &max_protection,
+                        VM_INHERIT_NONE);
+    dyld3::kdebug_trace_dyld_marker(DBG_DYLD_DEBUGGING_VM_REMAP, _localAddress, (uint64_t)_size, _kr, remote_address);
+    if (shared && (cur_protection != (VM_PROT_READ|VM_PROT_WRITE))) {
+        if (_kr == KERN_SUCCESS && _localAddress != 0) {
+            _kr = vm_deallocate(mach_task_self(), _localAddress, _size);
+            dyld3::kdebug_trace_dyld_marker(DBG_DYLD_DEBUGGING_VM_UNMAP, _localAddress, (uint64_t)_size, _kr, 0);
         }
+        _localAddress = 0;
+        _kr = KERN_PROTECTION_FAILURE;
     }
+    return (_kr == KERN_SUCCESS);
 }
 
-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, size_t size) {
-        block(*reinterpret_cast<T *>(buffer));
-    });
+RemoteBuffer::RemoteBuffer(task_t task, mach_vm_address_t remote_address, size_t remote_size, bool shared, bool allow_truncation)
+        : _localAddress(0), _size(remote_size), _kr(KERN_SUCCESS) {
+    // Try the initial map
+    if (map(task, remote_address, shared)) return;
+    // It failed, try to calculate the largest size that can fit in the same page as the remote_address
+    uint64_t newSize = PAGE_SIZE - remote_address%PAGE_SIZE;;
+    // If truncation is allowed and the newSize is different than the original size try that
+    if (!allow_truncation && newSize != _size) return;
+    _size = newSize;
+    if (map(task, remote_address, shared)) return;
+    // That did not work, null out the buffer
+    _size = 0;
+    _localAddress = 0;
 }
-};
+RemoteBuffer::~RemoteBuffer() {
+    if (_localAddress) {
+        _kr = vm_deallocate(mach_task_self(), _localAddress, _size);
+        dyld3::kdebug_trace_dyld_marker(DBG_DYLD_DEBUGGING_VM_UNMAP, _localAddress, (uint64_t)_size, _kr, 0);
+    }
+}
+void *RemoteBuffer::getLocalAddress() { return (void *)_localAddress; }
+size_t RemoteBuffer::getSize() { return _size; }
+kern_return_t RemoteBuffer::getKernelReturn() { return _kr; }
+
+void withRemoteBuffer(task_t task, mach_vm_address_t remote_address, size_t remote_size, bool shared, bool allow_truncation, kern_return_t *kr, void (^block)(void *buffer, size_t size)) {
+    kern_return_t krSink = KERN_SUCCESS;
+    if (kr == nullptr) {
+        kr = &krSink;
+    }
+    RemoteBuffer buffer(task, remote_address, remote_size, shared, allow_truncation);
+    *kr = buffer.getKernelReturn();
+    if (*kr == KERN_SUCCESS) {
+        block(buffer.getLocalAddress(), buffer.getSize());
+    }
+}
+
 
 //
 // Opaque object returned by _dyld_process_info_create()
 //
 
+struct __attribute__((visibility("hidden"))) dyld_process_info_deleter { // deleter
+    //    dyld_process_info_deleter() {};
+    //    dyld_process_info_deleter(const dyld_process_info_deleter&) { }
+    //    dyld_process_info_deleter(dyld_process_info_deleter&) {}
+    //    dyld_process_info_deleter(dyld_process_info_deleter&&) {}
+    void operator()(dyld_process_info_base* p) const {
+        if (p) {
+            free(p);
+        }
+    };
+};
+
+static dyld_process_info_deleter deleter;
+typedef std::unique_ptr<dyld_process_info_base, dyld_process_info_deleter> dyld_process_info_ptr;
+
 struct __attribute__((visibility("hidden"))) dyld_process_info_base {
-    static dyld_process_info_base* make(task_t task, const dyld_all_image_infos_64& allImageInfo, const dyld_image_info_64 imageArray[], kern_return_t* kr);
-    static dyld_process_info_base* makeSuspended(task_t task, kern_return_t* kr);
+    template<typename T1, typename T2>
+    static dyld_process_info_ptr make(task_t task, const T1& allImageInfo, uint64_t timestamp, kern_return_t* kr);
+    template<typename T>
+    static dyld_process_info_ptr makeSuspended(task_t task, const T& allImageInfo, kern_return_t* kr);
 
-    uint32_t&                   retainCount() const { return _retainCount; }
+    std::atomic<uint32_t>&      retainCount() const { return _retainCount; }
     dyld_process_cache_info*    cacheInfo() const { return (dyld_process_cache_info*)(((char*)this) + _cacheInfoOffset); }
     dyld_process_state_info*    stateInfo() const { return (dyld_process_state_info*)(((char*)this) + _stateInfoOffset); }
     void                        forEachImage(void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)) const;
     void                        forEachSegment(uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)) const;
 
+    void retain()
+    {
+        _retainCount++;
+    }
+
+    void release()
+    {
+        uint32_t newCount = --_retainCount;
+
+        if ( newCount == 0 ) {
+            free(this);
+        }
+    }
+
 private:
     struct ImageInfo {
         uuid_t                  uuid;
@@ -129,6 +179,7 @@ private:
 
     static bool                 inCache(uint64_t addr) { return (addr > SHARED_REGION_BASE) && (addr < SHARED_REGION_BASE+SHARED_REGION_SIZE); }
     kern_return_t               addImage(task_t task, bool sameCacheAsThisProcess, uint64_t imageAddress, uint64_t imagePath, const char* imagePathLocal);
+
     kern_return_t               addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath);
 
     bool                        invalid() { return ((char*)_stringRevBumpPtr < (char*)_curSegment); }
@@ -137,11 +188,12 @@ private:
     const char*                 copySegmentName(const char*);
 
     void                        addInfoFromLoadCommands(const mach_header* mh, uint64_t addressInTask, size_t size);
+    kern_return_t               addInfoFromRemoteLoadCommands(task_t task, uint64_t remoteMH);
 
     void                        inspectLocalImageLoadCommands(uint64_t imageAddress, void* func);
     kern_return_t               inspectRemoteImageLoadCommands(task_t task, uint64_t imageAddress, void* func);
 
-    mutable uint32_t            _retainCount;
+    mutable std::atomic<uint32_t>            _retainCount;
     const uint32_t              _cacheInfoOffset;
     const uint32_t              _stateInfoOffset;
     const uint32_t              _imageInfosOffset;
@@ -174,103 +226,175 @@ dyld_process_info_base::dyld_process_info_base(unsigned imageCount, size_t total
 {
 }
 
-
-dyld_process_info_base* dyld_process_info_base::make(task_t task, const dyld_all_image_infos_64& allImageInfo, const dyld_image_info_64 imageArray[], kern_return_t* kr)
+template<typename T1, typename T2>
+dyld_process_info_ptr dyld_process_info_base::make(task_t task, const T1& allImageInfo, uint64_t timestamp, kern_return_t* kr)
 {
-    // figure out how many path strings will need to be copied and their size
-    const dyld_all_image_infos* myInfo = _dyld_get_all_image_infos();
-    bool sameCacheAsThisProcess = !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) {
-            if ( !inCache(imageArray[i].imageFilePath) )
-                ++countOfPathsNeedingCopying;
-        }
-    }
-    else {
-        countOfPathsNeedingCopying = allImageInfo.infoArrayCount+1;
-    }
-    unsigned imageCountWithDyld = allImageInfo.infoArrayCount+1;
+    __block dyld_process_info_ptr result = nullptr;
 
-    // allocate result object
-    size_t allocationSize =   sizeof(dyld_process_info_base)
-                            + sizeof(dyld_process_cache_info)
-                            + sizeof(dyld_process_state_info)
-                            + sizeof(ImageInfo)*(imageCountWithDyld)
-                            + sizeof(SegmentInfo)*imageCountWithDyld*5
-                            + countOfPathsNeedingCopying*PATH_MAX;
-    void* storage = malloc(allocationSize);
-    dyld_process_info_base* obj =  new (storage) dyld_process_info_base(imageCountWithDyld, allocationSize); // placement new()
-
-    // fill in base info
-    dyld_process_cache_info* cacheInfo = obj->cacheInfo();
-    memcpy(cacheInfo->cacheUUID, allImageInfo.sharedCacheUUID, 16);
-    cacheInfo->cacheBaseAddress    = allImageInfo.sharedCacheBaseAddress;
-    cacheInfo->privateCache        = allImageInfo.processDetachedFromSharedRegion;
-    // if no cache is used, allImageInfo has all zeros for cache UUID
-    cacheInfo->noCache = true;
-    for (int i=0; i < 16; ++i) {
-        if ( cacheInfo->cacheUUID[i] != 0 ) {
-            cacheInfo->noCache = false;
+    // bail out of dyld is too old
+    if ( allImageInfo.version < 15 ) {
+        *kr = KERN_FAILURE;
+        return nullptr;
+    }
+
+    // Check if the process is suspended
+    if (allImageInfo.infoArrayChangeTimestamp == 0) {
+        result = dyld_process_info_base::makeSuspended<T1>(task, allImageInfo, kr);
+        // If we have a result return it, otherwise rescan
+        if (result) {
+            // If it returned the process is suspended and there is nothing more to do
+            return std::move(result);
+        } else {
+            // Check to see if the process change timestamp is greater than 0, if not then sleep to let the process
+            // finish initializing
+            if (allImageInfo.infoArrayChangeTimestamp == 0) {
+                usleep(1000 * 50); // 50ms
+            }
         }
     }
 
-    dyld_process_state_info* stateInfo = obj->stateInfo();
-    stateInfo->timestamp           = allImageInfo.infoArrayChangeTimestamp;
-    stateInfo->imageCount          = imageCountWithDyld;
-    stateInfo->initialImageCount   = (uint32_t)(allImageInfo.initialImageCount+1);
-    if ( allImageInfo.infoArray != 0 )
-        stateInfo->dyldState = dyld_process_state_dyld_initialized;
-    if ( allImageInfo.libSystemInitialized != 0 ) {
-        stateInfo->dyldState = dyld_process_state_libSystem_initialized;
-        if ( allImageInfo.initialImageCount != allImageInfo.infoArrayCount )
-            stateInfo->dyldState = dyld_process_state_program_running;
-    }
-    if ( allImageInfo.errorMessage != 0 )
-        stateInfo->dyldState = allImageInfo.terminationFlags ? dyld_process_state_terminated_before_inits : dyld_process_state_dyld_terminated;
+    // Test to see if there are no changes and we can exit early
+    if (timestamp != 0 && timestamp == allImageInfo.infoArrayChangeTimestamp) {
+        *kr = KERN_SUCCESS;
+        return nullptr;
+    }
+
+    for(uint32_t i = 0; i < 10; ++i) {
+        uint64_t currentTimestamp = allImageInfo.infoArrayChangeTimestamp;
+        mach_vm_address_t infoArray = allImageInfo.infoArray;
+        if (currentTimestamp == 0) continue;
+        if (infoArray == 0) {
+            // Check if the task is suspended mid dylib load and exit early
+            mach_task_basic_info ti;
+            mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT;
+            if ((*kr = task_info(task, MACH_TASK_BASIC_INFO, (task_info_t)&ti, &count))) {
+                continue;
+            }
 
-    // fill in info for dyld
-    if ( allImageInfo.dyldPath != 0 ) {
-        if ( kern_return_t r = obj->addDyldImage(task, allImageInfo.dyldImageLoadAddress, allImageInfo.dyldPath, NULL) ) {
-            if ( kr != NULL )
-                *kr = r;
-            goto fail;
-        }
-    }
+            // The task is suspended, exit
+            if (ti.suspend_count != 0) {
+                // Not exactly correct, but conveys that operation may succeed in the future
+                *kr = KERN_RESOURCE_SHORTAGE;
+                return  nullptr;
+            }
+            continue;
+        };
+
+        // For the moment we are going to truncate any image list longer than 8192 because some programs do
+        // terrible things that corrupt their own image lists and we need to stop clients from crashing
+        // reading them. We can try to do something more advanced in the future. rdar://27446361
+        uint32_t imageCount = allImageInfo.infoArrayCount;
+        imageCount = MIN(imageCount, 8192);
+        size_t imageArraySize = imageCount * sizeof(T2);
+
+        withRemoteBuffer(task, infoArray, imageArraySize, false, false, kr, ^(void *buffer, size_t size) {
+            // figure out how many path strings will need to be copied and their size
+            T2* imageArray = (T2 *)buffer;
+            const dyld_all_image_infos* myInfo = _dyld_get_all_image_infos();
+            bool sameCacheAsThisProcess = !allImageInfo.processDetachedFromSharedRegion
+                && !myInfo->processDetachedFromSharedRegion
+                && ((memcmp(myInfo->sharedCacheUUID, &allImageInfo.sharedCacheUUID[0], 16) == 0)
+                && (myInfo->sharedCacheSlide == allImageInfo.sharedCacheSlide));
+            unsigned countOfPathsNeedingCopying = 0;
+            if ( sameCacheAsThisProcess ) {
+                for (uint32_t i=0; i < imageCount; ++i) {
+                    if ( !inCache(imageArray[i].imageFilePath) )
+                        ++countOfPathsNeedingCopying;
+                }
+            }
+            else {
+                countOfPathsNeedingCopying = imageCount+1;
+            }
+            unsigned imageCountWithDyld = imageCount+1;
+
+            // allocate result object
+            size_t allocationSize = sizeof(dyld_process_info_base)
+                                        + sizeof(dyld_process_cache_info)
+                                        + sizeof(dyld_process_state_info)
+                                        + sizeof(ImageInfo)*(imageCountWithDyld)
+                                        + sizeof(SegmentInfo)*imageCountWithDyld*5
+                                        + countOfPathsNeedingCopying*PATH_MAX;
+            void* storage = malloc(allocationSize);
+            auto info = dyld_process_info_ptr(new (storage) dyld_process_info_base(imageCountWithDyld, allocationSize), deleter);
+            //info =  new (storage) dyld_process_info_base(imageCountWithDyld, allocationSize); // placement new()
+
+            // fill in base info
+            dyld_process_cache_info* cacheInfo = info->cacheInfo();
+            memcpy(cacheInfo->cacheUUID, &allImageInfo.sharedCacheUUID[0], 16);
+            cacheInfo->cacheBaseAddress    = allImageInfo.sharedCacheBaseAddress;
+            cacheInfo->privateCache        = allImageInfo.processDetachedFromSharedRegion;
+            // if no cache is used, allImageInfo has all zeros for cache UUID
+            cacheInfo->noCache = true;
+            for (int i=0; i < 16; ++i) {
+                if ( cacheInfo->cacheUUID[i] != 0 ) {
+                    cacheInfo->noCache = false;
+                }
+            }
 
-    // fill in info for each image
-    for (uint32_t i=0; i < allImageInfo.infoArrayCount; ++i) {
-        if ( kern_return_t r = obj->addImage(task, sameCacheAsThisProcess, imageArray[i].imageLoadAddress, imageArray[i].imageFilePath, NULL) ) {
-           if ( kr != NULL )
-                *kr = r;
-            goto fail;
-        }
-    }
+            dyld_process_state_info* stateInfo = info->stateInfo();
+            stateInfo->timestamp           = currentTimestamp;
+            stateInfo->imageCount          = imageCountWithDyld;
+            stateInfo->initialImageCount   = (uint32_t)(allImageInfo.initialImageCount+1);
+            stateInfo->dyldState = dyld_process_state_dyld_initialized;
 
-    // sanity check internal data did not overflow
-    if ( obj->invalid() )
-        goto fail;
+            if ( allImageInfo.libSystemInitialized != 0 ) {
+                stateInfo->dyldState = dyld_process_state_libSystem_initialized;
+                if ( allImageInfo.initialImageCount != imageCount ) {
+                    stateInfo->dyldState = dyld_process_state_program_running;
+                }
+            }
+            if ( allImageInfo.errorMessage != 0 ) {
+                stateInfo->dyldState = allImageInfo.terminationFlags ? dyld_process_state_terminated_before_inits : dyld_process_state_dyld_terminated;
+            }
+            // fill in info for dyld
+            if ( allImageInfo.dyldPath != 0 ) {
+                if ((*kr = info->addDyldImage(task, allImageInfo.dyldImageLoadAddress, allImageInfo.dyldPath, NULL))) {
+                    result = nullptr;
+                    return;
+                }
+            }
+            // fill in info for each image
+            for (uint32_t i=0; i < imageCount; ++i) {
+                if ((*kr = info->addImage(task, sameCacheAsThisProcess, imageArray[i].imageLoadAddress, imageArray[i].imageFilePath, NULL))) {
+                    result = nullptr;
+                    return;
+                }
+            }
+            // sanity check internal data did not overflow
+            if ( info->invalid() ) {
+                *kr = KERN_FAILURE;
+                result = nullptr;
+                return;
+            }
 
-    return obj;
+            result = std::move(info);
+        });
 
-fail:
-    free(obj);
-    return NULL;
+        if (result) break;
+    }
+
+    return std::move(result);
 }
 
-dyld_process_info_base* dyld_process_info_base::makeSuspended(task_t task, kern_return_t* kr)
+template<typename T>
+dyld_process_info_ptr dyld_process_info_base::makeSuspended(task_t task, const T& allImageInfo, kern_return_t* kr)
 {
     pid_t   pid;
-    kern_return_t result = pid_for_task(task, &pid);
-    if ( result != KERN_SUCCESS ) {
-        if ( kr != NULL )
-            *kr = result;
+    if ((*kr = pid_for_task(task, &pid))) {
         return NULL;
     }
 
+    mach_task_basic_info ti;
+    mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT;
+    if ((*kr = task_info(task, MACH_TASK_BASIC_INFO, (task_info_t)&ti, &count))) {
+        return  nullptr;
+    }
+
+    // The task is not suspended, exit
+    if (ti.suspend_count == 0) {
+        return  nullptr;
+    }
+
     __block unsigned    imageCount = 0;  // main executable and dyld
     __block uint64_t    mainExecutableAddress = 0;
     __block uint64_t    dyldAddress = 0;
@@ -283,14 +407,14 @@ dyld_process_info_base* dyld_process_info_base::makeSuspended(task_t task, kern_
         vm_region_basic_info_data_64_t  info;
         mach_port_t                     objectName;
         unsigned int                    infoCount = VM_REGION_BASIC_INFO_COUNT_64;
-        result = mach_vm_region(task, &address, &size, VM_REGION_BASIC_INFO,
-                         (vm_region_info_t)&info, &infoCount, &objectName);
-        if ( result != KERN_SUCCESS )
+        if (kern_return_t r = mach_vm_region(task, &address, &size, VM_REGION_BASIC_INFO,
+                                             (vm_region_info_t)&info, &infoCount, &objectName)) {
             break;
+        }
         if ( info.protection != (VM_PROT_READ|VM_PROT_EXECUTE) )
             continue;
             // read start of vm region to verify it is a mach header
-            withRemoteObject(task, address, NULL, ^(mach_header_64 mhBuffer){
+            withRemoteObject(task, address, false, 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
@@ -324,8 +448,7 @@ dyld_process_info_base* dyld_process_info_base::makeSuspended(task_t task, kern_
                             + sizeof(SegmentInfo)*imageCount*5
                             + imageCount*PATH_MAX;
     void* storage = malloc(allocationSize);
-    dyld_process_info_base* obj =  new (storage) dyld_process_info_base(imageCount, allocationSize); // placement new()
-
+    auto obj = dyld_process_info_ptr(new (storage) dyld_process_info_base(imageCount, allocationSize), deleter);
     // fill in base info
     dyld_process_cache_info* cacheInfo = obj->cacheInfo();
     bzero(cacheInfo->cacheUUID, 16);
@@ -341,24 +464,32 @@ 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, dyldPath) ) {
-            if ( kr != NULL )
-                *kr = r;
-            free(obj);
-            return NULL;
+        if ((*kr = obj->addDyldImage(task, dyldAddress, 0, dyldPath))) {
+            return nullptr;
         }
     }
 
     // fill in info for each image
     if ( mainExecutableAddress != 0 ) {
-        if ( kern_return_t r = obj->addImage(task, false, mainExecutableAddress, 0, mainExecutablePath) ) {
-            if ( kr != NULL )
-                *kr = r;
-            free(obj);
-            return NULL;
+        if ((*kr = obj->addImage(task, false, mainExecutableAddress, 0, mainExecutablePath))) {
+            return nullptr;
         }
     }
 
+    if (allImageInfo.infoArrayChangeTimestamp != 0) {
+        return  nullptr;
+    }
+
+    count = MACH_TASK_BASIC_INFO_COUNT;
+    if ((*kr = task_info(task, MACH_TASK_BASIC_INFO, (task_info_t)&ti, &count))) {
+        return  nullptr;
+    }
+
+    // The task is not suspended, exit
+    if (ti.suspend_count == 0) {
+        return  nullptr;
+    }
+
     return obj;
 }
 
@@ -375,15 +506,15 @@ const char* dyld_process_info_base::addString(const char* str, size_t maxlen)
 const char* dyld_process_info_base::copyPath(task_t task, uint64_t stringAddressInTask, kern_return_t* kr)
 {
     __block const char* retval = NULL;
-    withRemoteBuffer(task, stringAddressInTask, PATH_MAX, true, kr, ^(void *buffer, size_t size) {
+    withRemoteBuffer(task, stringAddressInTask, PATH_MAX, false, true, kr, ^(void *buffer, size_t size) {
         retval = addString(static_cast<const char *>(buffer), size);
     });
-
     return retval;
 }
 
 kern_return_t dyld_process_info_base::addImage(task_t task, bool sameCacheAsThisProcess, uint64_t imageAddress, uint64_t imagePath, const char* imagePathLocal)
 {
+    kern_return_t kr = KERN_SUCCESS;
     _curImage->loadAddress = imageAddress;
     _curImage->segmentStartIndex = _curSegmentIndex;
     if ( imagePathLocal != NULL ) {
@@ -393,25 +524,17 @@ kern_return_t dyld_process_info_base::addImage(task_t task, bool sameCacheAsThis
         _curImage->path = (const char*)imagePath;
     }
     else {
-        kern_return_t kr;
         _curImage->path = copyPath(task, imagePath, &kr);
-        if ( kr )
+        if ( kr != KERN_SUCCESS)
             return kr;
     }
     if ( sameCacheAsThisProcess && inCache(imageAddress) ) {
         addInfoFromLoadCommands((mach_header*)imageAddress, imageAddress, 32*1024);
     }
     else {
-        __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, size_t size) {
-                addInfoFromLoadCommands((mach_header*)buffer, imageAddress, size);
-            });
-        });
-        if (kr != KERN_SUCCESS) {
+        kr = addInfoFromRemoteLoadCommands(task, imageAddress);
+        if ( kr != KERN_SUCCESS)
             return kr;
-        }
     }
     _curImage->segmentsCount = _curSegmentIndex - _curImage->segmentStartIndex;
     _curImage++;
@@ -419,6 +542,34 @@ kern_return_t dyld_process_info_base::addImage(task_t task, bool sameCacheAsThis
 }
 
 
+kern_return_t dyld_process_info_base::addInfoFromRemoteLoadCommands(task_t task, uint64_t remoteMH) {
+    __block kern_return_t kr = KERN_SUCCESS;
+    __block size_t headerPagesSize = 0;
+    __block bool done = false;
+
+    //Since the minimum we can reasonably map is a page, map that.
+    withRemoteBuffer(task, remoteMH, PAGE_SIZE, false, false, &kr, ^(void * buffer, size_t size) {
+        const mach_header* mh = (const mach_header*)buffer;
+        headerPagesSize = sizeof(mach_header) + mh->sizeofcmds;
+        if (headerPagesSize <= PAGE_SIZE) {
+            addInfoFromLoadCommands(mh, remoteMH, size);
+            done = true;
+        }
+    });
+
+    //The load commands did not fit in the first page, but now we know the size, so remap and try again
+    if (!done) {
+        if (kr != KERN_SUCCESS) {
+            return kr;
+        }
+        withRemoteBuffer(task, remoteMH, headerPagesSize, false, false, &kr, ^(void * buffer, size_t size) {
+            addInfoFromLoadCommands((mach_header*)buffer, remoteMH, size);
+        });
+    }
+
+    return kr;
+}
+
 kern_return_t dyld_process_info_base::addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath)
 {
     __block kern_return_t kr = KERN_SUCCESS;
@@ -429,19 +580,13 @@ kern_return_t dyld_process_info_base::addDyldImage(task_t task, uint64_t dyldAdd
     }
     else {
         _curImage->path = copyPath(task, dyldPathAddress, &kr);
-        if ( kr )
+        if ( kr != KERN_SUCCESS)
             return kr;
     }
 
-    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, size_t size) {
-            addInfoFromLoadCommands((mach_header*)buffer, dyldAddress, size);
-        });
-    });
-    if (kr != KERN_SUCCESS) {
+    kr = addInfoFromRemoteLoadCommands(task, dyldAddress);
+    if ( kr != KERN_SUCCESS)
         return kr;
-    }
 
     _curImage->segmentsCount = _curSegmentIndex - _curImage->segmentStartIndex;
     _curImage++;
@@ -514,14 +659,14 @@ void dyld_process_info_base::forEachSegment(uint64_t machHeaderAddress, void (^c
     for (const ImageInfo* p = _firstImage; p < _curImage; ++p) {
         if ( p->loadAddress == machHeaderAddress ) {
             uint64_t slide = 0;
-            for (int i=0; i < p->segmentsCount; ++i) {
+            for (uint32_t i=0; i < p->segmentsCount; ++i) {
                 const SegmentInfo* seg = &_firstSegment[p->segmentStartIndex+i];
                 if ( strcmp(seg->name, "__TEXT") == 0 ) {
                     slide = machHeaderAddress - seg->addr;
                     break;
                 }
             }
-            for (int i=0; i < p->segmentsCount; ++i) {
+            for (uint32_t i=0; i < p->segmentsCount; ++i) {
                 const SegmentInfo* seg = &_firstSegment[p->segmentStartIndex+i];
                 callback(seg->addr + slide, seg->size, seg->name);
             }
@@ -530,163 +675,47 @@ void dyld_process_info_base::forEachSegment(uint64_t machHeaderAddress, void (^c
     }
 }
 
-
-
-
-
-// Implementation that works with existing dyld data structures
-static dyld_process_info _dyld_process_info_create_inner(task_t task, uint64_t timestamp, kern_return_t* kr)
+dyld_process_info _dyld_process_info_create(task_t task, uint64_t timestamp, kern_return_t* kr)
 {
-    if ( kr != NULL )
-        *kr = KERN_SUCCESS;
+    __block dyld_process_info result = nullptr;
+    kern_return_t krSink = KERN_SUCCESS;
+    if (kr == nullptr) {
+        kr = &krSink;
+    }
+    *kr = KERN_SUCCESS;
 
     task_dyld_info_data_t task_dyld_info;
     mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
     if ( kern_return_t r = task_info(task, TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) ) {
-        if ( kr != NULL )
-            *kr = r;
-        return  NULL;
+        *kr = r;
+        return  nullptr;
     }
 
     //The kernel will return MACH_VM_MIN_ADDRESS for an executable that has not had dyld loaded
     if (task_dyld_info.all_image_info_addr == MACH_VM_MIN_ADDRESS)
-        return NULL;
+        return nullptr;
 
     if ( task_dyld_info.all_image_info_size > sizeof(dyld_all_image_infos_64) )
-        return NULL;
-
-    // read all_image_infos struct
-    dyld_all_image_infos_64 allImageInfo64;
-    mach_vm_size_t readSize = task_dyld_info.all_image_info_size;
-    if ( kern_return_t r = mach_vm_read_overwrite(task, task_dyld_info.all_image_info_addr, task_dyld_info.all_image_info_size, (vm_address_t)&allImageInfo64, &readSize) ) {
-        if ( kr != NULL )
-            *kr = r;
-        return  NULL;
-    }
-    if ( allImageInfo64.infoArrayCount == 0 ) {
-        // could be task was launch suspended or still launching, wait a moment to see
-        usleep(1000 * 50); // 50ms
-        if ( kern_return_t r = mach_vm_read_overwrite(task, task_dyld_info.all_image_info_addr, task_dyld_info.all_image_info_size, (vm_address_t)&allImageInfo64, &readSize) ) {
-            if ( kr != NULL )
-                *kr = r;
-            return  NULL;
-        }
-        // if infoArrayCount is still zero, then target was most likely launched suspended
-        if ( allImageInfo64.infoArrayCount == 0 )
-            return dyld_process_info_base::makeSuspended(task, kr);
-    }
-
-    // bail out of dyld is too old
-    if ( allImageInfo64.version < 15 ) {
-        if ( kr != NULL )
-            *kr = KERN_INVALID_HOST;
-        return NULL;
-    }
-
-    // normalize by expanding 32-bit all_image_infos into 64-bit one
-    uint32_t imageCount = allImageInfo64.infoArrayCount;
-    size_t imageArraySize = imageCount * sizeof(dyld_image_info_64);
-    if ( task_dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) {
-        const dyld_all_image_infos_32* allImageInfo32 = (dyld_all_image_infos_32*)&allImageInfo64;
-        dyld_all_image_infos_64 info64;
-        bzero(&info64, sizeof(info64));
-        info64.version                  = allImageInfo32->version;
-        info64.infoArrayCount           = allImageInfo32->infoArrayCount;
-        info64.infoArray                = allImageInfo32->infoArray;
-        info64.processDetachedFromSharedRegion = allImageInfo32->processDetachedFromSharedRegion;
-        info64.libSystemInitialized     = allImageInfo32->libSystemInitialized;
-        info64.dyldImageLoadAddress     = allImageInfo32->dyldImageLoadAddress;
-        info64.initialImageCount        = allImageInfo32->initialImageCount;
-        info64.uuidArrayCount           = allImageInfo32->uuidArrayCount;
-        info64.uuidArray                = allImageInfo32->uuidArray;
-        info64.dyldAllImageInfosAddress = allImageInfo32->dyldAllImageInfosAddress;
-        info64.sharedCacheSlide         = allImageInfo32->sharedCacheSlide;
-        info64.infoArrayChangeTimestamp = allImageInfo32->infoArrayChangeTimestamp;
-        info64.sharedCacheBaseAddress   = allImageInfo32->sharedCacheBaseAddress;
-        info64.dyldPath                 = allImageInfo32->dyldPath;
-        memcpy((void*)(info64.sharedCacheUUID), (void*)(allImageInfo32->sharedCacheUUID), 16);
-        allImageInfo64 = info64;
-        imageCount = allImageInfo64.infoArrayCount;
-        imageArraySize = imageCount * sizeof(dyld_image_info_32);
-    }
-
-    // don't do any (more) work if target process's dyld timestamp has not changed since previous query
-    if ( (timestamp != 0) && (timestamp == allImageInfo64.infoArrayChangeTimestamp) ) {
-        if ( kr != NULL )
-            *kr = KERN_SUCCESS;
-        return NULL;
-    }
-
-    // For the moment we are going to truncate any image list longer than 8192 because some programs do
-    // terrible things that corrupt their own image lists and we need to stop clients from crashing
-    // reading them. We can try to do something more advanced in the future. rdar://27446361
-    imageCount = MIN(imageCount, 8192);
-
-    // read image array
-    if ( allImageInfo64.infoArray == 0 ) {
-        // dyld is in middle of updating image list, try again
-        return NULL;
-    }
-    dyld_image_info_64 imageArray64[imageCount];
-    if ( kern_return_t r = mach_vm_read_overwrite(task, allImageInfo64.infoArray, imageArraySize, (vm_address_t)&imageArray64, &readSize) ) {
-        // if image array moved, try whole thing again
-        if ( kr != NULL ) {
-            *kr = r;
-        }
-        return  NULL;
-    }
-    // normalize by expanding 32-bit image_infos into 64-bit ones
-    if ( task_dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) {
-        const dyld_image_info_32* imageArray32 = (dyld_image_info_32*)&imageArray64;
-        dyld_image_info_64 tempArray[imageCount];
-        for (uint32_t i=0; i < imageCount; ++i) {
-            tempArray[i].imageLoadAddress = imageArray32[i].imageLoadAddress;
-            tempArray[i].imageFilePath    = imageArray32[i].imageFilePath;
-            tempArray[i].imageFileModDate = imageArray32[i].imageFileModDate;
-        }
-        memcpy(imageArray64, tempArray, sizeof(dyld_image_info_64)*imageCount);
-    }
-
-    // create object based on local copy of all image infos and image array
-    dyld_process_info result = dyld_process_info_base::make(task, allImageInfo64, imageArray64, kr);
-
-    // verify nothing has changed by re-reading all_image_infos struct and checking timestamp
-    if ( result != NULL ) {
-        dyld_all_image_infos_64 allImageInfo64again;
-        readSize = task_dyld_info.all_image_info_size;
-        if ( kern_return_t r = mach_vm_read_overwrite(task, task_dyld_info.all_image_info_addr, task_dyld_info.all_image_info_size, (vm_address_t)&allImageInfo64again, &readSize) ) {
-            if ( kr != NULL )
-                *kr = r;
-            free((void*)result);
-            return  NULL;
-        }
-        uint64_t doneTimeStamp = allImageInfo64again.infoArrayChangeTimestamp;
-        if ( task_dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) {
-            const dyld_all_image_infos_32* allImageInfo32 = (dyld_all_image_infos_32*)&allImageInfo64again;
-            doneTimeStamp = allImageInfo32->infoArrayChangeTimestamp;
+        return nullptr;
+
+    // We use a true shared memory buffer here, that way by making sure that libdyld in both processes
+    // reads and writes the the timestamp atomically we can make sure we get a coherent view of the
+    // remote process.
+    // That also means that we *MUST* directly read the memory, which is why we template the make() call
+    withRemoteBuffer(task, task_dyld_info.all_image_info_addr, task_dyld_info.all_image_info_size, true, false, kr, ^(void *buffer, size_t size) {
+        dyld_process_info_ptr base;
+        if (task_dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) {
+            const dyld_all_image_infos_32* info = (const dyld_all_image_infos_32*)buffer;
+            base = dyld_process_info_base::make<dyld_all_image_infos_32, dyld_image_info_32>(task, *info, timestamp, kr);
+        } else {
+            const dyld_all_image_infos_64* info = (const dyld_all_image_infos_64*)buffer;
+            base = dyld_process_info_base::make<dyld_all_image_infos_64, dyld_image_info_64>(task, *info, timestamp, kr);
         }
-        if ( allImageInfo64.infoArrayChangeTimestamp != doneTimeStamp ) {
-            // image list has changed since we started reading it
-            // throw out what we have and start over
-            free((void*)result);
-            result = nullptr;
+        if (base) {
+            result = base.release();
         }
-    }
-
-    return result;
-}
-
-
-dyld_process_info _dyld_process_info_create(task_t task, uint64_t timestamp, kern_return_t* kr)
-{
-    // Other process may be loading and unloading as we read its memory, which can cause a read failure
-    // <rdar://problem30067343&29567679> Retry if something fails
-    for (int i=0; i < 100; ++i) {
-        if ( dyld_process_info result = _dyld_process_info_create_inner( task,  timestamp, kr) )
-            return result;
-
-    }
-    return NULL;
+    });
+    return  result;
 }
 
 void _dyld_process_info_get_state(dyld_process_info info, dyld_process_state_info* stateInfo)
@@ -699,16 +728,14 @@ void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_inf
     *cacheInfo = *info->cacheInfo();
 }
 
-void _dyld_process_info_retain(dyld_process_info info)
+void _dyld_process_info_retain(dyld_process_info object)
 {
-    info->retainCount() += 1;
+    const_cast<dyld_process_info_base*>(object)->retain();
 }
 
-void _dyld_process_info_release(dyld_process_info info)
+void _dyld_process_info_release(dyld_process_info object)
 {
-    info->retainCount() -= 1;
-    if ( info->retainCount() == 0 )
-        free((void*)info);
+    const_cast<dyld_process_info_base*>(object)->release();
 }
 
 void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path))
index cd8c6078969da424c5bfb6d395197df3a31f4a2e..ca12c65bd0520047ddc2e3e0dfa79d68b994cd6c 100644 (file)
 #ifndef _DYLD_PROCESS_INFO_INTERNAL_H_
 #define _DYLD_PROCESS_INFO_INTERNAL_H_
 
+#define VIS_HIDDEN __attribute__((visibility("hidden")))
 
 #include <stdlib.h>
 #include <string.h>
 #include <limits.h>
 #include <stdio.h>
+#include <uuid/uuid.h>
 
+#include <array>
 
 struct dyld_all_image_infos_32 {
-    uint32_t        version;
-    uint32_t        infoArrayCount;
-    uint32_t        infoArray;
-    uint32_t        notification;           
-    bool            processDetachedFromSharedRegion;
-    bool            libSystemInitialized;
-    uint32_t        dyldImageLoadAddress;
-    uint32_t        jitInfo;
-    uint32_t        dyldVersion;
-    uint32_t        errorMessage;
-    uint32_t        terminationFlags;
-    uint32_t        coreSymbolicationShmPage;
-    uint32_t        systemOrderFlag;
-    uint32_t        uuidArrayCount;
-    uint32_t        uuidArray;
-    uint32_t        dyldAllImageInfosAddress;
-    uint32_t        initialImageCount;
-    uint32_t        errorKind;
-    uint32_t        errorClientOfDylibPath;
-    uint32_t        errorTargetDylibPath;
-    uint32_t        errorSymbol;
-    uint32_t        sharedCacheSlide;
-    uint8_t         sharedCacheUUID[16];
-    uint32_t        sharedCacheBaseAddress;
-    uint64_t        infoArrayChangeTimestamp;
-    uint32_t        dyldPath;
-    uint32_t        notifyMachPorts[8];
-    uint32_t        reserved[5];
-    uint32_t        compact_dyld_image_info_addr;
-    uint32_t        compact_dyld_image_info_size;
+    uint32_t                        version;
+    uint32_t                        infoArrayCount;
+    std::atomic<uint32_t>           infoArray;
+    uint32_t                        notification;
+    bool                            processDetachedFromSharedRegion;
+    bool                            libSystemInitialized;
+    uint32_t                        dyldImageLoadAddress;
+    uint32_t                        jitInfo;
+    uint32_t                        dyldVersion;
+    uint32_t                        errorMessage;
+    uint32_t                        terminationFlags;
+    uint32_t                        coreSymbolicationShmPage;
+    uint32_t                        systemOrderFlag;
+    uint32_t                        uuidArrayCount;
+    uint32_t                        uuidArray;
+    uint32_t                        dyldAllImageInfosAddress;
+    uint32_t                        initialImageCount;
+    uint32_t                        errorKind;
+    uint32_t                        errorClientOfDylibPath;
+    uint32_t                        errorTargetDylibPath;
+    uint32_t                        errorSymbol;
+    uint32_t                        sharedCacheSlide;
+    std::array<uint8_t, 16>         sharedCacheUUID;
+    uint32_t                        sharedCacheBaseAddress;
+    std::atomic<uint64_t>           infoArrayChangeTimestamp;
+    uint32_t                        dyldPath;
+    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 {
-    uint32_t        version;
-    uint32_t        infoArrayCount;
-    uint64_t        infoArray;
-    uint64_t        notification;           
-    bool            processDetachedFromSharedRegion;
-    bool            libSystemInitialized;
-    uint32_t        paddingToMakeTheSizeCorrectOn32bitAndDoesntAffect64b; // NOT PART OF DYLD_ALL_IMAGE_INFOS!
-    uint64_t        dyldImageLoadAddress;
-    uint64_t        jitInfo;
-    uint64_t        dyldVersion;
-    uint64_t        errorMessage;
-    uint64_t        terminationFlags;
-    uint64_t        coreSymbolicationShmPage;
-    uint64_t        systemOrderFlag;
-    uint64_t        uuidArrayCount;
-    uint64_t        uuidArray;
-    uint64_t        dyldAllImageInfosAddress;
-    uint64_t        initialImageCount;
-    uint64_t        errorKind;
-    uint64_t        errorClientOfDylibPath;
-    uint64_t        errorTargetDylibPath;
-    uint64_t        errorSymbol;
-    uint64_t        sharedCacheSlide;
-    uint8_t         sharedCacheUUID[16];
-    uint64_t        sharedCacheBaseAddress;
-    uint64_t        infoArrayChangeTimestamp;
-    uint64_t        dyldPath;
-    uint32_t        notifyMachPorts[8];
-    uint64_t        reserved[9];
-    uint64_t        compact_dyld_image_info_addr;
-    uint64_t        compact_dyld_image_info_size;
+    uint32_t                version;
+    uint32_t                infoArrayCount;
+    std::atomic<uint64_t>   infoArray;
+    uint64_t                notification;
+    bool                    processDetachedFromSharedRegion;
+    bool                    libSystemInitialized;
+    uint32_t                paddingToMakeTheSizeCorrectOn32bitAndDoesntAffect64b; // NOT PART OF DYLD_ALL_IMAGE_INFOS!
+    uint64_t                dyldImageLoadAddress;
+    uint64_t                jitInfo;
+    uint64_t                dyldVersion;
+    uint64_t                errorMessage;
+    uint64_t                terminationFlags;
+    uint64_t                coreSymbolicationShmPage;
+    uint64_t                systemOrderFlag;
+    uint64_t                uuidArrayCount;
+    uint64_t                uuidArray;
+    uint64_t                dyldAllImageInfosAddress;
+    uint64_t                initialImageCount;
+    uint64_t                errorKind;
+    uint64_t                errorClientOfDylibPath;
+    uint64_t                errorTargetDylibPath;
+    uint64_t                errorSymbol;
+    uint64_t                sharedCacheSlide;
+    std::array<uint8_t, 16> sharedCacheUUID;
+    uint64_t                sharedCacheBaseAddress;
+    std::atomic<uint64_t>   infoArrayChangeTimestamp;
+    uint64_t                dyldPath;
+    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 {
@@ -132,7 +135,46 @@ struct dyld_process_info_notify_header {
     uint64_t                    timestamp;
 };
 
+struct VIS_HIDDEN RemoteBuffer {
+    RemoteBuffer();
+    RemoteBuffer(task_t task, mach_vm_address_t remote_address, size_t remote_size, bool shared, bool allow_truncation);
+    ~RemoteBuffer();
+    RemoteBuffer& operator=(RemoteBuffer&& other) {
+        _localAddress = other._localAddress;
+        _size = other._size;
+        _kr = other._kr;
+        other._localAddress = 0;
+        other._size = 0;
+        other._kr = KERN_SUCCESS;
+        return *this;
+    }
+    RemoteBuffer(const RemoteBuffer &) = delete;
+    RemoteBuffer& operator=(const RemoteBuffer &) = delete;
+    void *getLocalAddress();
+    kern_return_t getKernelReturn();
+    size_t getSize();
+private:
+    bool map(task_t task, mach_vm_address_t remote_address, bool shared);
+    mach_vm_address_t _localAddress;
+    vm_size_t _size;
+    kern_return_t _kr;
+};
 
+// only called during libdyld set up
+void setNotifyMonitoringDyldMain(void (*func)()) VIS_HIDDEN;
+void setNotifyMonitoringDyld(void (*func)(bool unloading, unsigned imageCount,
+                                          const struct mach_header* loadAddresses[],
+                                          const char* imagePaths[])) VIS_HIDDEN;
+
+void withRemoteBuffer(task_t task, mach_vm_address_t remote_address, size_t remote_size, bool shared, bool allow_truncation, kern_return_t *kr, void (^block)(void *buffer, size_t size)) __attribute__((visibility("hidden")));
+
+template<typename T>
+VIS_HIDDEN void withRemoteObject(task_t task, mach_vm_address_t remote_address, bool shared, kern_return_t *kr, void (^block)(T t))
+{
+    withRemoteBuffer(task, remote_address, sizeof(T), shared, false, kr, ^(void *buffer, size_t size) {
+        block(*reinterpret_cast<T *>(buffer));
+    });
+}
 
 
 #endif // _DYLD_PROCESS_INFO_INTERNAL_H_
index ec9bce4d65dd8ae0550dbb5cc01b3f0ffaa912d1..571e67790010961a0f75c17e79b9527a28b4786d 100644 (file)
 #include <mach/shared_region.h>
 #include <mach/mach_vm.h>
 #include <libkern/OSAtomic.h>
+#include <execinfo.h>
+
 
 #include "dyld_process_info.h"
 #include "dyld_process_info_internal.h"
 #include "dyld_images.h"
 #include "dyld_priv.h"
 
-#include "LaunchCache.h"
 #include "Loading.h"
 #include "AllImages.h"
 
+extern "C" int _dyld_func_lookup(const char* name, void** address);
 
 typedef void (^Notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path);
 typedef void (^NotifyExit)();
 typedef void (^NotifyMain)();
 
-
 //
 // Object used for monitoring another processes dyld loads
 //
 struct __attribute__((visibility("hidden"))) dyld_process_info_notify_base
 {
-    static dyld_process_info_notify_base* make(task_t task, dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, kern_return_t* kr);
-                                                                               ~dyld_process_info_notify_base();
-
-    bool                incRetainCount() const;
-    bool                decRetainCount() const;
+                        dyld_process_info_notify_base(dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, task_t task, kern_return_t* kr);
+                        ~dyld_process_info_notify_base();
+    bool                enabled() const;
+    void                retain();
+    void                release();
 
        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); }
+    static void         operator delete(void* p) { 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();
-
-       mutable int32_t         _retainCount;
-    dispatch_queue_t    _queue;
-    Notify              _notify;
-    NotifyExit          _notifyExit;
-       mutable NotifyMain      _notifyMain;
-       task_t                          _targetTask;
-       dispatch_source_t       _machSource;
-    uint64_t            _portAddressInTarget;
-    mach_port_t         _sendPortInTarget;          // target is process being watched for image loading/unloading
-    mach_port_t         _receivePortInMonitor;      // monitor is process being notified of image loading/unloading
+    void                handleEvent();
+    void                teardown();
+    void                replyToMonitoredProcess(mach_msg_header_t& header);
+
+    RemoteBuffer                    _remoteAllImageInfoBuffer;
+    uint32_t*                       _notifyMachPorts;
+    uint32_t                        _notifySlot;
+    mutable std::atomic<int32_t>    _retainCount;
+    dispatch_queue_t                _queue;
+    Notify                          _notify;
+    NotifyExit                      _notifyExit;
+       mutable NotifyMain              _notifyMain;
+       task_t                          _targetTask;
+       dispatch_source_t               _machSource;
+    mach_port_t                     _sendPortInTarget;          // target is process being watched for image loading/unloading
+    mach_port_t                     _receivePortInMonitor;      // monitor is process being notified of image loading/unloading
+    std::atomic<bool>               _disabled;
 };
 
 
-dyld_process_info_notify_base::dyld_process_info_notify_base(dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, task_t task)
-    : _retainCount(1), _queue(queue), _notify(notify), _notifyExit(notifyExit), _notifyMain(NULL), _targetTask(task), _machSource(NULL), _portAddressInTarget(0), _sendPortInTarget(0), _receivePortInMonitor(0)
+dyld_process_info_notify_base::dyld_process_info_notify_base(dispatch_queue_t queue, Notify notify, NotifyExit notifyExit,
+                                                             task_t task, kern_return_t* kr) :
+        _notifyMachPorts(nullptr), _notifySlot(0), _retainCount(1), _queue(queue), _notify(notify), _notifyExit(notifyExit),
+        _notifyMain(nullptr), _targetTask(task), _machSource(nullptr), _sendPortInTarget(0), _receivePortInMonitor(0),
+        _disabled(false)
 {
+    assert(_disabled == false);
     dispatch_retain(_queue);
-}
-
-dyld_process_info_notify_base::~dyld_process_info_notify_base()
-{
-       if ( _machSource ) {
-        dispatch_source_cancel(_machSource);
-               dispatch_release(_machSource);
-               _machSource = NULL;
-       }
-       if ( _portAddressInTarget ) {
-               unpokeSendPortInTarget();
-               _portAddressInTarget = 0;
-       }
-       if ( _sendPortInTarget ) {
-               _sendPortInTarget = 0;
-       }
-    dispatch_release(_queue);
-       if ( _receivePortInMonitor != 0 ) {
-               mach_port_deallocate(mach_task_self(), _receivePortInMonitor);
-               _receivePortInMonitor = 0;
-       }
-}
-
-bool dyld_process_info_notify_base::incRetainCount() const
-{
-    int32_t newCount = OSAtomicIncrement32(&_retainCount);
-    return ( newCount == 1 );
-}
-
-bool dyld_process_info_notify_base::decRetainCount() const
-{
-    int32_t newCount = OSAtomicDecrement32(&_retainCount);
-    return ( newCount == 0 );
-}
+    // Allocate a port to listen on in this monitoring task
+    mach_port_options_t options = { .flags = MPO_IMPORTANCE_RECEIVER | MPO_CONTEXT_AS_GUARD | MPO_STRICT,
+        .mpl = { MACH_PORT_QLIMIT_DEFAULT }};
+    if ((*kr = mach_port_construct(mach_task_self(), &options, (mach_port_context_t)this, &_receivePortInMonitor))) {
+        teardown();
+        return;
+    }
+    if (_targetTask == mach_task_self()) {
+        _sendPortInTarget = _receivePortInMonitor;
+        (void)mach_port_insert_right(_targetTask, _sendPortInTarget, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND);
+    } else {
+        // Insert a deadname right into the port to trigger notifications
+        kern_return_t r = KERN_NAME_EXISTS;
+        while (r == KERN_NAME_EXISTS) {
+            _sendPortInTarget = MACH_PORT_NULL;
+            //FIXME file radar
+            r = mach_port_allocate(_targetTask, MACH_PORT_RIGHT_DEAD_NAME, &_sendPortInTarget);
+            if (r != KERN_SUCCESS) {
+                *kr = r;
+                return;
+            }
+            (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+            r = mach_port_insert_right(_targetTask, _sendPortInTarget, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND);
+        }
+        if (r != KERN_SUCCESS) {
+            *kr = r;
+            return;
+        }
 
+        // Notify us if the target dies
+        mach_port_t previous = MACH_PORT_NULL;
+        if ((*kr = mach_port_request_notification(_targetTask, _sendPortInTarget, MACH_NOTIFY_DEAD_NAME, 0, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous))) {
+            (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+            (void)mach_port_destruct(mach_task_self(), _receivePortInMonitor, 0, (mach_port_context_t)this);
+            teardown();
+            return;
+        }
+        // This is a new port, if there is a previous notifier attached then something is wrong... abort.
+        if (previous != MACH_PORT_NULL) {
+            (void)mach_port_deallocate(mach_task_self(), previous);
+            (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+            (void)mach_port_destruct(mach_task_self(), _receivePortInMonitor, 0, (mach_port_context_t)this);
+            teardown();
+            return;
+        }
+    }
 
-dyld_process_info_notify_base* dyld_process_info_notify_base::make(task_t task, dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, kern_return_t* kr)
-{
-    dyld_process_info_notify_base* obj = new dyld_process_info_notify_base(queue, notify, notifyExit, task);
+    // Setup the event handler for the port
+    _machSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, _receivePortInMonitor, 0, _queue);
+    if (_machSource == nullptr) {
+        (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+        (void)mach_port_destruct(mach_task_self(), _receivePortInMonitor, 0, (mach_port_context_t)this);
+        teardown();
+        return;
+    }
+    dispatch_source_set_event_handler(_machSource, ^{
+        handleEvent();
+    });
+    dispatch_source_set_cancel_handler(_machSource, ^{
+        if ( _receivePortInMonitor != 0 ) {
+            (void)mach_port_destruct(mach_task_self(), _receivePortInMonitor, 0, (mach_port_context_t)this);
+            _receivePortInMonitor = 0;
+        }
+    });
+    dispatch_activate(_machSource);
 
-    if ( kern_return_t r = obj->makePorts() ) {
-               if ( kr != NULL )
-                       *kr = r;
-        goto fail;
-       }
+    // get location on all_image_infos in the target task
+    task_dyld_info_data_t taskDyldInfo;
+    mach_msg_type_number_t taskDyldInfoCount = TASK_DYLD_INFO_COUNT;
+    if ((*kr = task_info(_targetTask, TASK_DYLD_INFO, (task_info_t)&taskDyldInfo, &taskDyldInfoCount))) {
+        (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+        teardown();
+        return;
+    }
+    // Poke the portname of our port into the target task
+    _remoteAllImageInfoBuffer = RemoteBuffer(_targetTask, taskDyldInfo.all_image_info_addr, taskDyldInfo.all_image_info_size, true, false);
+    *kr = _remoteAllImageInfoBuffer.getKernelReturn();
+    if (*kr) {
+        (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+        teardown();
+        return;
+    }
 
-    obj->setMachSourceOnQueue();
+    static_assert(sizeof(mach_port_t) == sizeof(uint32_t), "machport size not 32-bits");
+    if ( taskDyldInfo.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) {
+        _notifyMachPorts = (uint32_t *)((uint8_t *)_remoteAllImageInfoBuffer.getLocalAddress() + offsetof(dyld_all_image_infos_32,notifyMachPorts));
+    } else {
+        _notifyMachPorts = (uint32_t *)((uint8_t *)_remoteAllImageInfoBuffer.getLocalAddress() + offsetof(dyld_all_image_infos_64,notifyMachPorts));
+    }
 
-    if ( kern_return_t r = obj->pokeSendPortIntoTarget() ) {
-               if ( kr != NULL )
-                       *kr = r;
-        goto fail;
-       }
+#if 0
+    //If all the slots are filled we will sleep and retry a few times before giving up
+    for (uint32_t i=0; i<10; ++i) {
+        for (_notifySlot=0; _notifySlot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++_notifySlot) {
+            if (OSAtomicCompareAndSwap32(0, _sendPortInTarget, (volatile int32_t*)&_notifyMachPorts[_notifySlot])) {
+                break;
+            }
+        }
+        if (_notifySlot == DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT) {
+            // all the slots are filled, sleep and try again
+            usleep(1000 * 50); // 50ms
+        } else {
+            // if _notifySlot is set we are done
+            break;
+        }
+    }
+#else
+    for (_notifySlot=0; _notifySlot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++_notifySlot) {
+        if (OSAtomicCompareAndSwap32(0, _sendPortInTarget, (volatile int32_t*)&_notifyMachPorts[_notifySlot])) {
+            break;
+        }
+    }
+#endif
 
-       if ( kr != NULL )
-               *kr = KERN_SUCCESS;
-    return obj;
+    if (_notifySlot == DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT) {
+        (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+        teardown();
+        *kr = KERN_UREFS_OVERFLOW;
+        return;
+    }
 
-fail:
-    delete obj;
-    return NULL;
+    *kr = KERN_SUCCESS;
 }
 
-
-kern_return_t dyld_process_info_notify_base::makePorts()
-{
-    // Allocate a port to listen on in this monitoring task
-    if ( kern_return_t r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &_receivePortInMonitor) )
-        return r;
-
-    // Add send rights for replying
-    if ( kern_return_t r = mach_port_insert_right(mach_task_self(), _receivePortInMonitor, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND) )
-        return r;
-
-    // Allocate a name in the target. We need a new name to add send rights to
-    if ( kern_return_t r = mach_port_allocate(_targetTask, MACH_PORT_RIGHT_DEAD_NAME, &_sendPortInTarget) )
-        return r;
-
-    // Deallocate the dead name
-    if ( kern_return_t r = mach_port_mod_refs(_targetTask, _sendPortInTarget, MACH_PORT_RIGHT_DEAD_NAME, -1) )
-        return r;
-
-    // Make the dead name a send right to our listening port
-    if ( kern_return_t r = mach_port_insert_right(_targetTask, _sendPortInTarget, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND) )
-        return r;
-
-    // Notify us if the target dies
-    mach_port_t previous = MACH_PORT_NULL;
-    if ( kern_return_t r = mach_port_request_notification(_targetTask, _sendPortInTarget, MACH_NOTIFY_DEAD_NAME, 0, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous))
-        return r;
-
-    //fprintf(stderr, "_sendPortInTarget=%d, _receivePortInMonitor=%d\n", _sendPortInTarget, _receivePortInMonitor);
-    return KERN_SUCCESS;
+dyld_process_info_notify_base::~dyld_process_info_notify_base() {
+    if (!_disabled) {
+        fprintf(stderr, "dyld: ~dyld_process_info_notify_base called while still enabled\n");
+    }
+    dispatch_release(_queue);
 }
 
-
-
-void dyld_process_info_notify_base::setMachSourceOnQueue()
-{
-       NotifyExit exitHandler = _notifyExit;
-       _machSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, _receivePortInMonitor, 0, _queue);
-    dispatch_source_set_event_handler(_machSource, ^{
-        // This event handler block has an implicit reference to "this"
-        // if incrementing the count goes to one, that means the object may have already been destroyed
-        if ( incRetainCount() )
-            return;
-        uint8_t messageBuffer[DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE];
-        mach_msg_header_t* h = (mach_msg_header_t*)messageBuffer;
-
-        kern_return_t r = mach_msg(h, MACH_RCV_MSG, 0, sizeof(messageBuffer), _receivePortInMonitor, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
-        if ( r == KERN_SUCCESS ) {
-            //fprintf(stderr, "received message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size);
-                       if ( h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_LOAD_ID || h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID ) {
-                               // run notifier block for each [un]load image
-                               const dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)messageBuffer;
-                               const dyld_process_info_image_entry* entries = (dyld_process_info_image_entry*)&messageBuffer[header->imagesOffset];
-                               const char* const stringPool = (char*)&messageBuffer[header->stringsOffset];
-                               for (unsigned i=0; i < header->imageCount; ++i) {
-                                       bool isUnload = (h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID);
-                                       _notify(isUnload, header->timestamp, entries[i].loadAddress, entries[i].uuid, stringPool + entries[i].pathStringOffset);
-                               }
-                               // reply to dyld, so it can continue
-                               mach_msg_header_t replyHeader;
-                               replyHeader.msgh_bits        = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND);
-                               replyHeader.msgh_id          = 0;
-                               replyHeader.msgh_local_port  = MACH_PORT_NULL;
-                               replyHeader.msgh_remote_port = h->msgh_remote_port;
-                               replyHeader.msgh_reserved    = 0;
-                               replyHeader.msgh_size        = sizeof(replyHeader);
-                               mach_msg(&replyHeader, MACH_SEND_MSG | MACH_SEND_TIMEOUT, replyHeader.msgh_size, 0, MACH_PORT_NULL, 100, MACH_PORT_NULL);
-                       }
-                       else if ( h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_MAIN_ID ) {
-                               if ( _notifyMain != NULL )  {
-                                       _notifyMain();
-                               }
-                               // reply to dyld, so it can continue
-                               mach_msg_header_t replyHeader;
-                               replyHeader.msgh_bits        = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND);
-                               replyHeader.msgh_id          = 0;
-                               replyHeader.msgh_local_port  = MACH_PORT_NULL;
-                               replyHeader.msgh_remote_port = h->msgh_remote_port;
-                               replyHeader.msgh_reserved    = 0;
-                               replyHeader.msgh_size        = sizeof(replyHeader);
-                               mach_msg(&replyHeader, MACH_SEND_MSG | MACH_SEND_TIMEOUT, replyHeader.msgh_size, 0, MACH_PORT_NULL, 100, MACH_PORT_NULL);
-                       }
-                       else if ( h->msgh_id == MACH_NOTIFY_PORT_DELETED ) {
-                               mach_port_t deadPort = ((mach_port_deleted_notification_t *)h)->not_port;
-                               //fprintf(stderr, "received message id=MACH_NOTIFY_PORT_DELETED, size=%d, deadPort=%d\n", h->msgh_size, deadPort);
-                               if ( deadPort == _sendPortInTarget ) {
-                                       // target process died.  Clean up ports
-                                       _sendPortInTarget = 0;
-                                       mach_port_deallocate(mach_task_self(), _receivePortInMonitor);
-                                       _receivePortInMonitor = 0;
-                                       _portAddressInTarget = 0;
-                                       // notify that target is gone
-                                       exitHandler();
-                               }
-                       }
-                       else {
-                               fprintf(stderr, "received unknown message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size);
-                       }
+void dyld_process_info_notify_base::teardown() {
+    if (!_disabled) {
+        _disabled = true;
+        // The connection to the target is dead.  Clean up ports
+        if ( _remoteAllImageInfoBuffer.getLocalAddress() != 0 && _notifySlot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT) {
+            mach_port_t extractedPort = MACH_PORT_NULL;
+            mach_msg_type_name_t extractedPortType;
+            kern_return_t kr = mach_port_extract_right(_targetTask, _sendPortInTarget, MACH_MSG_TYPE_COPY_SEND, &extractedPort, &extractedPortType);
+            if (kr == KERN_SUCCESS) {
+                if (extractedPort == _receivePortInMonitor) {
+                    if (OSAtomicCompareAndSwap32(_sendPortInTarget, 0, (volatile int32_t*)&_notifyMachPorts[_notifySlot])) {
+                        (void)mach_port_deallocate(_targetTask, _sendPortInTarget);
+                    }
+                }
+                (void)mach_port_deallocate(mach_task_self(), extractedPort);
+            }
         }
-        if ( decRetainCount() )
-            delete this;
-   });
-    dispatch_resume(_machSource);
+        _sendPortInTarget = 0;
+        if ( _machSource ) {
+            dispatch_source_cancel(_machSource);
+            dispatch_release(_machSource);
+            _machSource = NULL;
+        }
+        if (_notifyExit) {
+            dispatch_async(_queue, ^{
+                _notifyExit();
+            });
+        }
+    }
 }
 
-
-kern_return_t dyld_process_info_notify_base::pokeSendPortIntoTarget()
+bool dyld_process_info_notify_base::enabled() const
 {
-    // get location on all_image_infos in target task
-    task_dyld_info_data_t taskDyldInfo;
-    mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
-    kern_return_t r = task_info(_targetTask, TASK_DYLD_INFO, (task_info_t)&taskDyldInfo, &count);
-    if ( r )
-        return  r;
-
-    // remap the page containing all_image_infos into this process r/w
-    mach_vm_address_t mappedAddress = 0;
-    mach_vm_size_t    mappedSize = taskDyldInfo.all_image_info_size;
-    vm_prot_t curProt = VM_PROT_NONE;
-    vm_prot_t maxProt = VM_PROT_NONE;
-    r = mach_vm_remap(mach_task_self(), &mappedAddress, mappedSize, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR,
-                                       _targetTask, taskDyldInfo.all_image_info_addr, false, &curProt, &maxProt, VM_INHERIT_NONE);
-    if ( r )
-        return r;
-    if ( curProt != (VM_PROT_READ|VM_PROT_WRITE) )
-        return KERN_PROTECTION_FAILURE;
-
-    // atomically set port into all_image_info_struct
-    static_assert(sizeof(mach_port_t) == sizeof(uint32_t), "machport size not 32-bits");
-
-    mach_vm_address_t mappedAddressToPokePort = 0;
-    if ( taskDyldInfo.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 )
-        mappedAddressToPokePort = mappedAddress + offsetof(dyld_all_image_infos_32,notifyMachPorts);
-    else
-        mappedAddressToPokePort = mappedAddress + offsetof(dyld_all_image_infos_64,notifyMachPorts);
-
-    // use first available slot
-       bool slotFound = false;
-       for (int slotIndex=0; slotIndex < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slotIndex) {
-               if ( OSAtomicCompareAndSwap32Barrier(0, _sendPortInTarget, (volatile int32_t*)mappedAddressToPokePort) ) {
-                       slotFound = true;
-                       break;
-               }
-               mappedAddressToPokePort += sizeof(uint32_t);
-     }
-       if ( !slotFound ) {
-               mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize);
-               return KERN_UREFS_OVERFLOW;
-       }
-    _portAddressInTarget = taskDyldInfo.all_image_info_addr + mappedAddressToPokePort - mappedAddress;
-    //fprintf(stderr, "poked port %d into target at address 0x%llX\n", _sendPortInTarget, _portAddressInTarget);
-    mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize);
-    return r;
+    return !_disabled;
 }
 
+void dyld_process_info_notify_base::retain()
+{
+    _retainCount++;
+}
 
-
-kern_return_t dyld_process_info_notify_base::unpokeSendPortInTarget()
+void dyld_process_info_notify_base::release()
 {
-    // remap the page containing all_image_infos into this process r/w
-    mach_vm_address_t mappedAddress = 0;
-    mach_vm_size_t    mappedSize = sizeof(mach_port_t);
-    vm_prot_t curProt = VM_PROT_NONE;
-    vm_prot_t maxProt = VM_PROT_NONE;
-    kern_return_t r = mach_vm_remap(mach_task_self(), &mappedAddress, mappedSize, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR,
-                                                                       _targetTask, _portAddressInTarget, false, &curProt, &maxProt, VM_INHERIT_NONE);
-    if ( r )
-        return r;
-    if ( curProt != (VM_PROT_READ|VM_PROT_WRITE) )
-        return KERN_PROTECTION_FAILURE;
-
-    OSAtomicCompareAndSwap32Barrier(_sendPortInTarget, 0, (volatile int32_t*)mappedAddress);
-
-    //fprintf(stderr, "cleared port %d from target\n", _sendPortInTarget);
-    mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize);
-    return r;
+    uint32_t newCount = --_retainCount;
+    
+    if ( newCount == 0 ) {
+        teardown();
+    }
+    dispatch_async(_queue, ^{
+        delete this;
+    });
 }
 
+void dyld_process_info_notify_base::replyToMonitoredProcess(mach_msg_header_t& header) {
+    mach_msg_header_t replyHeader;
+    replyHeader.msgh_bits        = MACH_MSGH_BITS_SET(MACH_MSGH_BITS_REMOTE(header.msgh_bits), 0, 0, 0);
+    replyHeader.msgh_id          = 0;
+    replyHeader.msgh_local_port  = MACH_PORT_NULL;
+    replyHeader.msgh_remote_port  = header.msgh_remote_port;
+    replyHeader.msgh_reserved    = 0;
+    replyHeader.msgh_size        = sizeof(replyHeader);
+    kern_return_t r = mach_msg(&replyHeader, MACH_SEND_MSG, replyHeader.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
+    if (r == KERN_SUCCESS) {
+        header.msgh_remote_port = MACH_PORT_NULL;
+    } else {
+        teardown();
+    }
+}
 
+void dyld_process_info_notify_base::handleEvent() {
+    // References object may still exist even after the ports are dead. Disable event dispatching
+    // if the ports have been torn down.
+    if (_disabled) {
+        return;
+    }
+    // This event handler block has an implicit reference to "this"
+    // if incrementing the count goes to one, that means the object may have already been destroyed
+    uint8_t messageBuffer[DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE] = {};
+    mach_msg_header_t* h = (mach_msg_header_t*)messageBuffer;
+
+    kern_return_t r = mach_msg(h, MACH_RCV_MSG | MACH_RCV_VOUCHER| MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0), 0, sizeof(messageBuffer)-sizeof(mach_msg_audit_trailer_t), _receivePortInMonitor, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+    if ( r == KERN_SUCCESS && !(h->msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
+        //fprintf(stderr, "received message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size);
+
+        if ( h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_LOAD_ID || h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID ) {
+            // run notifier block for each [un]load image
+            const dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)messageBuffer;
+            if (sizeof(*header) <= h->msgh_size
+                && header->imagesOffset <= h->msgh_size
+                && header->stringsOffset <= h->msgh_size
+                && (header->imageCount * sizeof(dyld_process_info_image_entry)) <= (h->msgh_size - header->imagesOffset)) {
+                const dyld_process_info_image_entry* entries = (dyld_process_info_image_entry*)&messageBuffer[header->imagesOffset];
+                const char* const stringPool = (char*)&messageBuffer[header->stringsOffset];
+                for (unsigned i=0; i < header->imageCount; ++i) {
+                    bool isUnload = (h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID);
+                    if (entries[i].pathStringOffset <= h->msgh_size - header->stringsOffset) {
+                        //fprintf(stderr, "Notifying about: %s\n", stringPool + entries[i].pathStringOffset);
+                        _notify(isUnload, header->timestamp, entries[i].loadAddress, entries[i].uuid, stringPool + entries[i].pathStringOffset);
+                    } else {
+                        teardown();
+                        break;
+                    }
+                }
+                // reply to dyld, so it can continue
+                replyToMonitoredProcess(*h);
+            } else {
+                teardown();
+            }
+        }
+        else if ( h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_MAIN_ID ) {
+            if (h->msgh_size != sizeof(mach_msg_header_t)) {
+                teardown();
+            } else if ( _notifyMain != NULL )  {
+                _notifyMain();
+            }
+            replyToMonitoredProcess(*h);
+        } else if ( h->msgh_id == MACH_NOTIFY_PORT_DELETED ) {
+            mach_port_t deadPort = ((mach_port_deleted_notification_t *)h)->not_port;
+            // Validate this notification came from the kernel
+            const mach_msg_audit_trailer_t *audit_tlr = (mach_msg_audit_trailer_t *)((uint8_t *)h + round_msg(h->msgh_size));
+            if (audit_tlr->msgh_trailer_type == MACH_MSG_TRAILER_FORMAT_0
+                && audit_tlr->msgh_trailer_size >= sizeof(mach_msg_audit_trailer_t)
+                // We cannot link to libbsm, so we are hardcoding the audit token offset (5)
+                // And the value the represents the kernel (0)
+                && audit_tlr->msgh_audit.val[5] == 0
+                && deadPort == _sendPortInTarget ) {
+                teardown();
+            }
+        }
+        else {
+            fprintf(stderr, "dyld: received unknown message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size);
+        }
+    }
+    mach_msg_destroy(h);
+}
 
 dyld_process_info_notify _dyld_process_info_notify(task_t task, dispatch_queue_t queue,
                                                    void (^notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path),
                                                    void (^notifyExit)(),
                                                    kern_return_t* kr)
 {
-    return dyld_process_info_notify_base::make(task, queue, notify, notifyExit, kr);
+    kern_return_t krSink = KERN_SUCCESS;
+    if (kr == nullptr) {
+        kr = &krSink;
+    }
+    *kr = KERN_SUCCESS;
+    
+    dyld_process_info_notify result = new dyld_process_info_notify_base(queue, notify, notifyExit, task, kr);
+    if (result->enabled())
+        return result;
+    const_cast<dyld_process_info_notify_base*>(result)->release();
+    return nullptr;
 }
 
 void _dyld_process_info_notify_main(dyld_process_info_notify object, void (^notifyMain)())
@@ -344,181 +384,60 @@ void _dyld_process_info_notify_main(dyld_process_info_notify object, void (^noti
 
 void _dyld_process_info_notify_retain(dyld_process_info_notify object)
 {
-    object->incRetainCount();
+    const_cast<dyld_process_info_notify_base*>(object)->retain();
 }
 
 void _dyld_process_info_notify_release(dyld_process_info_notify object)
 {
-    // Note if _machSource is currently handling a message, the retain count will not be zero
-    // and object will instead be deleted when handling is done.
-    if ( object->decRetainCount() )
-        delete object;
+    const_cast<dyld_process_info_notify_base*>(object)->release();
 }
 
+static void (*sNotifyMonitoringDyldMain)() = nullptr;
+static void (*sNotifyMonitoringDyld)(bool unloading, unsigned imageCount, const struct mach_header* loadAddresses[],
+                                     const char* imagePaths[]) = nullptr;
 
-
-
-
-
-
-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)
+void setNotifyMonitoringDyldMain(void (*func)())
 {
-    if ( sZombieNotifiers[portSlot] )
-        return;
+    sNotifyMonitoringDyldMain = func;
+}
 
-    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 setNotifyMonitoringDyld(void (*func)(bool unloading, unsigned imageCount,
+                                          const struct mach_header* loadAddresses[],
+                                          const char* imagePaths[]))
+{
+    sNotifyMonitoringDyld = func;
 }
 
+namespace dyld3 {
+
 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;
-            }
-        }
-    }
+    assert(sNotifyMonitoringDyldMain != nullptr);
+    sNotifyMonitoringDyldMain();
 }
 
-void AllImages::notifyMonitorLoads(const launch_cache::DynArray<loader::ImageInfo>& newImages)
+void AllImages::notifyMonitorLoads(const Array<LoadedImage>& 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;
-        }
+    assert(sNotifyMonitoringDyld != nullptr);
+    const struct mach_header* loadAddresses[newImages.count()];
+    const char* loadPaths[newImages.count()];
+    for(uint32_t i = 0; i<newImages.count(); ++i) {
+        loadAddresses[i] = newImages[i].loadedAddress();
+        loadPaths[i] = newImages[i].image()->path();
     }
+    sNotifyMonitoringDyld(false, (unsigned)newImages.count(), loadAddresses, loadPaths);
 }
 
-void AllImages::notifyMonitorUnloads(const launch_cache::DynArray<loader::ImageInfo>& unloadingImages)
+void AllImages::notifyMonitorUnloads(const Array<LoadedImage>& 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;
-        }
+    assert(sNotifyMonitoringDyld != nullptr);
+    const struct mach_header* loadAddresses[unloadingImages.count()];
+    const char* loadPaths[unloadingImages.count()];
+    for(uint32_t i = 0; i<unloadingImages.count(); ++i) {
+        loadAddresses[i] = unloadingImages[i].loadedAddress();
+        loadPaths[i] = unloadingImages[i].image()->path();
     }
+    sNotifyMonitoringDyld(true, (unsigned)unloadingImages.count(), loadAddresses, loadPaths);
 }
 
 } // namespace dyld3
diff --git a/src/dyld_usage.cpp b/src/dyld_usage.cpp
new file mode 100644 (file)
index 0000000..e8d0194
--- /dev/null
@@ -0,0 +1,838 @@
+/*
+ * Copyright (c) 2017 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
+ * Reserved.  This file contains Original Code and/or Modifications of
+ * Original Code as defined in and that are subject to the Apple Public
+ * Source License Version 1.0 (the 'License').  You may not use this file
+ * except in compliance with the License.  Please obtain a copy of the
+ * License at http://www.apple.com/publicsource and read it before using
+ * this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
+ * License for the specific language governing rights and limitations
+ * under the License."
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <map>
+#include <set>
+#include <memory>
+#include <string>
+#include <cstring>
+#include <array>
+#include <vector>
+#include <iomanip>
+#include <sstream>
+#include <iostream>
+#include <cinttypes>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <libutil.h>
+#include <ktrace/session.h>
+#include <dispatch/dispatch.h>
+#include <System/sys/kdebug.h>
+
+#include "Tracing.h"
+
+#define DBG_FUNC_ALL    (DBG_FUNC_START | DBG_FUNC_END)
+
+
+/*
+ * MAXCOLS controls when extra data kicks in.
+ * MAX_WIDE_MODE_COLS controls -w mode to get even wider data in path.
+ */
+#define MAXCOLS (132)
+#define MAXWIDTH (MAXCOLS + 64)
+
+unsigned int columns = 0;
+ktrace_session_t s;
+bool wideflag = false;
+bool RAW_flag = false;
+bool JSON_flag = false;
+bool JSON_Tracing_flag = false;
+dispatch_source_t sigwinch_source;
+static void
+exit_usage(void)
+{
+    fprintf(stderr, "Usage: dyld_usage [-e] [-f mode] [-t seconds] [-R rawfile [-S start_time] [-E end_time]] [pid | cmd [pid | cmd] ...]\n");
+    fprintf(stderr, "  -e    exclude the specified list of pids from the sample\n");
+    fprintf(stderr, "        and exclude dyld_usage by default\n");
+    fprintf(stderr, "  -t    specifies timeout in seconds (for use in automated tools)\n");
+    fprintf(stderr, "  -R    specifies a raw trace file to process\n");
+    fprintf(stderr, "  pid   selects process(s) to sample\n");
+    fprintf(stderr, "  cmd   selects process(s) matching command string to sample\n");
+    fprintf(stderr, "By default (no options) the following processes are excluded from the output:\n");
+    fprintf(stderr, "dyld_usage, Terminal, telnetd, sshd, rlogind, tcsh, csh, sh\n\n");
+
+    exit(1);
+}
+
+static void
+get_screenwidth(void)
+{
+    struct winsize size;
+
+    columns = MAXCOLS;
+
+    if (isatty(STDOUT_FILENO)) {
+        if (ioctl(1, TIOCGWINSZ, &size) != -1) {
+            columns = size.ws_col;
+
+            if (columns > MAXWIDTH)
+                columns = MAXWIDTH;
+        }
+    }
+}
+
+std::map<uint64_t, uint64_t> gActiveStringIDs;
+std::map<uint64_t, std::string> gActiveStrings;
+
+const std::string& stringForID(uint64_t id) {
+    static std::string emptyString = "";
+    auto i = gActiveStrings.find(id);
+    if (i == gActiveStrings.end())
+        return emptyString;
+    return i->second;
+}
+
+static uint64_t
+mach_to_nano(uint64_t mach)
+{
+    uint64_t nanoseconds = 0;
+    assert(ktrace_convert_timestamp_to_nanoseconds(s, mach, &nanoseconds) == 0);
+    return nanoseconds;
+}
+
+struct output_renderer {
+    output_renderer(ktrace_session_t S, ktrace_event_t E) :
+    _commandName(ktrace_get_execname_for_thread(s, E->threadid)),
+    _threadid(E->threadid), _pid(ktrace_get_pid_for_thread(s, E->threadid)) {}
+    void recordEvent(ktrace_event_t event) {
+        uint32_t code = event->debugid & KDBG_EVENTID_MASK;
+        if (event->debugid & DBG_FUNC_START) {
+            switch(code) {
+                case DBG_DYLD_TIMING_DLOPEN: enqueueEvent<dlopen>(event, true); break;
+                case DBG_DYLD_TIMING_LAUNCH_EXECUTABLE: enqueueEvent<app_launch>(event, true); break;
+                case DBG_DYLD_TIMING_DLSYM: enqueueEvent<dlsym>(event, true); break;
+                case DBG_DYLD_TIMING_STATIC_INITIALIZER: enqueueEvent<static_init>(event, false); break;
+                case DBG_DYLD_TIMING_MAP_IMAGE: enqueueEvent<map_image>(event, false); break;
+                case DBG_DYLD_TIMING_APPLY_FIXUPS: enqueueEvent<apply_fixups>(event, false); break;
+                case DBG_DYLD_TIMING_ATTACH_CODESIGNATURE: enqueueEvent<attach_signature>(event, false); break;
+                case DBG_DYLD_TIMING_BUILD_CLOSURE: enqueueEvent<build_closure>(event, false); break;
+                case DBG_DYLD_TIMING_DLADDR: enqueueEvent<dladdr>(event, true); break;
+                case DBG_DYLD_TIMING_DLCLOSE: enqueueEvent<dlclose>(event, true); break;
+                case DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE: enqueueEvent<add_image_callback>(event, false); break;
+                case DBG_DYLD_TIMING_FUNC_FOR_REMOVE_IMAGE: enqueueEvent<remove_image_callback>(event, false); break;
+                case DBG_DYLD_TIMING_OBJC_INIT: enqueueEvent<objc_image_init>(event, false); break;
+                case DBG_DYLD_TIMING_OBJC_MAP: enqueueEvent<objc_images_map>(event, false); break;
+            }
+        } else {
+            switch(code) {
+                case DBG_DYLD_TIMING_DLOPEN: dequeueEvent<dlopen>(event, [&](dlopen* endEvent){
+                    endEvent->result = event->arg2;
+                }); break;
+                case DBG_DYLD_TIMING_LAUNCH_EXECUTABLE: dequeueEvent<app_launch>(event, [&](app_launch* endEvent){
+                    endEvent->launchMode = event->arg4;
+                }); break;
+                case DBG_DYLD_TIMING_DLSYM: dequeueEvent<dlsym>(event, [&](dlsym* endEvent){
+                    endEvent->result = event->arg2;
+                }); break;
+                case DBG_DYLD_TIMING_STATIC_INITIALIZER: dequeueEvent<static_init>(event, [&](static_init* endEvent){}); break;
+                case DBG_DYLD_TIMING_MAP_IMAGE: dequeueEvent<map_image>(event, [&](map_image* endEvent){
+                    endEvent->result = event->arg2;
+                }); break;
+                case DBG_DYLD_TIMING_APPLY_FIXUPS: dequeueEvent<apply_fixups>(event, [&](apply_fixups* endEvent){}); break;
+                case DBG_DYLD_TIMING_ATTACH_CODESIGNATURE: dequeueEvent<attach_signature>(event, [&](attach_signature* endEvent){}); break;
+                case DBG_DYLD_TIMING_BUILD_CLOSURE: dequeueEvent<build_closure>(event, [&](build_closure* endEvent){}); break;
+                case DBG_DYLD_TIMING_DLCLOSE: dequeueEvent<dlclose>(event, [&](dlclose* endEvent){
+                    endEvent->result = (int)event->arg2;
+                }); break;
+                case DBG_DYLD_TIMING_DLADDR: dequeueEvent<dladdr>(event, [&](dladdr* endEvent){
+                    endEvent->result = (int)event->arg2;
+                    endEvent->imageAddress = (int)event->arg3;
+                    endEvent->symbolAddress = (int)event->arg4;
+                }); break;
+                case DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE: dequeueEvent<add_image_callback>(event, [&](add_image_callback* endEvent){}); break;
+                case DBG_DYLD_TIMING_FUNC_FOR_REMOVE_IMAGE: dequeueEvent<remove_image_callback>(event, [&](remove_image_callback* endEvent){}); break;
+                case DBG_DYLD_TIMING_OBJC_INIT: dequeueEvent<objc_image_init>(event, [&](objc_image_init* endEvent){}); break;
+                case DBG_DYLD_TIMING_OBJC_MAP: dequeueEvent<objc_images_map>(event, [&](objc_images_map* endEvent){}); break;
+            }
+        }
+    }
+
+    bool empty() { return !_currentRootEvent && _eventStack.empty() && _rootEvents.empty(); }
+private:
+    template<typename T>
+    void enqueueEvent(ktrace_event_t event, bool rootEvent) {
+        auto sharedEvent = std::make_shared<T>(event);
+        if (!_currentRootEvent) {
+            if (!rootEvent) return;
+            assert(_eventStack.empty());
+            _currentRootEvent = sharedEvent;
+        } else {
+            sharedEvent->setDepth(_eventStack.size());
+            _eventStack.back()->addChild(sharedEvent);
+        }
+        _eventStack.push_back(sharedEvent);
+    }
+
+    template<typename T>
+    void dequeueEvent(ktrace_event_t event, std::function<void(T*)> lambda) {
+        if (_eventStack.empty()) return;
+        auto currentEvent = dynamic_cast<T*>(_eventStack.back().get());
+        currentEvent->setEndEvent(event);
+        lambda(currentEvent);
+        _eventStack.pop_back();
+        if (_currentRootEvent && _eventStack.empty()) {
+            output(_currentRootEvent);
+            _currentRootEvent = nullptr;
+        }
+    }
+
+    struct event_pair {
+        event_pair(ktrace_event_t E) : _startTime(E->timestamp), _walltime(E->walltime), _threadid(E->threadid), _depth(0),
+                                       _eventCode(KDBG_EXTRACT_CODE(E->debugid)) {};
+        virtual ~event_pair(){}
+        std::vector<std::shared_ptr<event_pair>>& children() { return _children; }
+        void setDepth(uint64_t D) { _depth = D; }
+        uint64_t depth() { return _depth; }
+        struct timeval walltime() { return _walltime; };
+        uint64_t startTimestamp() { return _startTime; };
+        uint64_t endTimestamp() { return _endTime; }
+        unsigned long threadid() { return _threadid; }
+        void addChild(std::shared_ptr<event_pair> child) {_children.push_back(child);}
+        void setEndEvent(ktrace_event_t E) { _endTime = E->timestamp; }
+        uint16_t eventCode() const { return _eventCode; }
+    private:
+        std::vector<std::shared_ptr<event_pair>> _children;
+        uint64_t _startTime;
+        uint64_t _endTime;
+        uint64_t _depth;
+        unsigned long _threadid;
+        uint16_t _eventCode;
+        struct timeval _walltime;
+    };
+
+    time_t _lastTimeWallSeconds = -1;
+    std::string _timestampStr;
+    std::string _commandName;
+    pid_t _pid;
+    unsigned long _threadid;
+
+    std::string timestamp(std::shared_ptr<event_pair> event, bool extended) {
+        std::ostringstream result;
+        struct timeval now_walltime = event->walltime();
+        assert(now_walltime.tv_sec || now_walltime.tv_usec);
+
+        /* try and reuse the timestamp string */
+        if (_lastTimeWallSeconds != now_walltime.tv_sec) {
+            char timestamp[32];
+            (void)strftime(timestamp, sizeof (timestamp), "%H:%M:%S", localtime(&now_walltime.tv_sec));
+            _lastTimeWallSeconds = now_walltime.tv_sec;
+            _timestampStr = timestamp;
+        }
+        result << _timestampStr;
+
+        if (extended) {
+            result << "." << std::setw(6) << std::setfill(' ') << std::to_string(now_walltime.tv_usec);
+        }
+        return result.str();
+    }
+
+    std::string process(std::shared_ptr<event_pair> event, bool extended) {
+        if (extended) {
+            std::ostringstream result;
+            result << _commandName << "." << std::to_string(event->threadid());
+            return result.str();
+        } else {
+            std::string result = _commandName;
+            result.resize(12, ' ');
+            return result;
+        }
+    }
+
+    std::string duration(std::shared_ptr<event_pair> event) {
+        std::ostringstream result;
+        uint64_t usecs = (mach_to_nano(event->endTimestamp() - event->startTimestamp()) + (NSEC_PER_USEC - 1)) / NSEC_PER_USEC;
+        uint64_t secs = usecs / USEC_PER_SEC;
+        usecs -= secs * USEC_PER_SEC;
+        result << secs << "." << std::setw(6) << std::setfill('0') << usecs;
+        return result.str();
+    }
+
+public:
+    void outputConsole(std::shared_ptr<event_pair> node, uint64_t width, std::ostringstream& sstr, uint64_t depth) {
+        std::ostringstream line;
+        bool extended = false;
+        if (auto dlopenNode = dynamic_cast<dlopen *>(node.get())) {
+            line << "dlopen(\"" << dlopenNode->path << "\", " << dlopenNode->flagString() << ") -> 0x" << dlopenNode->result;
+        } else if (auto dlsymNode = dynamic_cast<dlsym *>(node.get())) {
+            line << "dlsym(0x" << std::hex << dlsymNode->handle << ", \"" << dlsymNode->symbol << "\") -> " << dlsymNode->result;
+        } else if (auto mapImageNode = dynamic_cast<map_image *>(node.get())) {
+            line << "map file \"" << mapImageNode->path << "\" -> 0x" << std::hex << mapImageNode->result;
+        } else if (auto sigNode = dynamic_cast<attach_signature *>(node.get())) {
+            line << "attach codesignature";
+        } else if (auto buildClosureNode = dynamic_cast<build_closure *>(node.get())) {
+            line << "build closure";
+        } else if (auto launchNode = dynamic_cast<app_launch *>(node.get())) {
+            line << "app launch (dyld" << std::dec << launchNode->launchMode << ") -> 0x" << std::hex << launchNode->address;
+        } else if (auto initNode  = dynamic_cast<static_init *>(node.get())) {
+            line << "run static initializer 0x" << std::hex << initNode->funcAddress;
+        } else if (auto initNode  = dynamic_cast<apply_fixups *>(node.get())) {
+            line << "apply fixups";
+        } else if (auto dlcloseNode  = dynamic_cast<dlclose *>(node.get())) {
+            line << "dlclose(0x" << std::hex << dlcloseNode->handle << ") -> " << dlcloseNode->result;
+        } else if (auto dladdrNode  = dynamic_cast<dladdr *>(node.get())) {
+            line << "dladdr(0x" << dladdrNode->address << ") -> image: 0x" << std::hex << dladdrNode->imageAddress;
+            line << ", symbol: 0x" << dladdrNode->symbolAddress;
+        } else if (auto addImageNode  = dynamic_cast<add_image_callback *>(node.get())) {
+            line << std::hex << "add image callback(0x" << addImageNode->funcAddress << ") for image 0x" << addImageNode->libraryAddress;
+        } else if (auto removeImageNode  = dynamic_cast<remove_image_callback *>(node.get())) {
+            line << std::hex << "remove image callback(0x" << removeImageNode->funcAddress << ") for image 0x" << removeImageNode->libraryAddress;
+        } else if (auto objcInitNode  = dynamic_cast<objc_image_init *>(node.get())) {
+            line << std::hex << "objC init image(0x" << objcInitNode->libraryAddress << ")";
+        } else if (auto objcMapNode  = dynamic_cast<objc_images_map *>(node.get())) {
+            line << std::hex << "objC map images callback";
+        }
+
+        if (width > MAXCOLS) {
+            extended = true;
+        }
+
+        std::string timestampStr = timestamp(node, extended);
+        std::string lineStr = line.str();
+        std::string commandStr = process(node, extended);
+        std::string durationStr = duration(node);
+        uint64_t lineMax = width - (timestampStr.length() + commandStr.length() + durationStr.length() + 2*depth + 3);
+        lineStr.resize(lineMax, ' ');
+
+        sstr << timestampStr << " ";
+        std::fill_n(std::ostream_iterator<char>(sstr), 2*depth, ' ');
+        sstr << lineStr << " " << durationStr << " " << commandStr << std::endl;
+
+        for (const auto& child : node->children()) {
+            outputConsole(child, width, sstr, depth+1);
+        }
+    }
+
+    void outputJSON(std::shared_ptr<event_pair> node, std::ostringstream& sstr) {
+        if (auto dlopenNode = dynamic_cast<dlopen *>(node.get())) {
+            sstr << std::hex;
+            sstr << "{\"type\":\"dlopen\",\"path\":\"" << dlopenNode->path << "\",\"flags\":\"0x" << dlopenNode->flags << "\"";
+            sstr << ",\"result\":\"" << dlopenNode->result << "\"";
+        } else if (auto dlsymNode = dynamic_cast<dlsym *>(node.get())) {
+            sstr << std::hex  << "{\"type\":\"dlsym\",\"symbol\":\"" << dlsymNode->symbol << "\",\"handle\":\"0x";
+            sstr << dlsymNode->handle << "\",\"result\":\"0x" << dlsymNode->result << "\"";
+        } else if (auto mapImageNode = dynamic_cast<map_image *>(node.get())) {
+            sstr << std::hex;
+            sstr << "{\"type\":\"map_image\",\"path\":\"" << mapImageNode->path << "\",\"result\":\"0x" << mapImageNode->result << "\"";
+        } else if (auto sigNode = dynamic_cast<attach_signature *>(node.get())) {
+            sstr << "{\"type\":\"attach_codesignature\"";
+        } else if (auto buildClosureNode = dynamic_cast<build_closure *>(node.get())) {
+            sstr << "{\"type\":\"build_closure\"";
+        } else if (auto launchNode = dynamic_cast<app_launch *>(node.get())) {
+            sstr << std::hex;
+            sstr << "{\"type\":\"app_launch\",\"address\":\"0x";
+            sstr << launchNode->address << "\",\"mode\":" << launchNode->launchMode << "";
+        } else if (auto initNode  = dynamic_cast<static_init *>(node.get())) {
+            sstr << std::hex;
+            sstr << "{\"type\":\"static_init\",\"image_address\":\"0x"  << initNode->libraryAddress;
+            sstr << "\",\"function_address\":\"0x" << initNode->funcAddress << "\"";
+        } else if (auto initNode  = dynamic_cast<apply_fixups *>(node.get())) {
+            sstr << "{\"type\":\"apply_fixups\"";
+        } else if (auto dlcloseNode  = dynamic_cast<dlclose *>(node.get())) {
+            sstr << std::hex << "{\"type\":\"dlclose\",\"handle\":\"0x";
+            sstr << dlcloseNode->handle << "\",\"result\":\"0x" << dlcloseNode->result << "\"";
+        } else if (auto dladdrNode  = dynamic_cast<dladdr *>(node.get())) {
+            sstr << std::hex << "{\"type\":\"dladdr\",\"address\":\"0x" << dladdrNode->address << "\",\"result\":\"0x";
+            sstr << dladdrNode->result << "\",\"symbol_address\":\"0x" << dladdrNode->symbolAddress;
+            sstr << "\",\"image_address\":\"0x" << dladdrNode->imageAddress << "\"";
+        } else {
+            sstr << "{\"type\":\"unknown\"";
+        }
+
+        if (!node->children().empty()) {
+            bool firstChild = true;
+            sstr << ",\"children\":[";
+            for (const auto& child : node->children()) {
+                if (!firstChild) {
+                    sstr << ",";
+                }
+                firstChild = false;
+                outputJSON(child, sstr);
+            }
+            sstr << "]";
+        }
+        sstr << std::dec << ",\"start_nano\":\"" << mach_to_nano(node->startTimestamp());
+        sstr << "\",\"end_nano\":\"" << mach_to_nano(node->endTimestamp()) << "\"}";
+    }
+
+    void outputTracingJSON(std::shared_ptr<event_pair> node, std::ostringstream& sstr) {
+        auto emitEventInfo = [&](bool isStart) {
+            if (auto dlopenNode = dynamic_cast<dlopen *>(node.get())) {
+                sstr << "{\"name\": \"dlopen(" << dlopenNode->path << ")\", \"cat\": \"" << "dlopen" << "\"";
+            } else if (auto dlsymNode = dynamic_cast<dlsym *>(node.get())) {
+                sstr << "{\"name\": \"dlsym(" << dlsymNode->symbol << ")\", \"cat\": \"" << "dlsym" << "\"";
+            } else if (auto mapImageNode = dynamic_cast<map_image *>(node.get())) {
+                sstr << "{\"name\": \"map_image(" << mapImageNode->path << ")\", \"cat\": \"" << "map_image" << "\"";
+            } else if (auto sigNode = dynamic_cast<attach_signature *>(node.get())) {
+                sstr << "{\"name\": \"" << "attach_codesignature" << "\", \"cat\": \"" << "attach_codesignature" << "\"";
+            } else if (auto buildClosureNode = dynamic_cast<build_closure *>(node.get())) {
+                sstr << "{\"name\": \"" << "build_closure" << "\", \"cat\": \"" << "build_closure" << "\"";
+            } else if (auto launchNode = dynamic_cast<app_launch *>(node.get())) {
+                sstr << "{\"name\": \"" << "app_launch" << "\", \"cat\": \"" << "app_launch" << "\"";
+            } else if (auto initNode  = dynamic_cast<static_init *>(node.get())) {
+                sstr << "{\"name\": \"" << "static_init" << "\", \"cat\": \"" << "static_init" << "\"";
+            } else if (auto initNode  = dynamic_cast<apply_fixups *>(node.get())) {
+                sstr << "{\"name\": \"" << "apply_fixups" << "\", \"cat\": \"" << "apply_fixups" << "\"";
+            } else if (auto dlcloseNode  = dynamic_cast<dlclose *>(node.get())) {
+                sstr << "{\"name\": \"" << "dlclose" << "\", \"cat\": \"" << "dlclose" << "\"";
+            } else if (auto dladdrNode  = dynamic_cast<dladdr *>(node.get())) {
+                sstr << "{\"name\": \"" << "dladdr" << "\", \"cat\": \"" << "dladdr" << "\"";
+            } else if (auto addImageNode  = dynamic_cast<add_image_callback *>(node.get())) {
+                sstr << "{\"name\": \"" << "add_image" << "\", \"cat\": \"" << "add_image" << "\"";
+            } else if (auto removeImageNode  = dynamic_cast<remove_image_callback *>(node.get())) {
+                sstr << "{\"name\": \"" << "remove_image" << "\", \"cat\": \"" << "remove_image" << "\"";
+            } else if (auto objcInitNode  = dynamic_cast<objc_image_init *>(node.get())) {
+                sstr << "{\"name\": \"" << "objc_init" << "\", \"cat\": \"" << "objc_init" << "\"";
+            } else if (auto objcMapNode  = dynamic_cast<objc_images_map *>(node.get())) {
+                sstr << "{\"name\": \"" << "objc_map" << "\", \"cat\": \"" << "objc_map" << "\"";
+            } else {
+                sstr << "{\"name\": \"" << "unknown" << "\", \"cat\": \"" << node->eventCode() << "\"";
+            }
+            if (isStart) {
+                sstr << ", \"ph\": \"B\", \"pid\": " << _pid << ", \"tid\": " << _threadid << ", \"ts\": " << mach_to_nano(node->startTimestamp()) << "},";
+            } else {
+                sstr << ", \"ph\": \"E\", \"pid\": " << _pid << ", \"tid\": " << _threadid << ", \"ts\": " << mach_to_nano(node->endTimestamp()) << "}";
+            }
+        };
+
+        emitEventInfo(true);
+        emitEventInfo(false);
+
+        if (!node->children().empty()) {
+            for (const auto& child : node->children()) {
+                sstr << ", ";
+                outputTracingJSON(child, sstr);
+            }
+        }
+    }
+
+    const std::vector<std::shared_ptr<event_pair>>& rootEvents() const { return _rootEvents; }
+
+private:
+
+    void output(std::shared_ptr<event_pair> root) {
+        std::ostringstream ostream;
+        if (JSON_flag) {
+            ostream << "{\"command\":\"" << _commandName << "\",\"pid\":\"" << _pid << "\",\"thread\":\"";
+            ostream << _threadid << "\", \"event\":";
+            outputJSON(root, ostream);
+            ostream << "}" << std::endl;
+        } else if (JSON_Tracing_flag) {
+            _rootEvents.push_back(root);
+        } else {
+            outputConsole(root, columns, ostream, 0);
+        }
+        std::cout << ostream.str();
+        if (!RAW_flag)
+            fflush(stdout);
+    }
+
+    struct dlopen : event_pair {
+    dlopen(ktrace_event_t E) : event_pair(E), path(stringForID(E->arg2)), flags((int)E->arg3) {}
+        std::string flagString() {
+            std::vector<std::string> flagStrs;
+            uint64_t flagCheck = 0;
+            std::string flagString;
+
+            if (flags & RTLD_LAZY) {
+                flagStrs.push_back("RTLD_LAZY");
+                flagCheck |= RTLD_LAZY;
+            }
+            if (flags & RTLD_NOW) {
+                flagStrs.push_back("RTLD_NOW");
+                flagCheck |= RTLD_NOW;
+            }
+            if (flags & RTLD_LOCAL) {
+                flagStrs.push_back("RTLD_LOCAL");
+                flagCheck |= RTLD_LOCAL;
+            }
+            if (flags & RTLD_GLOBAL) {
+                flagStrs.push_back("RTLD_GLOBAL");
+                flagCheck |= RTLD_GLOBAL;
+            }
+            if (flags & RTLD_NOLOAD) {
+                flagStrs.push_back("RTLD_NOLOAD");
+                flagCheck |= RTLD_NOLOAD;
+            }
+            if (flags & RTLD_NODELETE) {
+                flagStrs.push_back("RTLD_NODELETE");
+                flagCheck |= RTLD_NODELETE;
+            }
+            if (flags & RTLD_FIRST) {
+                flagStrs.push_back("RTLD_FIRST");
+                flagCheck |= RTLD_FIRST;
+            }
+
+            if (flagCheck == flags) {
+                for (auto& flagStr : flagStrs) {
+                    if (!flagString.empty()) {
+                        flagString += "|";
+                    }
+                    flagString += flagStr;
+                }
+            }
+
+            return flagString;
+        }
+        std::string path;
+        int flags;
+        uint64_t result;
+    };
+
+    struct dlsym : event_pair {
+        dlsym(ktrace_event_t E) : event_pair(E), handle(E->arg2), symbol(stringForID(E->arg3)) {}
+        std::string symbol;
+        uint64_t handle;
+        uint64_t result;
+    };
+
+    struct dladdr : event_pair {
+        dladdr(ktrace_event_t E) : event_pair(E), address(E->arg2), imageAddress(0), symbolAddress(0)  {}
+        uint64_t address;
+        uint64_t imageAddress;
+        uint64_t symbolAddress;
+        int result;
+    };
+
+    struct dlclose : event_pair {
+        dlclose(ktrace_event_t E) : event_pair(E), handle(E->arg2) {}
+        uint64_t handle;
+        int result;
+    };
+
+    struct app_launch : event_pair {
+        app_launch(ktrace_event_t E) : event_pair(E), address(E->arg2) {}
+        uint64_t address;
+        uint64_t launchMode;
+        std::vector<event_pair *> _children;
+    };
+
+    struct static_init : event_pair {
+        static_init(ktrace_event_t E) : event_pair(E), libraryAddress(E->arg2), funcAddress(E->arg3) {}
+        uint64_t libraryAddress;
+        uint64_t funcAddress;
+    };
+
+    struct map_image : event_pair {
+        map_image(ktrace_event_t E) : event_pair(E), path(stringForID(E->arg2)) {}
+        std::string path;
+        uint64_t result;
+    };
+
+    struct apply_fixups : event_pair {
+        apply_fixups(ktrace_event_t E) : event_pair(E) {}
+    };
+
+    struct attach_signature : event_pair {
+        attach_signature(ktrace_event_t E) : event_pair(E) {}
+    };
+
+    struct build_closure : event_pair {
+        build_closure(ktrace_event_t E) : event_pair(E) {}
+    };
+
+    struct add_image_callback : event_pair {
+        add_image_callback(ktrace_event_t E) : event_pair(E), libraryAddress(E->arg2), funcAddress(E->arg3) {}
+        uint64_t libraryAddress;
+        uint64_t funcAddress;
+    };
+
+    struct remove_image_callback : event_pair {
+        remove_image_callback(ktrace_event_t E) : event_pair(E), libraryAddress(E->arg2), funcAddress(E->arg3) {}
+        uint64_t libraryAddress;
+        uint64_t funcAddress;
+
+    };
+
+    struct objc_image_init : event_pair {
+        objc_image_init(ktrace_event_t E) : event_pair(E), libraryAddress(E->arg2) {}
+        uint64_t libraryAddress;
+    };
+
+    struct objc_images_map : event_pair {
+        objc_images_map(ktrace_event_t E) : event_pair(E) {}
+    };
+
+    std::shared_ptr<event_pair> _currentRootEvent;
+    std::vector<std::shared_ptr<event_pair>> _eventStack;
+    std::vector<std::shared_ptr<event_pair>> _rootEvents;
+};
+
+struct OutputManager {
+    std::map<unsigned long, std::unique_ptr<output_renderer>> sOutputRenders;
+
+    void flush() {
+        if (JSON_Tracing_flag) {
+            std::ostringstream ostream;
+            ostream << "{\"displayTimeUnit\":\"ns\"";
+            ostream << ", \"traceEvents\": [";
+            bool firstEvent = true;
+            for (const auto& renderer : sOutputRenders) {
+                for (const auto& root : renderer.second->rootEvents()) {
+                    if (firstEvent)
+                        firstEvent = false;
+                    else
+                        ostream << ", ";
+                    renderer.second->outputTracingJSON(root, ostream);
+                }
+            }
+            ostream << "]";
+            ostream << "}" << std::endl;
+            std::cout << ostream.str();
+            if (!RAW_flag)
+                fflush(stdout);
+        }
+    }
+};
+
+static OutputManager sOutputManager;
+
+void
+setup_ktrace_callbacks(void)
+{
+    ktrace_events_single(s, TRACEDBG_CODE(DBG_TRACE_STRING, TRACE_STRING_GLOBAL), ^(ktrace_event_t event){
+        char argChars[33] = {0};
+        memset(&argChars[0], 0, 33);
+        if ((event->debugid & DBG_FUNC_START) == DBG_FUNC_START) {
+            uint64_t str_id = event->arg2;
+            if (((event->debugid & DBG_FUNC_END) == DBG_FUNC_END)
+                && str_id != 0) {
+                auto i = gActiveStrings.find(str_id);
+                if (i != gActiveStrings.end()) {
+                    gActiveStrings.erase(i);
+                }
+            }
+            *((uint64_t*)&argChars[0]) = event->arg3;
+            *((uint64_t*)&argChars[8]) = event->arg4;
+            gActiveStringIDs.insert(std::make_pair(event->threadid, str_id));
+            gActiveStrings.insert(std::make_pair(str_id, argChars));
+        } else {
+            // Not a start, so lets grab our data
+            *((uint64_t*)&argChars[0]) = event->arg1;
+            *((uint64_t*)&argChars[8]) = event->arg2;
+            *((uint64_t*)&argChars[16]) = event->arg3;
+            *((uint64_t*)&argChars[24]) = event->arg4;
+
+            auto i = gActiveStringIDs.find(event->threadid);
+            if (i != gActiveStringIDs.end()) {
+                auto j = gActiveStrings.find(i->second);
+                if (j != gActiveStrings.end()) {
+                    j->second += argChars;
+                }
+            }
+        }
+
+        if ((event->debugid & DBG_FUNC_END) == DBG_FUNC_END) {
+            auto i = gActiveStringIDs.find(event->threadid);
+            if (i != gActiveStringIDs.end()) {
+                gActiveStringIDs.erase(i);
+            }
+        };
+    });
+
+    // Event though our events are paired, we process them individually so we can
+    // render nested events
+    ktrace_events_range(s, KDBG_EVENTID(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 0), KDBG_EVENTID(DBG_DYLD, DBG_DYLD_API_SUBCLASS+1, 0), ^(ktrace_event_t event){
+        assert((event->debugid & KDBG_FUNC_MASK) != 0);
+        auto i = sOutputManager.sOutputRenders.find(event->threadid);
+        if (i == sOutputManager.sOutputRenders.end()) {
+            sOutputManager.sOutputRenders.emplace(std::make_pair(event->threadid, std::make_unique<output_renderer>(s, event)));
+            i = sOutputManager.sOutputRenders.find(event->threadid);
+        }
+        i->second->recordEvent(event);
+        if (i->second->empty()) {
+            sOutputManager.sOutputRenders.erase(i);
+        }
+    });
+}
+
+int
+main(int argc, char *argv[])
+{
+    char ch;
+    int rv = 0;
+    bool exclude_pids = false;
+    uint64_t time_limit_ns = 0;
+
+    get_screenwidth();
+
+    s = ktrace_session_create();
+    assert(s);
+
+    while ((ch = getopt(argc, argv, "jJeR:t:")) != -1) {
+        switch (ch) {
+            case 'j':
+                JSON_flag = true;
+                break;
+            case 'J':
+                JSON_Tracing_flag = true;
+                break;
+            case 'e':
+                exclude_pids = true;
+                break;
+            case 't':
+                time_limit_ns = (uint64_t)(NSEC_PER_SEC * atof(optarg));
+                if (time_limit_ns == 0) {
+                    fprintf(stderr, "ERROR: could not set time limit to %s\n",
+                            optarg);
+                    exit(1);
+                }
+                break;
+            case 'R':
+                RAW_flag = true;
+                rv = ktrace_set_file(s, optarg);
+                if (rv) {
+                    fprintf(stderr, "ERROR: reading trace from '%s' failed (%s)\n", optarg, strerror(errno));
+                    exit(1);
+                }
+                break;
+            default:
+                exit_usage();
+        }
+    }
+
+    argc -= optind;
+    argv += optind;
+
+    if (time_limit_ns > 0) {
+        if (RAW_flag) {
+            fprintf(stderr, "NOTE: time limit ignored when a raw file is specified\n");
+        } else {
+            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, time_limit_ns),
+                           dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0),
+                           ^{
+                               ktrace_end(s, 0);
+                           });
+        }
+    }
+
+    if (!RAW_flag) {
+        if (geteuid() != 0) {
+            fprintf(stderr, "'dyld_usage' must be run as root...\n");
+            exit(1);
+        }
+
+        /*
+         * ktrace can't both *in*clude and *ex*clude pids, so: if we are
+         * already excluding pids, or if we are not explicitly including
+         * or excluding any pids, then exclude the defaults.
+         *
+         * if on the other hand we are explicitly including pids, we'll
+         * filter the defaults out naturally.
+         */
+        if (exclude_pids || argc == 0) {
+            ktrace_exclude_process(s, "dyld_usage");
+            ktrace_exclude_process(s, "Terminal");
+            ktrace_exclude_process(s, "telnetd");
+            ktrace_exclude_process(s, "telnet");
+            ktrace_exclude_process(s, "sshd");
+            ktrace_exclude_process(s, "rlogind");
+            ktrace_exclude_process(s, "tcsh");
+            ktrace_exclude_process(s, "csh");
+            ktrace_exclude_process(s, "sh");
+            ktrace_exclude_process(s, "zsh");
+#if TARGET_OS_EMBEDDED
+            ktrace_exclude_process(s, "dropbear");
+#endif /* TARGET_OS_EMBEDDED */
+        }
+    }
+
+    /*
+     * Process the list of specified pids, and in/exclude them as
+     * appropriate.
+     */
+    while (argc > 0) {
+        pid_t pid;
+        char *name;
+        char *endptr;
+
+        name = argv[0];
+        pid = (pid_t)strtoul(name, &endptr, 10);
+
+        if (*name != '\0' && *endptr == '\0') {
+            if (exclude_pids) {
+                rv = ktrace_exclude_pid(s, pid);
+            } else {
+                if (pid != 0)
+                    rv = ktrace_filter_pid(s, pid);
+            }
+        } else {
+            if (exclude_pids) {
+                rv = ktrace_exclude_process(s, name);
+            } else {
+                if (strcmp(name, "kernel_task"))
+                    rv = ktrace_filter_process(s, name);
+            }
+        }
+
+        if (rv == EINVAL) {
+            fprintf(stderr, "ERROR: cannot both include and exclude simultaneously\n");
+            exit(1);
+        } else {
+            assert(!rv);
+        }
+
+        argc--;
+        argv++;
+    }
+    /* provides SIGINT, SIGHUP, SIGPIPE, SIGTERM handlers */
+    ktrace_set_signal_handler(s);
+    ktrace_set_completion_handler(s, ^{
+        sOutputManager.flush();
+        exit(0);
+    });
+
+    signal(SIGWINCH, SIG_IGN);
+    sigwinch_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGWINCH, 0, dispatch_get_main_queue());
+    dispatch_source_set_event_handler(sigwinch_source, ^{
+        get_screenwidth();
+    });
+    dispatch_activate(sigwinch_source);
+
+    setup_ktrace_callbacks();
+
+    ktrace_set_dropped_events_handler(s, ^{
+        fprintf(stderr, "dyld_usage: buffer overrun, events generated too quickly\n");
+
+        /* clear any state that is now potentially invalid */
+    });
+
+    ktrace_set_execnames_enabled(s, KTRACE_FEATURE_LAZY);
+    ktrace_set_vnode_paths_enabled(s, false);
+    /* no need to symbolicate addresses */
+    ktrace_set_uuid_map_enabled(s, KTRACE_FEATURE_DISABLED);
+
+    rv = ktrace_start(s, dispatch_get_main_queue());
+
+    if (rv) {
+        perror("ktrace_start");
+        exit(1);
+    }
+
+    dispatch_main();
+
+    return 0;
+}
index 99a66448c24b0a206865b8f8ae019cb2099b72c1..dfad3f59ae7d94a14830376dcbdc42f8c0c8c945 100644 (file)
@@ -22,6 +22,8 @@
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#define _FORTIFY_SOURCE 0
+
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
 #include <libkern/OSAtomic.h>
 #include <errno.h>
 #include <pthread.h>
+#include <corecrypto/ccdigest.h>
+#include <corecrypto/ccsha1.h>
+#include <corecrypto/ccsha2.h>
+
 #if TARGET_IPHONE_SIMULATOR
        #include "dyldSyscallInterface.h"
        #include "dyld_images.h"
                typedef struct mach_header                      macho_header;
                typedef struct nlist                            macho_nlist;
        #endif
+
+    #define DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE    (32*1024)
+    #define DYLD_PROCESS_INFO_NOTIFY_LOAD_ID            0x1000
+    #define DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID          0x2000
+    #define DYLD_PROCESS_INFO_NOTIFY_MAIN_ID            0x3000
+
+    struct dyld_process_info_image_entry {
+        uuid_t                        uuid;
+        uint64_t                    loadAddress;
+        uint32_t                    pathStringOffset;
+        uint32_t                    pathLength;
+    };
+
+    struct dyld_process_info_notify_header {
+        mach_msg_header_t            header;
+        uint32_t                    version;
+        uint32_t                    imageCount;
+        uint32_t                    imagesOffset;
+        uint32_t                    stringsOffset;
+        uint64_t                    timestamp;
+    };
 #endif
 
 // from _simple.h in libc
@@ -109,13 +136,13 @@ void _ZN10__cxxabiv112__unexpectedEPFvvE()
 }
 
 // std::__terminate() called by C++ unwinding code
-void _ZSt11__terminatePFvvE(void (*func)())
+void _ZSt11__terminatePFvvE(void (*func)(void))
 {
        _ZN4dyld4haltEPKc("dyld std::__terminate()\n");
 }
 
 // std::__unexpected() called by C++ unwinding code
-void _ZSt12__unexpectedPFvvE(void (*func)())
+void _ZSt12__unexpectedPFvvE(void (*func)(void))
 {
        _ZN4dyld4haltEPKc("dyld std::__unexpected()\n");
 }
@@ -290,8 +317,6 @@ void __chk_fail()
 
 
 // referenced by libc.a(pthread.o) but unneeded in dyld
-void _init_cpu_capabilities() { }
-void _cpu_capabilities() {}
 void set_malloc_singlethreaded() {}
 int PR_5243343_flag = 0;
 
@@ -312,8 +337,8 @@ FILE* __stderrp = NULL;
 FILE* __stdoutp = NULL;
 
 // work with c++abi.a
-void (*__cxa_terminate_handler)() = _ZSt9terminatev;
-void (*__cxa_unexpected_handler)() = _ZSt10unexpectedv;
+void (*__cxa_terminate_handler)(void) = _ZSt9terminatev;
+void (*__cxa_unexpected_handler)(void) = _ZSt10unexpectedv;
 
 void abort_message(const char* format, ...)
 {
@@ -375,8 +400,6 @@ int _ZN4dyld7my_openEPKcii(const char* path, int flag, int other)
 
 #if TARGET_IPHONE_SIMULATOR
 
-#include <coreSymbolicationDyldSupport.h>
-
 int myopen(const char* path, int oflag, int extra) __asm("_open");
 int myopen(const char* path, int oflag, int extra) {
        return gSyscallHelpers->open(path, oflag, extra);
@@ -530,20 +553,25 @@ int       readdir_r(DIR* dirp, struct dirent* entry, struct dirent **result) {
        return gSyscallHelpers->readdir_r(dirp, entry, result);
 }
 
+// HACK: readdir() is not used in dyld_sim, but it is pulled in by libc.a, then dead stripped.
+struct dirent* readdir(DIR *dirp) {
+    _ZN4dyld4haltEPKc("dyld_sim readdir() not supported\n");
+}
+
 int closedir(DIR* dirp) {
        if ( gSyscallHelpers->version < 3 )
                return EPERM;
        return gSyscallHelpers->closedir(dirp);
 }
 
-void xcoresymbolication_load_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh)
+void coresymbolication_load_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh)
 {
        // if host dyld supports this notifier, call into host dyld
        if ( gSyscallHelpers->version >= 4 )
                return gSyscallHelpers->coresymbolication_load_notifier(connection, timestamp, path, mh);
 }
 
-void xcoresymbolication_unload_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh)
+void coresymbolication_unload_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh)
 {
        // if host dyld supports this notifier, call into host dyld
        if ( gSyscallHelpers->version >= 4 )
@@ -556,16 +584,24 @@ void xcoresymbolication_unload_notifier(void* connection, uint64_t timestamp, co
 
 #if SUPPORT_HOST_10_11
 typedef int               (*FuncPtr_proc_regionfilename)(int pid, uint64_t address, void* buffer, uint32_t bufferSize);
-typedef pid_t             (*FuncPtr_getpid)();
+typedef pid_t             (*FuncPtr_getpid)(void);
 typedef bool              (*FuncPtr_mach_port_insert_right)(ipc_space_t task, mach_port_name_t name, mach_port_t poly, mach_msg_type_name_t polyPoly);
 typedef kern_return_t     (*FuncPtr_mach_port_allocate)(ipc_space_t, mach_port_right_t, mach_port_name_t*);
 typedef mach_msg_return_t (*FuncPtr_mach_msg)(mach_msg_header_t *, mach_msg_option_t , mach_msg_size_t , mach_msg_size_t , mach_port_name_t , mach_msg_timeout_t , mach_port_name_t);
+typedef void              (*FuncPtr_mach_msg_destroy)(mach_msg_header_t *);
+typedef kern_return_t     (*FuncPtr_mach_port_construct)(ipc_space_t task, mach_port_options_ptr_t options, mach_port_context_t context, mach_port_name_t *name);
+typedef kern_return_t     (*FuncPtr_mach_port_destruct)(ipc_space_t task, mach_port_name_t name, mach_port_delta_t srdelta, mach_port_context_t guard);
+typedef void              (*FuncPtr_notifyMonitoringDyld)(bool unloading, unsigned portSlot, unsigned imageCount, const struct dyld_image_info infos[]);
 
 static FuncPtr_proc_regionfilename              proc_proc_regionfilename = NULL;
 static FuncPtr_getpid                    proc_getpid = NULL;
 static FuncPtr_mach_port_insert_right    proc_mach_port_insert_right = NULL;
 static FuncPtr_mach_port_allocate        proc_mach_port_allocate = NULL;
 static FuncPtr_mach_msg                  proc_mach_msg = NULL;
+static FuncPtr_mach_msg_destroy          proc_mach_msg_destroy = NULL;
+static FuncPtr_mach_port_construct       proc_mach_port_construct = NULL;
+static FuncPtr_mach_port_destruct        proc_mach_port_destruct = NULL;
+static FuncPtr_notifyMonitoringDyld      proc_notifyMonitoringDyld = NULL;
 
 
 
@@ -633,10 +669,91 @@ static void findHostFunctions() {
                        else if ( strcmp(name, "_mach_port_allocate") == 0 )
                                proc_mach_port_allocate = (FuncPtr_mach_port_allocate)(s->n_value + slide);
                        else if ( strcmp(name, "_mach_msg") == 0 )
-                               proc_mach_msg = (FuncPtr_mach_msg)(s->n_value + slide);
+                proc_mach_msg = (FuncPtr_mach_msg)(s->n_value + slide);
+            else if ( strcmp(name, "__ZN4dyldL20notifyMonitoringDyldEbjjPK15dyld_image_info") == 0 )
+                proc_notifyMonitoringDyld = (FuncPtr_notifyMonitoringDyld)(s->n_value + slide);
                }
        }
 }
+
+// Look up sycalls in host dyld needed by coresymbolication_ routines in dyld_sim
+static bool findHostLibSystemFunctions() {
+    // Only look up symbols once
+    if (proc_mach_msg_destroy != NULL && proc_mach_port_construct != NULL && proc_mach_port_destruct != NULL)
+        return true;
+
+    const struct mach_header* hostLibSystemMH = NULL;
+    struct dyld_all_image_infos* imageInfo = (struct dyld_all_image_infos*)(gSyscallHelpers->getProcessInfo());
+    const struct dyld_image_info* infoArray = imageInfo->infoArray;
+    if (infoArray == NULL)
+        return false;
+    uint32_t imageCount = imageInfo->infoArrayCount;
+    for (uint32_t i = 0; i<imageCount; ++i) {
+        if (strcmp("/usr/lib/system/libsystem_kernel.dylib", infoArray[i].imageFilePath) == 0) {
+            //Found the kernel interface
+            hostLibSystemMH = infoArray[i].imageLoadAddress;
+            break;
+        }
+    }
+    if (hostLibSystemMH == NULL)
+        return false;
+
+    // find symbol table and slide of host dyld
+    uintptr_t slide = 0;
+    const macho_nlist* symbolTable = NULL;
+    const char* symbolTableStrings = NULL;
+    const struct dysymtab_command* dynSymbolTable = NULL;
+    const uint32_t cmd_count = hostLibSystemMH->ncmds;
+    const struct load_command* const cmds = (struct load_command*)(((char*)hostLibSystemMH)+sizeof(macho_header));
+    const struct load_command* cmd = cmds;
+    const uint8_t* linkEditBase = NULL;
+    for (uint32_t i = 0; i < cmd_count; ++i) {
+        switch (cmd->cmd) {
+            case LC_SEGMENT_COMMAND:
+            {
+                const macho_segment_command* seg = (macho_segment_command*)cmd;
+                if ( (seg->fileoff == 0) && (seg->filesize != 0) )
+                    slide = (uintptr_t)hostLibSystemMH - seg->vmaddr;
+                if ( strcmp(seg->segname, "__LINKEDIT") == 0 )
+                    linkEditBase = (uint8_t*)(seg->vmaddr - seg->fileoff + slide);
+            }
+                break;
+            case LC_SYMTAB:
+            {
+                const struct symtab_command* symtab = (struct symtab_command*)cmd;
+                if ( linkEditBase == NULL )
+                    return false;
+                symbolTableStrings = (const char*)&linkEditBase[symtab->stroff];
+                symbolTable = (macho_nlist*)(&linkEditBase[symtab->symoff]);
+            }
+                break;
+            case LC_DYSYMTAB:
+                dynSymbolTable = (struct dysymtab_command*)cmd;
+                break;
+        }
+        cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+    }
+    if ( symbolTableStrings == NULL )
+        return false;;
+    if ( dynSymbolTable == NULL )
+        return false;;
+
+    // scan local symbols in host dyld looking for load/unload functions
+    const macho_nlist* const localsStart = &symbolTable[dynSymbolTable->iextdefsym];
+    const macho_nlist* const localsEnd= &localsStart[dynSymbolTable->nextdefsym];
+    for (const macho_nlist* s = localsStart; s < localsEnd; ++s) {
+        if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) {
+            const char* name = &symbolTableStrings[s->n_un.n_strx];
+            if ( strcmp(name, "_mach_msg_destroy") == 0 )
+                proc_mach_msg_destroy = (FuncPtr_mach_msg_destroy)(s->n_value + slide);
+            else if ( strcmp(name, "_mach_port_construct") == 0 )
+                proc_mach_port_construct = (FuncPtr_mach_port_construct)(s->n_value + slide);
+            else if ( strcmp(name, "_mach_port_destruct") == 0 )
+                proc_mach_port_destruct = (FuncPtr_mach_port_destruct)(s->n_value + slide);
+        }
+    }
+    return (proc_mach_msg_destroy != NULL && proc_mach_port_construct != NULL && proc_mach_port_destruct != NULL);
+}
 #endif
 
 
@@ -706,6 +823,41 @@ kern_return_t mach_msg(mach_msg_header_t* msg, mach_msg_option_t option, mach_ms
 #endif
 }
 
+void mach_msg_destroy(mach_msg_header_t *msg) {
+    if ( gSyscallHelpers->version >= 12 ) {
+        gSyscallHelpers->mach_msg_destroy(msg);
+        return;
+    }
+#if SUPPORT_HOST_10_11
+    if (findHostLibSystemFunctions()) {
+        (*proc_mach_msg_destroy)(msg);
+    }
+#endif
+}
+
+kern_return_t mach_port_construct(ipc_space_t task, mach_port_options_ptr_t options, mach_port_context_t context, mach_port_name_t *name) {
+    if ( gSyscallHelpers->version >= 12 ) {
+        return gSyscallHelpers->mach_port_construct(task, options, context, name);
+    }
+#if SUPPORT_HOST_10_11
+    if (findHostLibSystemFunctions()) {
+        return (*proc_mach_port_construct)(task, options, context, name);
+    }
+#endif
+    return KERN_NOT_SUPPORTED;
+}
+
+kern_return_t mach_port_destruct(ipc_space_t task, mach_port_name_t name, mach_port_delta_t srdelta, mach_port_context_t guard) {
+    if ( gSyscallHelpers->version >= 12 ) {
+        return gSyscallHelpers->mach_port_destruct(task, name, srdelta, guard);
+    }
+#if SUPPORT_HOST_10_11
+    if (findHostLibSystemFunctions()) {
+        return (*proc_mach_port_destruct)(task, name, srdelta, guard);
+    }
+#endif
+    return KERN_NOT_SUPPORTED;
+}
 
 void abort_with_payload(uint32_t reason_namespace, uint64_t reason_code, void* payload, uint32_t payload_size, const char* reason_string, uint64_t reason_flags)
 {
@@ -774,6 +926,86 @@ int kdebug_trace(uint32_t code, uint64_t arg1, uint64_t arg2, uint64_t arg3, uin
     return 0;
 }
 
+uint64_t kdebug_trace_string(uint32_t debugid, uint64_t str_id, const char *str) {
+    if ( gSyscallHelpers->version >= 9 )
+        return gSyscallHelpers->kdebug_trace_string(debugid, str_id, str);
+    return 0;
+}
+
+uint64_t amfi_check_dyld_policy_self(uint64_t inFlags, uint64_t* outFlags)
+{
+    if ( gSyscallHelpers->version >= 10 )
+        return gSyscallHelpers->amfi_check_dyld_policy_self(inFlags, outFlags);
+    *outFlags = 0x3F;  // on old kernel, simulator process get all flags
+    return 0;
+}
+    
+static mach_port_t sNotifyReplyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
+static bool        sZombieNotifiers[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT];
+
+void _ZN4dyld24notifyMonitoringDyldMainEv() {
+    if ( gSyscallHelpers->version >= 11 ) {
+        gSyscallHelpers->notifyMonitoringDyldMain();
+        return;
+    }
+    struct dyld_all_image_infos* imageInfo = (struct dyld_all_image_infos*)(gSyscallHelpers->getProcessInfo());
+    for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
+        if ( (imageInfo->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    = imageInfo->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]);
+                mach_port_deallocate(mach_task_self(), imageInfo->notifyPorts[slot]);
+                mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]);
+                imageInfo->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 _ZN4dyld20notifyMonitoringDyldEbjPPK11mach_headerPPKc(bool unloading, unsigned imageCount, const struct mach_header* loadAddresses[], const char* imagePaths[]) {
+    if ( gSyscallHelpers->version >= 11 ) {
+        gSyscallHelpers->notifyMonitoringDyld(unloading, imageCount, loadAddresses, imagePaths);
+        return;
+    }
+#if SUPPORT_HOST_10_11
+    findHostFunctions();
+    if ( proc_notifyMonitoringDyld ) {
+        struct dyld_image_info infos[imageCount];
+        for (int i=0; i<imageCount; ++i) {
+            struct dyld_image_info info = { loadAddresses[i], imagePaths[i], 0};
+            infos[i] = info;
+        }
+        for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) {
+            (*proc_notifyMonitoringDyld)(unloading, slot, imageCount, infos);
+        }
+    }
+#endif
+}
+
 int* __error(void) {
        return gSyscallHelpers->errnoAddress();
 } 
@@ -788,6 +1020,10 @@ mach_port_t mach_task_self_ = MACH_PORT_NULL;
 extern int myerrno_fallback  __asm("_errno");
 int myerrno_fallback = 0;
 
+
+vm_size_t vm_kernel_page_mask = 0xFFF;
+vm_size_t vm_page_size = 0x1000;
+
 #endif  // TARGET_IPHONE_SIMULATOR
 
 
@@ -817,4 +1053,52 @@ void _Block_object_dispose(const void* object, int flags)
 }
 
 
+unsigned char* CC_SHA384(const void* data, unsigned long len, unsigned char* md)
+{
+    const struct ccdigest_info *di = ccsha384_di();
+    ccdigest_di_decl(di, dc);//declares dc array in stack
+    ccdigest_init(di, dc);
+       ccdigest_update(di, dc, len, data);
+    ccdigest_final(di, dc, md);
+    ccdigest_di_clear(di, dc);
+    return NULL;
+}
+
+
+unsigned char* CC_SHA256(const void* data, unsigned long len, unsigned char* md)
+{
+    const struct ccdigest_info *di = ccsha256_di();
+    ccdigest_di_decl(di, dc);//declares dc array in stack
+    ccdigest_init(di, dc);
+       ccdigest_update(di, dc, len, data);
+    ccdigest_final(di, dc, md);
+    ccdigest_di_clear(di, dc);
+    return NULL;
+}
+
+unsigned char* CC_SHA1(const void* data, unsigned long len, unsigned char* md)
+{
+    const struct ccdigest_info *di = ccsha1_di();
+    ccdigest_di_decl(di, dc);//declares dc array in stack
+    ccdigest_init(di, dc);
+    ccdigest_update(di, dc, len, data);
+    ccdigest_final(di, dc, md);
+    ccdigest_di_clear(di, dc);
+    return NULL;
+}
+
+#if !TARGET_IPHONE_SIMULATOR
+errno_t memset_s(void* s, rsize_t smax, int c, rsize_t n)
+{
+    errno_t err = 0;
+    if (s == NULL)
+        return EINVAL;
+    if (n > smax) {
+        err = EOVERFLOW;
+        n = smax;
+    }
+    memset(s, c, n);
+    return err;
+}
+#endif
 
diff --git a/src/start_glue.h b/src/start_glue.h
deleted file mode 100644 (file)
index 57b03c1..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2013 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#ifndef __START_GLUE_H__
-#define __START_GLUE_H__
-
-// Implemented in start_glue.s
-extern "C" void start();
-
-
-// <rdar://problem/12792039> need 'start' to be one atom, but entry is in interior
-
-#if __x86_64__ || __i386__ 
-       #define address_of_start (void*)((uintptr_t)&start + 1)
-#elif __arm64__
-       #define address_of_start (void*)((uintptr_t)&start + 4)
-#elif __arm__
-       #define address_of_start (void*)((uintptr_t)&start + 2)
-#endif
-
-
-
-#endif // __START_GLUE_H__
index fb63817ec60305b0f7037269ca2020ef93727a1f..1e9aa807d4e90827a3254cfa7fa1c1dea76ee1b8 100644 (file)
@@ -228,12 +228,24 @@ LlazyAllocate:
        .globl _tlv_get_addr
        .private_extern _tlv_get_addr
 _tlv_get_addr:
+#if __LP64__
        ldr             x16, [x0, #8]                   // get key from descriptor
+#else
+       ldr             w16, [x0, #4]                   // get key from descriptor
+#endif
        mrs             x17, TPIDRRO_EL0
        and             x17, x17, #-8                   // clear low 3 bits???
+#if __LP64__
        ldr             x17, [x17, x16, lsl #3] // get thread allocation address for this key
+#else
+       ldr             w17, [x17, x16, lsl #2] // get thread allocation address for this key
+#endif
        cbz             x17, LlazyAllocate              // if NULL, lazily allocate
+#if __LP64__
        ldr             x16, [x0, #16]                  // get offset from descriptor
+#else
+       ldr             w16, [x0, #8]                   // get offset from descriptor
+#endif
        add             x0, x17, x16                    // return allocation+offset
        ret             lr
 
@@ -258,7 +270,11 @@ LlazyAllocate:
        mov             x0, x16                                 // use key from descriptor as parameter
        bl              _tlv_allocate_and_initialize_for_key
        ldp             x16, x17, [sp], #16             // pop descriptor
+#if __LP64__
        ldr             x16, [x16, #16]                 // get offset from descriptor
+#else
+       ldr             w16, [x16, #8]                  // get offset from descriptor
+#endif
        add             x0, x0, x16                             // return allocation+offset
 
        ldp             q6,  q7,  [sp], #32
index ab4d4fd8e6fc1c92c86197253e6aa95382f9f38e..1783f7fcfe37941d481569cee8a2c0776f3b7395 100755 (executable)
@@ -20,6 +20,7 @@ def parseDirectives(testCaseSourceDir):
     runLines = []
     minOS = ""
     timeout = ""
+    noCrashLogs = []
     for file in os.listdir(testCaseSourceDir):
         if file.endswith((".c", ".cpp", ".cxx")):
             with open(testCaseSourceDir + "/" + file) as f:
@@ -39,13 +40,16 @@ def parseDirectives(testCaseSourceDir):
                     timeoutIndex = string.find(line, "RUN_TIMEOUT:")
                     if timeoutIndex != -1:
                         timeout = line[timeoutIndex+12:].lstrip()
-
+                    noCrashLogsIndex = string.find(line, "NO_CRASH_LOG:")
+                    if noCrashLogsIndex != -1:
+                        noCrashLogs.append(line[noCrashLogsIndex+13:].lstrip())
     return {
         "BUILD":        buildLines,
         "BUILD_ONLY":   onlyLines,
         "BUILD_MIN_OS": minOS,
         "RUN":          runLines,
-        "RUN_TIMEOUT":  timeout
+        "RUN_TIMEOUT":  timeout,
+        "NO_CRASH_LOG": noCrashLogs
    }
 
 
@@ -66,13 +70,13 @@ def useTestCase(testCaseDirectives, platformName):
 # Use BUILD directives to construct the test case
 # Use RUN directives to generate a shell script to run test(s)
 #
-def buildTestCase(testCaseDirectives, testCaseSourceDir, toolsDir, sdkDir, minOsOptionsName, defaultMinOS, archOptions, testCaseDestDirBuild, testCaseDestDirRun):
+def buildTestCase(testCaseDirectives, testCaseSourceDir, toolsDir, sdkDir, dyldIncludesDir, minOsOptionsName, defaultMinOS, archOptions, testCaseDestDirBuild, testCaseDestDirRun):
     scratchDir = tempfile.mkdtemp()
     if testCaseDirectives["BUILD_MIN_OS"]:
         minOS = testCaseDirectives["BUILD_MIN_OS"]
     else:
         minOS = defaultMinOS
-    compilerSearchOptions = " -isysroot " + sdkDir + " -I" + sdkDir + "/System/Library/Frameworks/System.framework/PrivateHeaders"
+    compilerSearchOptions = " -isysroot " + sdkDir + " -I" + sdkDir + "/System/Library/Frameworks/System.framework/PrivateHeaders" + " -I" + dyldIncludesDir
     if minOsOptionsName == "mmacosx-version-min":
         taskForPidCommand = "touch "
         envEnableCommand  = "touch "
@@ -116,8 +120,17 @@ def buildTestCase(testCaseDirectives, testCaseSourceDir, toolsDir, sdkDir, minOs
         runFile.write("#!/bin/sh\n")
         runFile.write("cd " + testCaseDestDirRun + "\n")
         os.chmod(runFilePath, 0755)
+        runFile.write("echo \"run in dyld2 mode\" \n");
         for runline in testCaseDirectives["RUN"]:
            runFile.write(string.Template(runline).safe_substitute(runSubs) + "\n")
+        runFile.write("echo \"run in dyld3 mode\" \n");
+        for runline in testCaseDirectives["RUN"]:
+            subLine = string.Template(runline).safe_substitute(runSubs)
+            if subLine.startswith("sudo "):
+                subLine = "sudo DYLD_USE_CLOSURES=1 " + subLine[5:]
+            else:
+                subLine = "DYLD_USE_CLOSURES=1 " + subLine
+            runFile.write(subLine + "\n")
         runFile.write("\n")
         runFile.close()
     return 0
@@ -137,13 +150,13 @@ if __name__ == "__main__":
     if not dyldSrcDir:
         dyldSrcDir = os.getcwd()
     testsSrcTopDir = dyldSrcDir + "/testing/test-cases/"
+    dyldIncludesDir = dyldSrcDir + "/include/"
     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"
+        sdkDir = subprocess.check_output(["xcrun", "-sdk", "macosx.internal", "--show-sdk-path"]).rstrip()
     toolsDir = os.getenv("TOOLCHAIN_DIR", "/")
     defaultMinOS = ""
-    minVersNum = "10.12"
+    minVersNum = "10.14"
     minOSOption = os.getenv("DEPLOYMENT_TARGET_CLANG_FLAG_NAME", "")
     if minOSOption:
         minOSVersName = os.getenv("DEPLOYMENT_TARGET_CLANG_ENV_NAME", "")
@@ -170,7 +183,8 @@ if __name__ == "__main__":
             else:
                 archOptions = "-arch x86_64"
     allTests = []
-    for f in os.listdir(testsSrcTopDir):
+    suppressCrashLogs = []
+    for f in sorted(os.listdir(testsSrcTopDir)):
         if f.endswith(".dtest"):
             testName = f[0:-6]
             outDirBuild = testsBuildDstTopDir + testName
@@ -178,7 +192,7 @@ if __name__ == "__main__":
             testCaseDir = testsSrcTopDir + f
             testCaseDirectives = parseDirectives(testCaseDir)
             if useTestCase(testCaseDirectives, platformName):
-                result = buildTestCase(testCaseDirectives, testCaseDir, toolsDir, sdkDir, minOSOption, minVersNum, archOptions, outDirBuild, outDirRun)
+                result = buildTestCase(testCaseDirectives, testCaseDir, toolsDir, sdkDir, dyldIncludesDir, minOSOption, minVersNum, archOptions, outDirBuild, outDirRun)
                 if result:
                     sys.exit(result)
                 mytest = {}
@@ -187,15 +201,27 @@ if __name__ == "__main__":
                 mytest["WorkingDirectory"] = testsRunDstTopDir + testName
                 mytest["Command"] = []
                 mytest["Command"].append("./run.sh")
+                usesDtrace = False
                 for runline in testCaseDirectives["RUN"]:
                     if "$SUDO" in runline:
                         mytest["AsRoot"] = True
+                    if "dtrace" in runline:
+                        usesDtrace = True
                 if testCaseDirectives["RUN_TIMEOUT"]:
                     mytest["Timeout"] = testCaseDirectives["RUN_TIMEOUT"]
+                if usesDtrace and minOSOption != "mmacosx-version-min":
+                    mytest["BootArgsSet"] = "dtrace_dof_mode=1"
                 allTests.append(mytest)
+            if testCaseDirectives["NO_CRASH_LOG"]:
+                for skipCrash in testCaseDirectives["NO_CRASH_LOG"]:
+                    suppressCrashLogs.append(skipCrash)
     batsInfo = { "BATSConfigVersion": "0.1.0",
                  "Project":           "dyld_tests",
                  "Tests":             allTests }
+    if suppressCrashLogs:
+        batsInfo["IgnoreCrashes"] = []
+        for skipCrash in suppressCrashLogs:
+            batsInfo["IgnoreCrashes"].append(skipCrash)
     batsFileDir = dstDir + "/AppleInternal/CoreOS/BATS/unit_tests/"
     shutil.rmtree(batsFileDir, ignore_errors=True)
     os.makedirs(batsFileDir)
@@ -213,4 +239,3 @@ if __name__ == "__main__":
         shFile.close()
     os.chmod(runHelper, 0755)
 
-
diff --git a/testing/test-cases/LC_DYLD_ENV-DYLD_LIBRARY_PATH.dtest/foo.c b/testing/test-cases/LC_DYLD_ENV-DYLD_LIBRARY_PATH.dtest/foo.c
new file mode 100644 (file)
index 0000000..2991d23
--- /dev/null
@@ -0,0 +1,4 @@
+void foo()
+{
+}
+
diff --git a/testing/test-cases/LC_DYLD_ENV-DYLD_LIBRARY_PATH.dtest/main.c b/testing/test-cases/LC_DYLD_ENV-DYLD_LIBRARY_PATH.dtest/main.c
new file mode 100644 (file)
index 0000000..40f90d3
--- /dev/null
@@ -0,0 +1,37 @@
+
+// BUILD:  mkdir -p $BUILD_DIR/hideyhole
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/hideyhole/libfoo1.dylib -install_name /bad/path/libfoo1.dylib
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/hideyhole/libfoo2.dylib -install_name /bad/path2/libfoo2.dylib
+// BUILD:  $CC main.c            -o $BUILD_DIR/main1.exe $BUILD_DIR/hideyhole/libfoo1.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@executable_path/hideyhole
+// BUILD:  $CC main.c            -o $BUILD_DIR/main2.exe $BUILD_DIR/hideyhole/libfoo1.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/hideyhole
+// BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main1.exe
+// BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main2.exe
+
+// BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main1.exe
+// BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main2.exe
+
+// RUN:  ./main1.exe
+// RUN:  ./main2.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+
+/// Test that main executable's LC_DYLD_ENVIRONMENT can set DYLD_LIBRARY_PATH with @executable_path or @loader_path relative paths
+
+extern char* __progname;
+
+int main()
+{
+    printf("[BEGIN] LC_DYLD_ENV-DYLD_LIBRARY_PATH %s\n", __progname);
+
+    void*h = dlopen("/other/path/libfoo2.dylib", 0);
+
+       if ( h != NULL )
+        printf("[PASS] LC_DYLD_ENV-DYLD_LIBRARY_PATH %s\n", __progname);
+    else
+        printf("[FAIL] LC_DYLD_ENV-DYLD_LIBRARY_PATH %s\n", __progname);
+
+       return 0;
+}
+
diff --git a/testing/test-cases/NSCreateObjectFileImageFromFile-stress.dtest/foo.c b/testing/test-cases/NSCreateObjectFileImageFromFile-stress.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-stress.dtest/main.cpp b/testing/test-cases/NSCreateObjectFileImageFromFile-stress.dtest/main.cpp
new file mode 100644 (file)
index 0000000..54c676d
--- /dev/null
@@ -0,0 +1,80 @@
+// BUILD_ONLY: MacOSX
+
+// BUILD:  $CXX main.cpp  -o $BUILD_DIR/NSCreateObjectFileImageFromFile-stress.exe -Wno-deprecated-declarations
+// BUILD:  $CC  foo.c     -o $BUILD_DIR/foo.bundle -bundle
+
+// RUN:  ./NSCreateObjectFileImageFromFile-stress.exe $RUN_DIR/foo.bundle
+
+
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+#include <vector>
+
+int main(int argc, const char* argv[])
+{
+    printf("[BEGIN] NSCreateObjectFileImageFromFile-basic\n");
+
+    const char* path = argv[1];
+
+    std::vector<NSObjectFileImage> ofis;
+    for (unsigned i = 0; i != 32; ++i) {
+               NSObjectFileImage ofi;
+               if ( NSCreateObjectFileImageFromFile(path, &ofi) != NSObjectFileImageSuccess ) {
+                       printf("[FAIL] NSCreateObjectFileImageFromFile failed\n");
+                       return 0;
+               }
+               ofis.push_back(ofi);
+       }
+       
+       for (unsigned i = 0; i != 32; ++i) {
+               NSObjectFileImage ofi = ofis[i];
+               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;
+           }
+       }
+
+       for (unsigned i = 0; i != 32; ++i) {
+               NSObjectFileImage ofi = ofis[i];
+               if ( !NSDestroyObjectFileImage(ofi) ) {
+                       printf("[FAIL] NSDestroyObjectFileImage failed\n");
+                       return 0;
+               }
+       }
+
+    printf("[PASS] NSCreateObjectFileImageFromFile-basic\n");
+       return 0;
+}
+
index 888c043c2a0f8120c01b24b835b5cb5d20d46674..409ff6b52dbe86169f033020e33113ec27de7140 100644 (file)
@@ -2,8 +2,11 @@
 
 // BUILD:  $CC main.c  -o $BUILD_DIR/NSCreateObjectFileImageFromMemory-basic.exe -Wno-deprecated-declarations
 // BUILD:  $CC foo.c   -o $BUILD_DIR/foo.bundle -bundle
+// BUILD:  lipo -thin x86_64 $BUILD_DIR/foo.bundle -output $BUILD_DIR/foo-thin.bundle
 
 // RUN:  ./NSCreateObjectFileImageFromMemory-basic.exe $RUN_DIR/foo.bundle
+// RUN:  ./NSCreateObjectFileImageFromMemory-basic.exe $RUN_DIR/foo-thin.bundle
+
 
 
 #include <stdio.h>
diff --git a/testing/test-cases/_dyld_images_for_addresses.dtest/foo.c b/testing/test-cases/_dyld_images_for_addresses.dtest/foo.c
new file mode 100644 (file)
index 0000000..1e11192
--- /dev/null
@@ -0,0 +1,22 @@
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+
+int foo1()
+{
+    return 1;
+}
+
+int foo2()
+{
+    return 2;
+}
+
+int foo3()
+{
+    return 3;
+}
+
+
diff --git a/testing/test-cases/_dyld_images_for_addresses.dtest/main.c b/testing/test-cases/_dyld_images_for_addresses.dtest/main.c
new file mode 100644 (file)
index 0000000..668eb1e
--- /dev/null
@@ -0,0 +1,105 @@
+
+// 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/dyld_images_for_addresses.exe
+
+// RUN:  ./dyld_images_for_addresses.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h> 
+#include <uuid/uuid.h>
+#include <mach-o/dyld_priv.h>
+
+extern void* __dso_handle;
+
+extern int foo1();
+extern int foo2();
+extern int foo3();
+
+
+int myfunc()
+{
+    return 3;
+}
+
+int myfunc2()
+{
+    return 3;
+}
+
+static int mystaticfoo()
+{
+    return 3;
+}
+
+int mydata = 5;
+int myarray[10];
+
+
+int main()
+{
+    printf("[BEGIN] _dyld_images_for_addresses\n");
+    int mylocal;
+
+    const void* addresses[10];
+    addresses[0] = &myfunc;
+    addresses[1] = &myfunc2;
+    addresses[2] = &mystaticfoo;
+    addresses[3] = &__dso_handle;
+    addresses[4] = &mydata;
+    addresses[5] = &myarray;
+    addresses[6] = &mylocal;    // not owned by dyld, so coresponding dyld_image_uuid_offset should be all zeros
+    addresses[7] = &foo1;
+    addresses[8] = &foo2;
+    addresses[9] = &foo3;
+
+    struct dyld_image_uuid_offset infos[10];
+    _dyld_images_for_addresses(10, addresses, infos);
+
+    for (int i=0; i < 10; ++i) {
+        uuid_string_t str;
+        uuid_unparse_upper(infos[i].uuid, str);
+        printf("0x%09llX 0x%08llX %s\n", (long long)infos[i].image, infos[i].offsetInImage, str);
+    }
+
+    if ( infos[0].image != infos[1].image )
+        printf("[FAIL] _dyld_images_for_addresses 1\n");
+    else if ( infos[0].image != infos[2].image )
+        printf("[FAIL] _dyld_images_for_addresses 2\n");
+    else if ( infos[0].image != infos[3].image )
+        printf("[FAIL] _dyld_images_for_addresses 3\n");
+    else if ( infos[0].image != infos[4].image )
+        printf("[FAIL] _dyld_images_for_addresses 4\n");
+    else if ( infos[0].image != infos[5].image )
+        printf("[FAIL] _dyld_images_for_addresses 5\n");
+    else if ( infos[6].image != NULL )
+        printf("[FAIL] _dyld_images_for_addresses 6\n");
+    else if ( infos[7].image != infos[8].image )
+        printf("[FAIL] _dyld_images_for_addresses 7\n");
+    else if ( infos[7].image != infos[9].image )
+        printf("[FAIL] _dyld_images_for_addresses 8\n");
+    else if ( infos[0].image == infos[7].image )
+        printf("[FAIL] _dyld_images_for_addresses 9\n");
+    else if ( uuid_compare(infos[0].uuid, infos[1].uuid) != 0  )
+        printf("[FAIL] _dyld_images_for_addresses 10\n");
+    else if ( uuid_compare(infos[0].uuid, infos[2].uuid) != 0 )
+        printf("[FAIL] _dyld_images_for_addresses 11\n");
+    else if ( uuid_compare(infos[0].uuid, infos[3].uuid) != 0 )
+        printf("[FAIL] _dyld_images_for_addresses 12\n");
+    else if ( uuid_compare(infos[0].uuid, infos[4].uuid) != 0 )
+        printf("[FAIL] _dyld_images_for_addresses 13\n");
+    else if ( uuid_compare(infos[0].uuid, infos[5].uuid) != 0 )
+        printf("[FAIL] _dyld_images_for_addresses 14\n");
+    else if ( uuid_is_null(infos[6].uuid) == 0 )
+        printf("[FAIL] _dyld_images_for_addresses 15\n");
+    else if ( uuid_compare(infos[7].uuid, infos[8].uuid) != 0 )
+        printf("[FAIL] _dyld_images_for_addresses 16\n");
+    else if ( uuid_compare(infos[7].uuid, infos[9].uuid) != 0 )
+        printf("[FAIL] _dyld_images_for_addresses 17\n");
+    else if ( uuid_compare(infos[0].uuid, infos[7].uuid) == 0 )
+        printf("[FAIL] _dyld_images_for_addresses 18\n");
+    else
+        printf("[PASS] _dyld_images_for_addresses\n");
+    return 0;
+}
+
index a58b85b1cc6f13b8dc2cfb3b1e8f04b2ebf855a2..eab0fb5f264fdaf5feb3a68105b1c92a36e1a331 100644 (file)
 #include <mach-o/dyld.h>
 #include <mach-o/dyld_priv.h>
 
+#if __has_feature(ptrauth_calls)
+    #include <ptrauth.h>
+#endif
+
+static const void* stripPointer(const void* ptr)
+{
+#if __has_feature(ptrauth_calls)
+    return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
+#else
+    return ptr;
+#endif
+}
+
+
 typedef const char* (*BarProc)(void);
 
 extern uint32_t _cpu_capabilities;
@@ -45,8 +59,8 @@ int main()
         return 0;
     }
 
-    if ( !_dyld_is_memory_immutable(&strcpy, 4) ) {
-               printf("[FAIL] _dyld_is_memory_immutable() returned false for function in dyld shared cache\n");
+    if ( !_dyld_is_memory_immutable(stripPointer((void*)&strcpy), 4) ) {
+               printf("[FAIL] _dyld_is_memory_immutable() returned false for strcpy function in dyld shared cache\n");
         return 0;
     }
 
diff --git a/testing/test-cases/_dyld_register_for_image_loads.dtest/foo.c b/testing/test-cases/_dyld_register_for_image_loads.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_for_image_loads.dtest/main.cxx b/testing/test-cases/_dyld_register_for_image_loads.dtest/main.cxx
new file mode 100644 (file)
index 0000000..3ed7f41
--- /dev/null
@@ -0,0 +1,101 @@
+
+// BUILD:  $CC  foo.c -dynamiclib  -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD:  $CXX main.cxx -o $BUILD_DIR/dyld_register_test.exe $BUILD_DIR/libfoo.dylib -DRUN_DIR="$RUN_DIR"
+// BUILD:  $CC  foo.c -dynamiclib  -install_name $RUN_DIR/libfoo2.dylib -o $BUILD_DIR/libfoo2.dylib
+// BUILD:  $CC  foo.c -bundle -o $BUILD_DIR/foo.bundle
+
+// 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 "C" void foo();
+
+extern mach_header __dso_handle;
+
+static std::unordered_set<const mach_header*> sCurrentImages;
+
+static void notify(const mach_header* mh, const char* path, bool unloadable)
+{
+    fprintf(stderr, "mh=%p, path=%s, unloadable=%d\n", mh, path, unloadable);
+    if ( sCurrentImages.count(mh) != 0 ) {
+        printf("[FAIL] _dyld_register_for_image_loads: notified twice about %p\n", mh);
+        exit(0);
+    }
+    sCurrentImages.insert(mh);
+
+    const char* leaf = strrchr(path, '/');
+    if ( unloadable ) {
+        if ( (strcmp(leaf, "/libfoo2.dylib") != 0) && (strcmp(leaf, "/foo.bundle") != 0) ) {
+            printf("[FAIL] _dyld_register_for_image_loads: image incorrectly marked unloadable %p %s\n", mh, path);
+            exit(0);
+        }
+    }
+    else {
+        if ( (strcmp(leaf, "/libfoo2.dylib") == 0) || (strcmp(leaf, "/foo.bundle") == 0) ) {
+            printf("[FAIL] _dyld_register_for_image_loads: image incorrectly marked as not unloadable %p %s\n", mh, path);
+            exit(0);
+        }
+    }
+}
+
+
+int main()
+{
+    printf("[BEGIN] _dyld_register_for_image_loads\n");
+
+    _dyld_register_for_image_loads(&notify);
+
+    // verify we were notified about already loaded images
+    if ( sCurrentImages.count(&__dso_handle) == 0 ) {
+               printf("[FAIL] _dyld_register_for_image_loads() did not notify us about main executable\n");
+               exit(0);
+    }
+    const mach_header* libSysMH = dyld_image_header_containing_address((void*)&printf);
+    if ( sCurrentImages.count(libSysMH) == 0 ) {
+               printf("[FAIL] _dyld_register_for_image_loads() did not notify us about libsystem_c.dylib\n");
+               exit(0);
+    }
+    const mach_header* libFoo = dyld_image_header_containing_address((void*)&foo);
+    if ( sCurrentImages.count(libFoo) == 0 ) {
+        printf("[FAIL] _dyld_register_for_image_loads() did not notify us about libfoo.dylib\n");
+        exit(0);
+    }
+
+    // verify we were notified about load of libfoo2.dylib
+       void* handle2 = dlopen(RUN_DIR "/libfoo2.dylib", RTLD_FIRST);
+       if ( handle2 == NULL ) {
+               printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/libfoo.dylib", dlerror());
+               exit(0);
+       }
+    const void* libfoo2Foo = dlsym(handle2, "foo");
+    const mach_header* libfoo2MH = dyld_image_header_containing_address(libfoo2Foo);
+    if ( sCurrentImages.count(libfoo2MH) == 0 ) {
+               printf("[FAIL] _dyld_register_for_image_loads() did not notify us about libfoo2.dylib\n");
+               exit(0);
+    }
+
+    // verify we were notified about load of foo.bundle
+    void* handleB = dlopen(RUN_DIR "/foo.bundle", RTLD_FIRST);
+    if ( handleB == NULL ) {
+        printf("[FAIL] dlopen(\"%s\") failed with: %s\n", RUN_DIR "/foo.bundle", dlerror());
+        exit(0);
+    }
+    const void* libfooBFoo = dlsym(handle2, "foo");
+    const mach_header* libfooB = dyld_image_header_containing_address(libfooBFoo);
+    if ( sCurrentImages.count(libfooB) == 0 ) {
+        printf("[FAIL] _dyld_register_for_image_loads() did not notify us about foo.bundle\n");
+        exit(0);
+    }
+
+
+
+    printf("[PASS] _dyld_register_for_image_loads\n");
+    return 0;
+}
+
diff --git a/testing/test-cases/cwd-relative-load.dtest/foo.c b/testing/test-cases/cwd-relative-load.dtest/foo.c
new file mode 100644 (file)
index 0000000..723758f
--- /dev/null
@@ -0,0 +1 @@
+int foo = 42;
diff --git a/testing/test-cases/cwd-relative-load.dtest/main.c b/testing/test-cases/cwd-relative-load.dtest/main.c
new file mode 100644 (file)
index 0000000..8f2042f
--- /dev/null
@@ -0,0 +1,28 @@
+
+
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name libfoo.dylib
+// BUILD:  $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/cwd-load.exe
+// BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/cwd-load.exe
+
+// RUN:  ./cwd-load.exe
+
+// libfoo.dylib is loaded from the current directory (not an absolute path)
+
+
+#include <stdio.h>
+
+extern int foo;
+
+
+int main()
+{
+    printf("[BEGIN] cwd-relative-load\n");
+    if ( foo == 42 )
+        printf("[PASS] cwd-relative-load\n");
+    else
+        printf("[FAIL] cwd-relative-load, wrong value\n");
+
+       return 0;
+}
+
+
index eeeabca8a57ffca2e34d18ac2ea6d4d4bf0a92a6..2552006ebebe8f2e7fcbcbfb77a66aedd64dea62 100644 (file)
@@ -9,6 +9,10 @@
 #include <dlfcn.h> 
 #include <mach-o/dyld_priv.h>
 
+#if __has_feature(ptrauth_calls)
+    #include <ptrauth.h>
+#endif
+
 
 int bar()
 {
@@ -25,6 +29,14 @@ __attribute__((visibility("hidden"))) int hide()
     return 4;
 }
 
+static const void *stripPointer(const void *ptr) {
+#if __has_feature(ptrauth_calls)
+    return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
+#else
+    return ptr;
+#endif
+}
+
 // checks global symbol
 static void verifybar()
 {
@@ -37,7 +49,7 @@ static void verifybar()
         printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"bar\"\n", info.dli_sname);
         exit(0);
     }
-    if ( info.dli_saddr != &bar) {
+    if ( info.dli_saddr != stripPointer(&bar) ) {
         printf("[FAIL] dladdr()->dli_saddr is not &bar\n");
         exit(0);
     }
@@ -59,7 +71,7 @@ static void verifyfoo()
         printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"foo\"\n", info.dli_sname);
         exit(0);
     }
-    if ( info.dli_saddr != &foo) {
+    if ( info.dli_saddr != stripPointer(&foo) ) {
         printf("[FAIL] dladdr()->dli_saddr is not &foo\n");
         exit(0);
     }
@@ -81,7 +93,7 @@ static void verifyhide()
         printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"hide\"\n", info.dli_sname);
         exit(0);
     }
-    if ( info.dli_saddr != &hide) {
+    if ( info.dli_saddr != stripPointer(&hide) ) {
         printf("[FAIL] dladdr()->dli_saddr is not &hide\n");
         exit(0);
     }
@@ -103,7 +115,7 @@ static void verifymalloc()
         printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"malloc\"\n", info.dli_sname);
         exit(0);
     }
-    if ( info.dli_saddr != &malloc) {
+    if ( info.dli_saddr != stripPointer(&malloc) ) {
         printf("[FAIL] dladdr()->dli_saddr is not &malloc\n");
         exit(0);
     }
@@ -113,6 +125,19 @@ static void verifymalloc()
     }
 }
 
+// checks passing NULL for info parameter gracefully fails
+static void verifyNULL()
+{
+    Dl_info info;
+    if ( dladdr(&malloc, NULL) != 0 ) {
+        printf("[FAIL] dladdr(&malloc, NULL) did not fail\n");
+        exit(0);
+    }
+    if ( dladdr(NULL, NULL) != 0 ) {
+        printf("[FAIL] dladdr(NULL, NULL) did not fail\n");
+        exit(0);
+    }
+}
 
 int main()
 {
@@ -121,7 +146,7 @@ int main()
     verifyhide();
     verifyfoo();
     verifymalloc();
-
+    verifyNULL();
 
     printf("[PASS] dladdr-basic\n");
     return 0;
index e45b27f7d22233334b07e2a59dfaaa0003424587..92f7b0ba4f72e594a318c48a0e45b9c23e22cb2e 100644 (file)
@@ -5,9 +5,23 @@
 #include <string.h> 
 #include <dlfcn.h> 
 #include <mach-o/dyld_priv.h>
+#if __has_feature(ptrauth_calls)
+    #include <ptrauth.h>
+#endif
 
 extern void* __dso_handle;
 
+
+static const void* stripPointer(const void* ptr)
+{
+#if __has_feature(ptrauth_calls)
+    return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
+#else
+    return ptr;
+#endif
+}
+
+
 int dylib_bar()
 {
     return 2;
@@ -35,7 +49,7 @@ static void verifybar()
         printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_bar\"\n", info.dli_sname);
         exit(0);
     }
-    if ( info.dli_saddr != &dylib_bar) {
+    if ( info.dli_saddr != stripPointer(&dylib_bar) ) {
         printf("[FAIL] dladdr()->dli_saddr is not &dylib_bar\n");
         exit(0);
     }
@@ -57,7 +71,7 @@ static void verifyfoo()
         printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_foo\"\n", info.dli_sname);
         exit(0);
     }
-    if ( info.dli_saddr != &dylib_foo) {
+    if ( info.dli_saddr != stripPointer(&dylib_foo) ) {
         printf("[FAIL] dladdr()->dli_saddr is not &dylib_foo\n");
         exit(0);
     }
@@ -69,7 +83,7 @@ static void verifyfoo()
 
 // checks hidden symbol
 static void verifyhide()
-{
+{ 
     Dl_info info;
     if ( dladdr(&dylib_hide, &info) == 0 ) {
         printf("[FAIL] dladdr(&dylib_hide, xx) failed\n");
@@ -79,7 +93,7 @@ static void verifyhide()
         printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_hide\"\n", info.dli_sname);
         exit(0);
     }
-    if ( info.dli_saddr != &dylib_hide) {
+    if ( info.dli_saddr != stripPointer(&dylib_hide) ) {
         printf("[FAIL] dladdr()->dli_saddr is not &dylib_hide\n");
         exit(0);
     }
index 5d9d0c937aa3feddfe9ca977efb8f1568b94b41c..a4401abf3d38e306da8c2891f925a999a9d1c3ca 100644 (file)
@@ -1,19 +1,33 @@
 
 // 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
+// BUILD:  $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dladdr-dylib.exe
 
-// RUN:  ./dladdr-basic.exe
+// RUN:  ./dladdr-dylib.exe
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h> 
 #include <dlfcn.h> 
 #include <mach-o/dyld_priv.h>
+#if __has_feature(ptrauth_calls)
+    #include <ptrauth.h>
+#endif
 
 extern void* __dso_handle;
 
 extern void verifyDylib();
 
+
+static const void* stripPointer(const void* ptr)
+{
+#if __has_feature(ptrauth_calls)
+    return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
+#else
+    return ptr;
+#endif
+}
+
+
 int bar()
 {
     return 2;
@@ -41,7 +55,7 @@ static void verifybar()
         printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"bar\"\n", info.dli_sname);
         exit(0);
     }
-    if ( info.dli_saddr != &bar) {
+    if ( info.dli_saddr != stripPointer(&bar) ) {
         printf("[FAIL] dladdr()->dli_saddr is not &bar\n");
         exit(0);
     }
@@ -63,7 +77,7 @@ static void verifyfoo()
         printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"foo\"\n", info.dli_sname);
         exit(0);
     }
-    if ( info.dli_saddr != &foo) {
+    if ( info.dli_saddr != stripPointer(&foo) ) {
         printf("[FAIL] dladdr()->dli_saddr is not &foo\n");
         exit(0);
     }
@@ -85,7 +99,7 @@ static void verifyhide()
         printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"hide\"\n", info.dli_sname);
         exit(0);
     }
-    if ( info.dli_saddr != &hide) {
+    if ( info.dli_saddr != stripPointer(&hide) ) {
         printf("[FAIL] dladdr()->dli_saddr is not &hide\n");
         exit(0);
     }
@@ -107,7 +121,7 @@ static void verifymalloc()
         printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"malloc\"\n", info.dli_sname);
         exit(0);
     }
-    if ( info.dli_saddr != &malloc) {
+    if ( info.dli_saddr != stripPointer(&malloc) ) {
         printf("[FAIL] dladdr()->dli_saddr is not &malloc\n");
         exit(0);
     }
@@ -120,7 +134,7 @@ static void verifymalloc()
 
 int main()
 {
-    printf("[BEGIN] dladdr-basic\n");
+    printf("[BEGIN] dladdr-dylib\n");
     verifybar();
     verifyhide();
     verifyfoo();
@@ -128,7 +142,7 @@ int main()
 
     verifyDylib();
 
-    printf("[PASS] dladdr-basic\n");
+    printf("[PASS] dladdr-dylib\n");
     return 0;
 }
 
diff --git a/testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/foo1.c b/testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/foo1.c
new file mode 100644 (file)
index 0000000..5f56e2b
--- /dev/null
@@ -0,0 +1,15 @@
+
+
+int __attribute__((weak)) coalA = 1;
+int __attribute__((weak)) coalB = 1;
+
+int foo1_coalA()
+{
+    return coalA;
+}
+
+int foo1_coalB()
+{
+    return coalB;
+}
+
diff --git a/testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/foo2.c b/testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/foo2.c
new file mode 100644 (file)
index 0000000..ea75d41
--- /dev/null
@@ -0,0 +1,20 @@
+
+
+int __attribute__((weak)) coalA = 2;
+int __attribute__((weak)) coalB = 2;
+int __attribute__((weak)) coalC = 2;
+
+int foo2_coalA()
+{
+    return coalA;
+}
+
+int foo2_coalB()
+{
+    return coalB;
+}
+
+int foo2_coalC()
+{
+    return coalC;
+}
diff --git a/testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/foo3.c b/testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/foo3.c
new file mode 100644 (file)
index 0000000..76ad019
--- /dev/null
@@ -0,0 +1,20 @@
+
+
+int __attribute__((weak)) coalA = 3;
+int __attribute__((weak)) coalB = 3;
+int __attribute__((weak)) coalC = 3;
+
+int foo3_coalA()
+{
+    return coalA;
+}
+
+int foo3_coalB()
+{
+    return coalB;
+}
+
+int foo3_coalC()
+{
+    return coalC;
+}
diff --git a/testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/main.c b/testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/main.c
new file mode 100644 (file)
index 0000000..8c91cb5
--- /dev/null
@@ -0,0 +1,122 @@
+
+// BUILD:  $CC foo1.c -dynamiclib  -install_name $RUN_DIR/libfoo1.dylib -o $BUILD_DIR/libfoo1.dylib
+// BUILD:  $CC foo2.c -dynamiclib  -install_name $RUN_DIR/libfoo2.dylib -o $BUILD_DIR/libfoo2.dylib
+// BUILD:  $CC foo3.c -dynamiclib  -install_name $RUN_DIR/libfoo3.dylib -o $BUILD_DIR/libfoo3.dylib
+// BUILD:  $CC main.c -o $BUILD_DIR/dlopen-RTLD_LOCAL-coalesce.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN:  ./dlopen-RTLD_LOCAL-coalesce.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+
+
+///
+/// This tests the interaction of RTLD_LOCAL and weak-def coalescing.
+///
+/// Normally, (for correct C++ ODR), dyld coalesces all weak-def symbols
+/// across all images, so that only one copy of each weak symbol name
+/// is in use.  But, dlopen with RTLD_LOCAL means to "hide" the symbols in
+/// that one (top) image being loaded. 
+///
+///
+
+//      main                *coalA
+//      libfoo1.dylib        coalA *coalB
+//      libfoo2.dylib        coalA  coalB  coalC        // loaded with RTLD_LOCAL
+//      libfoo3.dylib        coalA  coalB *coalC
+//
+
+typedef int (*IntProc)(void);
+
+int __attribute__((weak)) coalA = 0;
+
+int main()
+{
+    printf("[BEGIN] dlopen-RTLD_LOCAL-coalesce\n");
+
+    ///
+    /// Load three foo dylibs in order
+    ///
+    void* handle1 = dlopen(RUN_DIR "/libfoo1.dylib", RTLD_GLOBAL);
+    void* handle2 = dlopen(RUN_DIR "/libfoo2.dylib", RTLD_LOCAL);
+    void* handle3 = dlopen(RUN_DIR "/libfoo3.dylib", RTLD_GLOBAL);
+    if ( handle1 == NULL ) {
+        printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: dlopen(libfoo1.dylib, RTLD_GLOBAL) failed but it should have worked: %s\n", dlerror());
+        return 0;
+    }
+    if ( handle2 == NULL ) {
+        printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: dlopen(libfoo2.dylib, RTLD_LOCAL) failed but it should have worked: %s\n", dlerror());
+        return 0;
+    }
+    if ( handle3 == NULL ) {
+        printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: dlopen(libfoo3.dylib, RTLD_GLOBAL) failed but it should have worked: %s\n", dlerror());
+        return 0;
+    }
+
+    
+    ///
+    /// Get accessor functions
+    ///
+    IntProc foo1_coalA = (IntProc)dlsym(handle1, "foo1_coalA");
+    IntProc foo1_coalB = (IntProc)dlsym(handle1, "foo1_coalB");
+    IntProc foo2_coalA = (IntProc)dlsym(handle2, "foo2_coalA");
+    IntProc foo2_coalB = (IntProc)dlsym(handle2, "foo2_coalB");
+    IntProc foo2_coalC = (IntProc)dlsym(handle2, "foo2_coalC");
+    IntProc foo3_coalA = (IntProc)dlsym(handle3, "foo3_coalA");
+    IntProc foo3_coalB = (IntProc)dlsym(handle3, "foo3_coalB");
+    IntProc foo3_coalC = (IntProc)dlsym(handle3, "foo3_coalC");
+    if ( !foo1_coalA || !foo1_coalB ||
+         !foo2_coalA || !foo2_coalB || !foo2_coalC ||
+         !foo3_coalA || !foo3_coalB || !foo3_coalC ) {
+        printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: dlsym() failed\n");
+        return 0;
+    }
+
+    ///
+    /// Get values for each coal[ABC] seen in each image
+    ///
+    int foo1A = (*foo1_coalA)();
+    int foo1B = (*foo1_coalB)();
+    int foo2A = (*foo2_coalA)();
+    int foo2B = (*foo2_coalB)();
+    int foo2C = (*foo2_coalC)();
+    int foo3A = (*foo3_coalA)();
+    int foo3B = (*foo3_coalB)();
+    int foo3C = (*foo3_coalC)();
+    printf("coalA in main:    %d\n", coalA);
+    printf("coalA in libfoo1: %d\n", foo1A);
+    printf("coalA in libfoo2: %d\n", foo2A);
+    printf("coalA in libfoo3: %d\n", foo3A);
+
+    printf("coalB in libfoo1: %d\n", foo1B);
+    printf("coalB in libfoo2: %d\n", foo2B);
+    printf("coalB in libfoo3: %d\n", foo3B);
+
+    printf("coalC in libfoo2: %d\n", foo2C);
+    printf("coalC in libfoo3: %d\n", foo3C);
+
+
+
+    ///
+    /// Verify coalescing was done properly (foo2 was skipped because of RTLD_LOCAL)
+    ///
+    if ( (foo1A != 0) || (foo2A != 0) || (foo3A != 0) || (coalA != 0) ) {
+        printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: coalA was not coalesced properly\n");
+        return 0;
+    }
+    if ( (foo1B != 1) || (foo2B != 1) || (foo3B != 1) ) {
+        printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: coalB was not coalesced properly\n");
+        return 0;
+    }
+    if ( (foo2C != 2) || (foo3C != 3) ) {
+        printf("[FAIL] dlopen-RTLD_LOCAL-coalesce: coalC was not coalesced properly\n");
+        return 0;
+    }
+
+
+
+    printf("[PASS] dlopen-RTLD_LOCAL-coalesce\n");
+    return 0;
+}
diff --git a/testing/test-cases/dlopen-RTLD_LOCAL-hides.dtest/bar.c b/testing/test-cases/dlopen-RTLD_LOCAL-hides.dtest/bar.c
new file mode 100644 (file)
index 0000000..698dc6c
--- /dev/null
@@ -0,0 +1,5 @@
+int bar()
+{
+    return 11;
+}
+
diff --git a/testing/test-cases/dlopen-RTLD_LOCAL-hides.dtest/foo.c b/testing/test-cases/dlopen-RTLD_LOCAL-hides.dtest/foo.c
new file mode 100644 (file)
index 0000000..13457f2
--- /dev/null
@@ -0,0 +1,5 @@
+int foo()
+{
+    return 10;
+}
+
diff --git a/testing/test-cases/dlopen-RTLD_LOCAL-hides.dtest/main.c b/testing/test-cases/dlopen-RTLD_LOCAL-hides.dtest/main.c
new file mode 100644 (file)
index 0000000..79c113a
--- /dev/null
@@ -0,0 +1,74 @@
+
+// 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 -o $BUILD_DIR/dlopen-RTLD_LOCAL-hides.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN:  ./dlopen-RTLD_LOCAL-hides.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+
+
+int main()
+{
+    printf("[BEGIN] dlopen-RTLD_LOCAL-hides\n");
+
+    ///
+    /// This tests that RTLD_LOCAL prevents RTLD_DEFAULT from finding symbols, but can be found via handle
+    ///
+    void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LOCAL);
+    if ( handle == NULL ) {
+        printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlopen(libfoo.dylib, RTLD_LOCAL) failed but it should have worked: %s\n", dlerror());
+        return 0;
+    }
+    void* sym = dlsym(handle, "foo");
+    if ( sym == NULL ) {
+        printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlsym(handle, \"foo\") failed but it should have worked: %s\n", dlerror());
+        return 0;
+    }
+    void* sym2 = dlsym(RTLD_DEFAULT, "foo");
+    if ( sym2 != NULL ) {
+        printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlsym(RTLD_DEFAULT, \"foo\") succeeded but it should have failed\n");
+        return 0;
+    }
+
+
+    ///
+    /// This tests that RTLD_GLOBAL after RTLD_LOCAL allows RTLD_DEFAULT to find symbols
+    ///
+    void* handle2 = dlopen(RUN_DIR "/libfoo.dylib", RTLD_GLOBAL);
+    if ( handle2 == NULL ) {
+        printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlopen(libfoo.dylib, RTLD_GLOBAL) failed but it should have worked: %s\n", dlerror());
+        return 0;
+    }
+    void* sym3 = dlsym(RTLD_DEFAULT, "foo");
+    if ( sym3 == NULL ) {
+        printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlsym(RTLD_DEFAULT, \"foo\") failed after upgrading to RTLD_GLOBAL\n");
+        return 0;
+    }
+
+
+    ///
+    /// This tests that RTLD_LOCAL after RTLD_GLOBAL does not block RTLD_DEFAULT from finding symbols
+    ///
+    void* handle3 = dlopen(RUN_DIR "/libbar.dylib", RTLD_GLOBAL);
+    if ( handle3 == NULL ) {
+        printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlopen(libbar.dylib, RTLD_GLOBAL) failed but it should have worked: %s\n", dlerror());
+        return 0;
+    }
+    void* handle4 = dlopen(RUN_DIR "/libbar.dylib", RTLD_LOCAL);
+    if ( handle4 == NULL ) {
+        printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlopen(libbar.dylib, RTLD_LOCAL) failed but it should have worked: %s\n", dlerror());
+        return 0;
+    }
+    void* sym4 = dlsym(RTLD_DEFAULT, "bar");
+    if ( sym4 == NULL ) {
+        printf("[FAIL] dlopen-RTLD_LOCAL-hides: dlsym(RTLD_DEFAULT, \"bar\") failed but it should have worked: %s\n", dlerror());
+        return 0;
+    }
+
+    printf("[PASS] dlopen-RTLD_LOCAL-hides\n");
+    return 0;
+}
diff --git a/testing/test-cases/dlopen-RTLD_NODELETE.dtest/bar.c b/testing/test-cases/dlopen-RTLD_NODELETE.dtest/bar.c
new file mode 100644 (file)
index 0000000..95ef2ab
--- /dev/null
@@ -0,0 +1,4 @@
+
+
+int bar = 11;
+
diff --git a/testing/test-cases/dlopen-RTLD_NODELETE.dtest/foo.c b/testing/test-cases/dlopen-RTLD_NODELETE.dtest/foo.c
new file mode 100644 (file)
index 0000000..def52cd
--- /dev/null
@@ -0,0 +1,3 @@
+
+int foo = 10;
+
diff --git a/testing/test-cases/dlopen-RTLD_NODELETE.dtest/main.c b/testing/test-cases/dlopen-RTLD_NODELETE.dtest/main.c
new file mode 100644 (file)
index 0000000..3fff117
--- /dev/null
@@ -0,0 +1,78 @@
+
+// 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 -o $BUILD_DIR/dlopen-RTLD_NODELETE.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN:  ./dlopen-RTLD_NODELETE.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+
+
+int main()
+{
+    printf("[BEGIN] dlopen-RTLD_NODELETE\n");
+
+    ///
+    /// This tests that RTLD_NODELETE on first dlopen() blocks dlclose() from unloading image
+    ///
+    void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_NODELETE);
+    if ( handle == NULL ) {
+        printf("[FAIL] dlopen-RTLD_NODELETE: dlopen(libfoo.dylib, RTLD_NODELETE) failed but it should have worked: %s\n", dlerror());
+        return 0;
+    }
+    int* fooSym = (int*)dlsym(handle, "foo");
+    if ( fooSym == NULL ) {
+        printf("[FAIL] dlopen-RTLD_NODELETE: dlsym(handle, \"foo\") failed but it should have worked: %s\n", dlerror());
+        return 0;
+    }
+    int fooValue = *fooSym;
+    dlclose(handle);
+    Dl_info info;
+    if ( dladdr(fooSym, &info) != 0 ) {
+        printf("[FAIL] dlopen-RTLD_NODELETE: dladdr(fooSym, xx) succeeded as if libfoo.dylib was not unloaded\n");
+        return 0;
+    }
+    // dereference foo pointer.  If RTLD_NODELETE worked, this will not crash
+    if ( *fooSym != fooValue ) {
+        printf("[FAIL] dlopen-RTLD_NODELETE: value at fooSym changed\n");
+        return 0;
+    }
+
+    ///
+    /// This tests that RTLD_NODELETE on later dlopen() blocks dlclose() from unloading image
+    ///
+    void* handle2 = dlopen(RUN_DIR "/libbar.dylib", RTLD_GLOBAL);
+    if ( handle2 == NULL ) {
+        printf("[FAIL] dlopen-RTLD_NODELETE: dlopen(libfoo.dylib, RTLD_GLOBAL) failed but it should have worked: %s\n", dlerror());
+        return 0;
+    }
+    int* barSym = (int*)dlsym(handle2, "bar");
+    if ( barSym == NULL ) {
+        printf("[FAIL] dlopen-RTLD_NODELETE: dlsym(handle, \"bar\") failed but it should have worked: %s\n", dlerror());
+        return 0;
+    }
+    int barValue = *barSym;
+    void* handle3 = dlopen(RUN_DIR "/libbar.dylib", RTLD_NODELETE);
+    if ( handle3 == NULL ) {
+        printf("[FAIL] dlopen-RTLD_NODELETE: dlopen(libfoo.dylib, RTLD_NODELETE) failed but it should have worked: %s\n", dlerror());
+        return 0;
+    }
+    dlclose(handle2);
+    dlclose(handle3);
+    if ( dladdr(barSym, &info) != 0 ) {
+        printf("[FAIL] dlopen-RTLD_NODELETE: dladdr(barSym, xx) succeeded as if libbar.dylib was not unloaded\n");
+        return 0;
+    }
+    // dereference foo pointer.  If RTLD_NODELETE worked, this will not crash
+    if ( *barSym != barValue ) {
+        printf("[FAIL] dlopen-RTLD_NODELETE: value at barSym changed\n");
+        return 0;
+    }
+
+
+    printf("[PASS] dlopen-RTLD_NODELETE\n");
+    return 0;
+}
diff --git a/testing/test-cases/dlopen-atpath-restricted.dtest/bar.c b/testing/test-cases/dlopen-atpath-restricted.dtest/bar.c
new file mode 100644 (file)
index 0000000..e425999
--- /dev/null
@@ -0,0 +1 @@
+void bar() {}
diff --git a/testing/test-cases/dlopen-atpath-restricted.dtest/foo.c b/testing/test-cases/dlopen-atpath-restricted.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-atpath-restricted.dtest/main.c b/testing/test-cases/dlopen-atpath-restricted.dtest/main.c
new file mode 100644 (file)
index 0000000..bd55fb4
--- /dev/null
@@ -0,0 +1,80 @@
+// BUILD_ONLY: MacOSX
+
+// BUILD:  mkdir -p $BUILD_DIR/test1
+// BUILD:  $CC bar.c -dynamiclib -o $BUILD_DIR/test1/libtest1.dylib -install_name @rpath/libtest1.dylib
+// BUILD:  $CC foo.c -bundle -o $BUILD_DIR/test1.bundle -Wl,-rpath,@loader_path/test1/ $BUILD_DIR/test1/libtest1.dylib
+
+// BUILD:  mkdir -p $BUILD_DIR/test2
+// BUILD:  $CC bar.c -dynamiclib -o $BUILD_DIR/test2/libtest2.dylib -install_name @loader_path/test2/libtest2.dylib
+// BUILD:  $CC foo.c -bundle -o $BUILD_DIR/test2.bundle $BUILD_DIR/test2/libtest2.dylib
+
+// BUILD:  mkdir -p $BUILD_DIR/test3
+// BUILD:  $CC bar.c -dynamiclib -o $BUILD_DIR/test3/libtest3.dylib -install_name @rpath/libtest3.dylib
+// BUILD:  $CC foo.c -bundle -o $BUILD_DIR/test3.bundle -Wl,-rpath,$RUN_DIR/test3  $BUILD_DIR/test3/libtest3.dylib 
+
+// BUILD:  mkdir -p $BUILD_DIR/test4
+// BUILD:  $CC bar.c -dynamiclib -o $BUILD_DIR/test4/libtest4.dylib -install_name @rpath/libtest4.dylib
+// BUILD:  $CC foo.c -bundle -o $BUILD_DIR/test4.bundle -Wl,-rpath,@executable_path/test4/ $BUILD_DIR/test4/libtest4.dylib
+
+// BUILD:  mkdir -p $BUILD_DIR/test5
+// BUILD:  $CC bar.c -dynamiclib -o $BUILD_DIR/test5/libtest5.dylib -install_name @executable_path/test5/libtest5.dylib
+// BUILD:  $CC foo.c -bundle -o $BUILD_DIR/test5.bundle $BUILD_DIR/test5/libtest5.dylib
+
+
+
+// BUILD:  $CC main.c -o $BUILD_DIR/dlopen-restricted.exe -DRUN_DIR="$RUN_DIR" -sectcreate __RESTRICT __restrict /dev/null
+
+// RUN:  ./dlopen-restricted.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+
+int main(int argc, const char* argv[])
+{
+    printf("[BEGIN] dlopen-restricted\n");
+
+    // test1: LC_RPATH not in main executable uses @loader_path
+    void* handle = dlopen(RUN_DIR "/test1.bundle", RTLD_LAZY);
+    if ( handle == NULL ) {
+        printf("dlerror(): %s\n", dlerror());
+        printf("[FAIL]  dlopen-restricted test1.bundle\n");
+        return 0;
+    }
+
+    // test2: @loader_path not in main executable
+    handle = dlopen(RUN_DIR "/test2.bundle", RTLD_LAZY);
+    if ( handle == NULL ) {
+        printf("dlerror(): %s\n", dlerror());
+        printf("[FAIL]  dlopen-restricted test2.bundle\n");
+        return 0;
+    }
+
+    // test3: LC_RPATH not in main executable uses absolute path
+    handle = dlopen(RUN_DIR "/test3.bundle", RTLD_LAZY);
+    if ( handle == NULL ) {
+        printf("dlerror(): %s\n", dlerror());
+        printf("[FAIL]  dlopen-restricted test3.bundle\n");
+        return 0;
+    }
+
+    // test4: [SHOULD FAIL] LC_RPATH not in main executable uses @executable_path
+    handle = dlopen(RUN_DIR "/test4.bundle", RTLD_LAZY);
+    if ( handle != NULL ) {
+        printf("[FAIL]  dlopen-restricted test4.bundle dlopen() should not work\n");
+        return 0;
+    }
+
+    // test5: [SHOULD FAIL] @executable_path in LC_LOAD_DYLIB
+    handle = dlopen(RUN_DIR "/test5.bundle", RTLD_LAZY);
+    if ( handle != NULL ) {
+        printf("[FAIL]  dlopen-restricted test5.bundle dlopen() should not work\n");
+        return 0;
+    }
+
+
+
+    printf("[PASS]  dlopen-restricted\n");
+       return 0;
+}
+
index e506236520c9a3450496626211371caa27f40ccc..ddecb89bb8b401eb322accbfed82204f92597e6b 100644 (file)
@@ -28,7 +28,7 @@ static void tryImage(const char* path)
        
        int result = dlclose(handle);
        if ( result != 0 ) {
-        printf("dlclose() returned %c\n", result);
+        printf("dlclose() returned %d, dlerror()=%s\n", result, dlerror());
         printf("[FAIL] dlopen-basic %s\n", path);
                return;
        }
index 194b6aff1500fa636887f5cd9e91b495516455c7..d6aed69459723d6ae5050e766fbc16d914400cea 100644 (file)
@@ -3,7 +3,7 @@
 // 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
+// RUN:  ./dlopen-flat.exe
 
 #include <stdio.h>
 #include <dlfcn.h>
@@ -17,8 +17,7 @@ int main() {
        // Foo exports foo()
        void* fooHandle = 0;
        {
-               const char* path = RUN_DIR "/libfoo.dylib";
-               fooHandle = dlopen(path, RTLD_LAZY);
+               fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
                if (!fooHandle) {
                        printf("dlopen failed with error: %s\n", dlerror());
                        return 1;
@@ -39,8 +38,7 @@ int main() {
 
        // Open foo again which should do something.
        {
-               const char* path = RUN_DIR "/libfoo.dylib";
-               fooHandle = dlopen(path, RTLD_LAZY);
+               fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
                if (!fooHandle) {
                        printf("dlopen failed with error: %s\n", dlerror());
                        return 1;
@@ -55,8 +53,7 @@ int main() {
        // Bar is going to resolve foo()
        void* barHandle = 0;
        {
-               const char* path = RUN_DIR "/libbar.dylib";
-               barHandle = dlopen(path, RTLD_LAZY);
+               barHandle = dlopen(RUN_DIR "/libbar.dylib", RTLD_LAZY);
                if (!barHandle) {
                        printf("dlopen failed with error: %s\n", dlerror());
                        return 1;
@@ -77,8 +74,7 @@ int main() {
 
        // Open foo again which shouldn't do anything.
        {
-               const char* path = RUN_DIR "/libfoo.dylib";
-               fooHandle = dlopen(path, RTLD_LAZY);
+               fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
                if (!fooHandle) {
                        printf("dlopen failed with error: %s\n", dlerror());
                        return 1;
diff --git a/testing/test-cases/dlopen-haswell.dtest/a.c b/testing/test-cases/dlopen-haswell.dtest/a.c
new file mode 100644 (file)
index 0000000..d312ffb
--- /dev/null
@@ -0,0 +1,10 @@
+#include <stdbool.h>
+
+bool isHaswell()
+{
+#if __x86_64h__
+    return true;
+#else
+    return false;
+#endif
+}
diff --git a/testing/test-cases/dlopen-haswell.dtest/main.c b/testing/test-cases/dlopen-haswell.dtest/main.c
new file mode 100644 (file)
index 0000000..a76b9c5
--- /dev/null
@@ -0,0 +1,64 @@
+// BUILD_ONLY: MacOSX
+
+// BUILD:  $CC a.c -dynamiclib -arch x86_64h -o $BUILD_DIR/libHaswellCheck.dylib -install_name $RUN_DIR/libHaswellCheck.dylib
+// BUILD:  $CC main.c -o $BUILD_DIR/dlopen-haswell.exe -DRUN_DIR="$RUN_DIR"
+
+// RUN:  ./dlopen-haswell.exe
+
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <dlfcn.h>
+#include <mach/host_info.h>
+#include <mach/mach.h>
+#include <mach/mach_host.h>
+
+typedef bool (*BoolFunc)(void);
+
+
+bool isHaswell_dynamic()
+{
+    struct host_basic_info info;
+    mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
+    mach_port_t hostPort = mach_host_self();
+    kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count);
+    mach_port_deallocate(mach_task_self(), hostPort);
+    if ( result == KERN_SUCCESS ) {
+        return (info.cpu_subtype == CPU_SUBTYPE_X86_64_H);
+    }
+    return false;
+}
+
+
+int main(int arg, const char* argv[])
+{
+    printf("[BEGIN] dlopen-haswell\n");
+
+    void* handle = dlopen(RUN_DIR "/libHaswellCheck.dylib", RTLD_LAZY);
+    if ( handle == NULL ) {
+        printf("dlerror(): %s\n", dlerror());
+        printf("[FAIL] dlopen-haswell dlopen\n");
+        return 0;
+    }
+
+    BoolFunc libFunc = (BoolFunc)dlsym(handle, "isHaswell");
+    if ( libFunc == NULL ) {
+        printf("dlerror(): %s\n", dlerror());
+        printf("[FAIL] dlopen-haswell dlsym\n");
+        return 0;
+    }
+
+    // check if haswell slice of libHaswellCheck.dylib was loaded on haswell machines
+    bool dylibIsHaswellSlice = (*libFunc)();
+    bool runtimeIsHaswell = isHaswell_dynamic();
+
+       if ( dylibIsHaswellSlice != runtimeIsHaswell )
+        printf("[FAIL] dlopen-haswell, dylibIsHaswellSlice=%d, runtimeIsHaswell=%d\n", dylibIsHaswellSlice, runtimeIsHaswell);
+       else
+        printf("[PASS] dlopen-haswell\n");
+
+       return 0;
+}
+
+
+
diff --git a/testing/test-cases/dlopen-haswell/a.c b/testing/test-cases/dlopen-haswell/a.c
new file mode 100644 (file)
index 0000000..c02be2d
--- /dev/null
@@ -0,0 +1 @@
+void zzz() {}
diff --git a/testing/test-cases/dlopen-in-init.dtest/foo.c b/testing/test-cases/dlopen-in-init.dtest/foo.c
new file mode 100644 (file)
index 0000000..5f91679
--- /dev/null
@@ -0,0 +1,36 @@
+
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <dispatch/dispatch.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <assert.h>
+#include <unistd.h>
+
+
+static void* work1(void* arg)
+{
+    void* h = dlopen("System/Library/Frameworks/Foundation.framework/Foundation", 0);
+
+    return NULL;
+}
+
+
+__attribute__((constructor))
+void myinit()
+{
+    pthread_t workerThread;
+
+    if ( pthread_create(&workerThread, NULL, work1, NULL) != 0 ) {
+        printf("[FAIL]  dlopen-in-init, pthread_create\n");
+        return;
+    }
+
+    void* dummy;
+    pthread_join(workerThread, &dummy);
+}
+
diff --git a/testing/test-cases/dlopen-in-init.dtest/main.c b/testing/test-cases/dlopen-in-init.dtest/main.c
new file mode 100644 (file)
index 0000000..d96489e
--- /dev/null
@@ -0,0 +1,29 @@
+
+
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib
+// BUILD:  $CC main.c -o $BUILD_DIR/dlopen-in-init.exe $BUILD_DIR/libfoo.dylib
+
+// RUN:  ./dlopen-in-init.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+
+
+__attribute__((constructor))
+void myinit()
+{
+
+}
+
+
+int main()
+{
+    printf("[BEGIN] dlopen-in-init\n");
+
+    // The test is for libdyld.dylib to not crash when libfoo.dylib dlopen() stuff in its initializer
+
+    printf("[PASS]  dlopen-in-init\n");
+       return 0;
+}
+
index b34e0088040840ce095be5eeeabcf76105894d8c..c622e9f4257a5660fdf40b22a33c3aba71112883 100644 (file)
@@ -2,7 +2,7 @@
 // BUILD:  $CC main.c -o $BUILD_DIR/dlopen-realpath.exe
 // BUILD:  cd $BUILD_DIR && ln -s ./IOKit.framework/IOKit IOKit && ln -s /System/Library/Frameworks/IOKit.framework IOKit.framework
 
-// RUN:  ./dlopen-realpath.exe
+// RUN:  DYLD_FALLBACK_LIBRARY_PATH=/baz  ./dlopen-realpath.exe
 
 #include <stdio.h>
 #include <dlfcn.h>
@@ -20,7 +20,7 @@ static void tryImage(const char* path)
 
        int result = dlclose(handle);
        if ( result != 0 ) {
-        printf("dlclose() returned %c\n", result);
+        printf("dlclose(%p): %s\n", handle, dlerror());
         printf("[FAIL] dlopen-realpath %s\n", path);
                return;
        }
@@ -34,7 +34,15 @@ int main()
 {
        tryImage("./IOKit.framework/IOKit");
        tryImage("./././IOKit/../IOKit.framework/IOKit");
-        tryImage("./IOKit");
+    tryImage("./IOKit");
+
+    // Also try libSystem which has an alias in the OS to /usr/lib/libSystem.B.dylib
+    tryImage("/usr/lib/libSystem.dylib");
+
+    // Also try using non-canonical path
+    // This requires DYLD_FALLBACK_LIBRARY_PATH to be disabled, otherwise it is found that way
+    tryImage("//usr/lib/libSystem.dylib");
+    tryImage("/usr/./lib/libSystem.dylib");
 
        return 0;
 }
diff --git a/testing/test-cases/dlopen-rpath-from-dylib.dtest/bar.c b/testing/test-cases/dlopen-rpath-from-dylib.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-rpath-from-dylib.dtest/main.c b/testing/test-cases/dlopen-rpath-from-dylib.dtest/main.c
new file mode 100644 (file)
index 0000000..ab6936e
--- /dev/null
@@ -0,0 +1,29 @@
+
+// BUILD:  mkdir -p $BUILD_DIR/dir
+// BUILD:  $CC bar.c  -dynamiclib -install_name @rpath/libbar.dylib -o $BUILD_DIR/dir/libbar.dylib
+// BUILD:  $CC test.c -dynamiclib -install_name $RUN_DIR/libtest.dylib -o $BUILD_DIR/libtest.dylib -rpath @loader_path/dir
+// BUILD:  $CC main.c -o $BUILD_DIR/dlopen-rpath-from-dylib.exe $BUILD_DIR/libtest.dylib 
+
+// RUN:  ./dlopen-rpath-from-dylib.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdbool.h>
+
+
+/// test that a call to dlopen() from a dylib uses the LC_RPATH from that dylib
+
+extern bool test_dlopen();
+
+int main()
+{
+    printf("[BEGIN] dlopen-rpath-from-dylib\n");
+
+    if ( test_dlopen() )
+        printf("[PASS]  dlopen-rpath-from-dylib\n");
+    else
+        printf("[FAIL]  dlopen-rpath-from-dylib\n");
+
+       return 0;
+}
+
diff --git a/testing/test-cases/dlopen-rpath-from-dylib.dtest/test.c b/testing/test-cases/dlopen-rpath-from-dylib.dtest/test.c
new file mode 100644 (file)
index 0000000..dba4287
--- /dev/null
@@ -0,0 +1,19 @@
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdbool.h>
+
+bool test_dlopen()
+{
+    void* handle = dlopen("@rpath/libbar.dylib", RTLD_LAZY);
+    if ( handle == NULL ) {
+        printf("[FAIL] dlopen-rpath-from-dylib: dlopen(@rpath/libbar.dylib) failed: %s\n", dlerror());
+        return false;
+    }
+
+    dlclose(handle);
+
+    return true;
+}
+
+
diff --git a/testing/test-cases/dlopen-rpath-implicit.dtest/foo.c b/testing/test-cases/dlopen-rpath-implicit.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-rpath-implicit.dtest/main.c b/testing/test-cases/dlopen-rpath-implicit.dtest/main.c
new file mode 100644 (file)
index 0000000..6d43d7f
--- /dev/null
@@ -0,0 +1,30 @@
+
+// BUILD:  mkdir -p $BUILD_DIR/dir1
+// BUILD:  $CC foo.c -dynamiclib -install_name @rpath/libimplicitrpath.dylib -o $BUILD_DIR/dir1/libimplicitrpath.dylib
+// BUILD:  $CC main.c -o $BUILD_DIR/dlopen-rpath-implicit.exe -rpath @loader_path/dir1
+
+// RUN:  ./dlopen-rpath-implicit.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+
+/// test that a leaf name passed to dlopen() searches the rpath
+
+int main()
+{
+    printf("[BEGIN] dlopen-rpath-implicit\n");
+
+    void* handle = dlopen("libimplicitrpath.dylib", RTLD_LAZY);
+    if ( handle == NULL ) {
+        printf("[FAIL] dlopen-rpath-implicit: dlopen(libimplicitrpath.dylib) failed: %s\n", dlerror());
+        return 0;
+    }
+
+    dlclose(handle);
+
+    printf("[PASS] dlopen-rpath-implicit\n");
+
+       return 0;
+}
+
diff --git a/testing/test-cases/dlopen-rpath-prev-override.dtest/bad.c b/testing/test-cases/dlopen-rpath-prev-override.dtest/bad.c
new file mode 100644 (file)
index 0000000..08b8b6a
--- /dev/null
@@ -0,0 +1,10 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+
+__attribute__((constructor))
+void init()
+{
+    printf("[FAIL]  dlopen-rpath-prev-override\n");
+    exit(0);
+}
diff --git a/testing/test-cases/dlopen-rpath-prev-override.dtest/dyn.c b/testing/test-cases/dlopen-rpath-prev-override.dtest/dyn.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/dlopen-rpath-prev-override.dtest/foo.c b/testing/test-cases/dlopen-rpath-prev-override.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-rpath-prev-override.dtest/good.c b/testing/test-cases/dlopen-rpath-prev-override.dtest/good.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/dlopen-rpath-prev-override.dtest/main.c b/testing/test-cases/dlopen-rpath-prev-override.dtest/main.c
new file mode 100644 (file)
index 0000000..8aa206f
--- /dev/null
@@ -0,0 +1,33 @@
+
+// BUILD:  mkdir -p $BUILD_DIR/dir $BUILD_DIR/good $BUILD_DIR/bad
+// BUILD:  $CC good.c -dynamiclib -install_name @rpath/libtest.dylib     -o $BUILD_DIR/good/libtest.dylib
+// BUILD:  $CC bad.c  -dynamiclib -install_name @rpath/libtest.dylib     -o $BUILD_DIR/bad/libtest.dylib
+// BUILD:  $CC dyn.c  -dynamiclib -install_name @rpath/libdynamic.dylib  -o $BUILD_DIR/dir/libdynamic.dylib $BUILD_DIR/good/libtest.dylib -rpath @loader_path/../bad
+// BUILD:  $CC main.c $BUILD_DIR/good/libtest.dylib -DRUN_DIR="$RUN_DIR" -o $BUILD_DIR/dlopen-rpath-prev-override.exe -rpath @loader_path/good
+
+// RUN:  ./dlopen-rpath-prev-override.exe
+
+// Processing of @rpath in dlopen()s always checks existing loaded images before using LC_RPATHs to find images on disk
+//
+// main links with @rpath/libtest.dylib found via -rpath @loader_path/good
+// main dlopen()s libdynamic.dylib which links with @rpath/libtest.dylib and has LR_PATH to find it in bad/,
+// but since it was already loaded from good/, that one should be re-used.
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+int main()
+{
+    printf("[BEGIN] dlopen-rpath-prev-override\n");
+
+       void* handle = dlopen(RUN_DIR "/dir/libdynamic.dylib", RTLD_LAZY);
+       if ( handle == NULL ) {
+        printf("dlerror(): %s\n", dlerror());
+        printf("[FAIL]  dlopen-rpath-prev-override\n");
+               return 0;
+       }
+
+    printf("[PASS]  dlopen-rpath-prev-override\n");
+       return 0;
+}
+
diff --git a/testing/test-cases/dlopen-rpath-prev.dtest/foo.c b/testing/test-cases/dlopen-rpath-prev.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-rpath-prev.dtest/main.c b/testing/test-cases/dlopen-rpath-prev.dtest/main.c
new file mode 100644 (file)
index 0000000..67741d0
--- /dev/null
@@ -0,0 +1,30 @@
+
+// BUILD:  mkdir -p $BUILD_DIR/dir1 $BUILD_DIR/dir2
+// BUILD:  $CC sub1.c -dynamiclib -install_name @rpath/librpathstatic.dylib -o $BUILD_DIR/dir1/librpathstatic.dylib
+// BUILD:  $CC sub2.c -dynamiclib -install_name @rpath/libdynamic.dylib     -o $BUILD_DIR/dir2/libdynamic.dylib $BUILD_DIR/dir1/librpathstatic.dylib
+// BUILD:  $CC foo.c  -dynamiclib -install_name $RUN_DIR/libstatic.dylib    -o $BUILD_DIR/libstatic.dylib -rpath @loader_path/dir1 $BUILD_DIR/dir1/librpathstatic.dylib
+// BUILD:  $CC main.c $BUILD_DIR/libstatic.dylib -DRUN_DIR="$RUN_DIR"       -o $BUILD_DIR/dlopen-rpath-prev.exe
+
+// RUN:  ./dlopen-rpath-prev.exe
+
+// main links with libstatic.dylib which uses rpath to link with dir1/librpathstatic.dylib
+// main dlopen()s libdynamic.dylib which links with dir1/librpathstatic.dylib, but has no rpath for it and depends on it being already loaded
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+int main()
+{
+    printf("[BEGIN] dlopen-rpath-prev\n");
+
+       void* handle = dlopen(RUN_DIR "/dir2/libdynamic.dylib", RTLD_LAZY);
+       if ( handle == NULL ) {
+        printf("dlerror(): %s\n", dlerror());
+        printf("[FAIL]  dlopen-rpath-prev\n");
+               return 0;
+       }
+
+    printf("[PASS]  dlopen-rpath-prev\n");
+       return 0;
+}
+
diff --git a/testing/test-cases/dlopen-rpath-prev.dtest/sub1.c b/testing/test-cases/dlopen-rpath-prev.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/dlopen-rpath-prev.dtest/sub2.c b/testing/test-cases/dlopen-rpath-prev.dtest/sub2.c
new file mode 100644 (file)
index 0000000..852b89b
--- /dev/null
@@ -0,0 +1,5 @@
+int sub2()
+{
+       return 2;
+}
+
index 237dbd18b383e1c8d312938ba937e1fc77d4fe73..09f7e5c7e8329af58409af57fbdb4a65664abc9d 100644 (file)
@@ -1,3 +1,5 @@
+// BUILD_ONLY: MacOSX
+
 // 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
@@ -20,8 +22,9 @@ int main()
     DYLD_TESTING_CALLBACK();
 
     if (!DYLD_TESTING_CALLBACK_ENABLED())
-        printf("[FAIL] !DYLD_TESTING_CALLBACK_ENABLED\n");
+        printf("[FAIL] dtrace: DYLD_TESTING_CALLBACK_ENABLED() returned false\n");
+    else
+        printf("[PASS] dtrace\n");
 
-    printf("[PASS] dtrace\n");
        return 0;
 }
index e01234dd4f0ceb3a7b8ce1c3d9c8c3f7da1a3a96..9807f0960f4f452e9a0ef741c0615c52bba0e527 100644 (file)
@@ -1,11 +1,14 @@
 
 // BUILD:  $CC foo.c -dynamiclib -install_name /cant/find/me.dylib -o $BUILD_DIR/libmissing.dylib
 // BUILD:  $CC emptyMain.c $BUILD_DIR/libmissing.dylib  -o $BUILD_DIR/prog_missing_dylib.exe
-// BUILD:  $CC defSymbol.c -dynamiclib -install_name libMissingSymbols.dylib -o $BUILD_DIR/libMissingSymbols.dylib
-// BUILD:  $CC defSymbol.c -dynamiclib -install_name libMissingSymbols.dylib -o $BUILD_DIR/libHasSymbols.dylib -DHAS_SYMBOL
+// BUILD:  $CC defSymbol.c -dynamiclib -install_name $RUN_DIR/libMissingSymbols.dylib -o $BUILD_DIR/libMissingSymbols.dylib
+// BUILD:  $CC defSymbol.c -dynamiclib -install_name $RUN_DIR/libMissingSymbols.dylib -o $BUILD_DIR/libHasSymbols.dylib -DHAS_SYMBOL
 // BUILD:  $CC useSymbol.c $BUILD_DIR/libHasSymbols.dylib -o $BUILD_DIR/prog_missing_symbol.exe
 // BUILD:  $CC main.c -o $BUILD_DIR/dyld_abort_tests.exe
 
+// NO_CRASH_LOG: prog_missing_dylib.exe
+// NO_CRASH_LOG: prog_missing_symbol.exe
+
 // RUN:  ./dyld_abort_tests.exe 
 
 #include <stdio.h>
@@ -44,16 +47,17 @@ static void childDied(int sig)
     info.eri_reason_buf_size = OS_REASON_BUFFER_MAX_SIZE;
     info.eri_kcd_buf = (user_addr_t)packReasonData;
     //fprintf(stderr, "info=%p\n", &info);
-    if ( proc_pidinfo(sChildPid, PROC_PIDEXITREASONINFO, 1, &info, PROC_PIDEXITREASONINFO_SIZE) != sizeof(struct proc_exitreasoninfo) ) {
-        printf("bad return size from proc_pidinfo()\n");
+    int procResult = proc_pidinfo(sChildPid, PROC_PIDEXITREASONINFO, 1, &info, PROC_PIDEXITREASONINFO_SIZE);
+    if ( procResult != sizeof(struct proc_exitreasoninfo) ) {
+        printf("bad return size from proc_pidinfo(), %d expected %lu\n", procResult, PROC_PIDEXITREASONINFO_SIZE);
         return;
     }
     if ( info.eri_namespace != OS_REASON_DYLD ) {
-        printf("eri_namespace != OS_REASON_DYLD\n");
+        printf("eri_namespace (%d) != OS_REASON_DYLD\n", info.eri_namespace);
         return;
     }
     if ( info.eri_code != sExpectedDyldReason ) {
-        printf("eri_code != %lld\n", sExpectedDyldReason);
+        printf("eri_code (%llu) != %lld\n", sExpectedDyldReason, info.eri_code);
         return;
     }
     kcdata_iter_t iter = kcdata_iter(packReasonData, info.eri_reason_buf_size);
@@ -88,7 +92,7 @@ static void childDied(int sig)
     if ( sExpectedDylibPath != NULL ) {
         if ( dyldInfo->targetDylibPathOffset != 0 ) {
             const char* targetDylib = (char*)dyldInfo + dyldInfo->targetDylibPathOffset;
-            if ( strcmp(sExpectedDylibPath, targetDylib) != 0 ) {
+            if ( strstr(targetDylib, sExpectedDylibPath) == NULL ) {
                 printf("dylib path (%s) not what expected (%s)\n", targetDylib, sExpectedDylibPath);
                 return;
             }
diff --git a/testing/test-cases/dyld_get_image_versions.dtest/main.c b/testing/test-cases/dyld_get_image_versions.dtest/main.c
new file mode 100644 (file)
index 0000000..3a726b4
--- /dev/null
@@ -0,0 +1,40 @@
+
+// BUILD:  $CC main.c  -o $BUILD_DIR/dyld_get_image_versions.exe
+
+// RUN:  ./dyld_get_image_versions.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_image_versions\n");
+
+    // should succeed
+    dyld_get_image_versions(&__dso_handle, ^(dyld_platform_t platform, uint32_t sdkVersion, uint32_t minOS) {
+        printf("main binary: platform=%d, sdk=0x%08X, minOS-0x%08X\n", platform, sdkVersion, minOS);
+    });
+
+    uint8_t badFile[4096];
+    struct mach_header* mh = (struct mach_header*)badFile;
+    mh->magic       = MH_MAGIC_64;
+    mh->ncmds       = 1;
+    mh->filetype    = MH_DYLIB;
+    mh->sizeofcmds  = 40;
+    struct load_command* lc = (struct load_command*)&badFile[32];
+    lc->cmd         = 1;
+    lc->cmdsize     = 4000; // bad load command size
+
+    // should detect the mh is bad and not crash or call the callback
+    dyld_get_image_versions(mh, ^(dyld_platform_t platform, uint32_t sdkVersion, uint32_t minOS) {
+        printf("bad binary: platform=%d, sdk=0x%08X, minOS-0x%08X\n", platform, sdkVersion, minOS);
+    });
+
+
+    printf("[PASS] dyld_get_image_versions\n");
+       return 0;
+}
+
index efa9be110c21991b8ee47322b16027072d69dade..c5032a0e9f75b6c491ccf5b295f8feefc4f23a9d 100644 (file)
@@ -26,8 +26,24 @@ int main()
         return 0;
     }
 
-    printf("[PASS] dyld_get_sdk_version\n");
 
+#if TARGET_OS_WATCH
+    uint32_t iosVersion = dyld_get_program_sdk_version();
+    uint32_t watchOSVersion = dyld_get_program_sdk_watch_os_version();
+    if (iosVersion != (watchOSVersion + 0x00070000)) {
+        printf("[FAIL] dyld_get_program_sdk_watch_os_version\n");
+        return 0;
+    }
+#endif
+#if TARGET_OS_BRIDGE
+    uint32_t iosVersion = dyld_get_program_sdk_version();
+    uint32_t bridgeOSVersion = dyld_get_program_sdk_bridge_os_version();
+    if (bridgeOSVersion != (watchOSVersion + 0x00090000)) {
+        printf("[FAIL] dyld_get_program_sdk_watch_os_version\n");
+        return 0;
+    }
+#endif
+    printf("[PASS] dyld_get_sdk_version\n");
        return 0;
 }
 
index 41c5ccbbb4ff1a0420389866d6eeeda04e99a6a5..89943ed202a2ef91436fa65c949fcdccfd17a760 100644 (file)
@@ -12,6 +12,9 @@
 #include <signal.h>
 #include <spawn.h>
 #include <errno.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <sys/types.h>
 #include <mach/mach.h>
 #include <mach/machine.h>
 #include <mach-o/dyld_process_info.h>
@@ -108,13 +111,14 @@ static bool hasCF(task_t task, bool launchedSuspended)
     bool valueSaysLaunchedSuspended = (stateInfo.dyldState == dyld_process_state_not_started);
     if ( valueSaysLaunchedSuspended != launchedSuspended ) {
         printf("[FAIL] dyld_process_info suspend state mismatch\n");
+        _dyld_process_info_release(info);
         return false;
     }
 
     __block bool foundDyld = false;
     _dyld_process_info_for_each_image(info, ^(uint64_t machHeaderAddress, const uuid_t uuid, const char* path) {
         //fprintf(stderr, "0x%llX %s\n", machHeaderAddress, path);
-        if ( strstr(path, "/usr/lib/dyld") != NULL )
+        if ( strstr(path, "/dyld") != NULL )
             foundDyld = true;
     });
 
@@ -126,6 +130,7 @@ static bool hasCF(task_t task, bool launchedSuspended)
             if ( strstr(path, "/linksWithCF.exe") != NULL )
                 foundMain = true;
        });
+        _dyld_process_info_release(info);
         return foundMain && foundDyld;
     }
 
@@ -141,6 +146,37 @@ static bool hasCF(task_t task, bool launchedSuspended)
     return foundCF && foundDyld;
 }
 
+static void checkForLeaks(const char *name) {
+    printf("[BEGIN] %s checkForLeaks\n", name);
+    pid_t child;
+    int stat_loc;
+    char buffer[PAGE_SIZE];
+    (void)snprintf(&buffer[0], 128, "%d", getpid());
+
+    const char* argv[] = { "/usr/bin/leaks", buffer, NULL };
+    int psResult = posix_spawn(&child, "/usr/bin/leaks", NULL, NULL, (char**)argv, environ);
+    if ( psResult != 0 ) {
+        printf("[FAIL] %s checkForLeaks posix_spawn failed, err=%d\n", name, psResult);
+        exit(0);
+    }
+
+    (void)wait4(child, &stat_loc, 0, NULL);
+    ssize_t readBytes = 0;
+    if (WIFEXITED(stat_loc) == 0) {
+        printf("[FAIL] %s checkForLeaks leaks did not exit\n", name);
+        exit(0);
+    }
+    if (WEXITSTATUS(stat_loc) == 1) {
+        printf("[FAIL] %s checkForLeaks found leaks\n", name);
+        exit(0);
+    }
+    if (WEXITSTATUS(stat_loc) != 0) {
+        printf("[FAIL] %s checkForLeaks leaks errored out\n", name);
+        exit(0);
+    }
+    printf("[PASS] %s checkForLeaks\n", name);
+}
+
 
 int main(int argc, const char* argv[])
 {
@@ -202,5 +238,7 @@ int main(int argc, const char* argv[])
     }
 
     printf("[PASS] dyld_process_info\n");
+    checkForLeaks("dyld_process_info");
+
        return 0;
 }
index d76d44f5fdc4035454c988a39c87c2640f0094d8..ff1466f2c79565dd860675ef27153a3500b6645f 100644 (file)
@@ -135,12 +135,8 @@ static bool monitor(struct task_and_pid tp, bool disconnectEarly, bool attachLat
                                                     dispatch_semaphore_signal(taskDone);
                                                 }
                                             }
-                                            //fprintf(stderr, "unload=%d, 0x%012llX <%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> %s\n",
-                                            //   unload, machHeader, uuid[0],  uuid[1],  uuid[2],  uuid[3],  uuid[4],  uuid[5],  uuid[6],  uuid[7],
-                                            //                        uuid[8],  uuid[9],  uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15], path);
                                           },
                                           ^{
-                                            //fprintf(stderr, "target exited\n");
                                             gotTerminationNotice = true;
                                             dispatch_semaphore_signal(taskDone);
                                           },
@@ -239,6 +235,10 @@ 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];
+    // This loop goes through 10 iterations
+    // i = 0..7 Should succeed
+    // i = 8 Should fail,  but trigger a release that frees up a slot
+    // i = 9 Should succeed
     for (int i=0; i < 10; ++i) {
         kern_return_t kr;
         handles[i] = _dyld_process_info_notify(tp.task, serviceQueue,
@@ -274,9 +274,28 @@ static void validateMaxNotifies(struct task_and_pid tp)
     dispatch_release(serviceQueue);
 }
 
+static bool testSelfAttach(void) {
+    __block bool retval = false;
+    kern_return_t kr = KERN_SUCCESS;
+    dispatch_queue_t serviceQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
+    dyld_process_info_notify handle = _dyld_process_info_notify(mach_task_self(), serviceQueue,
+                                       ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) {
+                                           if ( strstr(path, "/libfoo.dylib") != NULL ) {
+                                               retval = true;
+                                           }
+                                       },
+                                       ^{},
+                                       &kr);
+    if ( handle == NULL ) {
+        fprintf(stderr, "_dyld_process_info_notify() returned NULL, result=%d\n", kr);
+    }
+    void* h = dlopen("./libfoo.dylib", 0);
+    dlclose(h);
+    return retval;
+}
+
 int main(int argc, const char* argv[])
 {
-    printf("[BEGIN] dyld_process_info_notify\n");
     if ( argc < 2 ) {
         printf("[FAIL] dyld_process_info_notify missing argument\n");
         exit(0);
@@ -287,6 +306,7 @@ int main(int argc, const char* argv[])
         struct task_and_pid child;
 
         // test 1) launch test program suspended in same arch as this program
+        printf("[BEGIN] dyld_process_info_notify laucnh suspended (same arch)\n");
         child = launchTest(testProgPath, "", false, true);
         if ( ! monitor(child, false, false) ) {
             printf("[FAIL] dyld_process_info_notify launch suspended missed some notifications\n");
@@ -294,19 +314,23 @@ int main(int argc, const char* argv[])
             exit(0);
         }
         killTest(child);
+        printf("[PASS] dyld_process_info_notify laucnh suspended (same arch)\n");
 
         // test 2) launch test program in same arch as this program where it sleeps itself
+        printf("[BEGIN] dyld_process_info_notify laucnh suspend-in-main (same arch)\n");
         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");
-             killTest(child);
+            killTest(child);
             exit(0);
         }
         killTest(child);
+        printf("[PASS] dyld_process_info_notify laucnh suspend-in-main (same arch)\n");
 
 #if __MAC_OS_X_VERSION_MIN_REQUIRED
         // test 3) launch test program suspended in opposite arch as this program
+        printf("[BEGIN] dyld_process_info_notify laucnh suspended (other arch)\n");
         child = launchTest(testProgPath, "", true, true);
         if ( ! monitor(child, false, false) ) {
             printf("[FAIL] dyld_process_info_notify launch suspended other arch missed some notifications\n");
@@ -314,8 +338,10 @@ int main(int argc, const char* argv[])
             exit(0);
         }
         killTest(child);
+        printf("[PASS] dyld_process_info_notify laucnh suspended (other arch)\n");
 
         // test 4) launch test program in opposite arch as this program where it sleeps itself
+        printf("[BEGIN] dyld_process_info_notify laucnh suspend-in-main (other arch)\n");
         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");
@@ -323,9 +349,11 @@ int main(int argc, const char* argv[])
             exit(0);
         }
         killTest(child);
+        printf("[PASS] dyld_process_info_notify laucnh suspend-in-main (other arch)\n");
 #endif
 
         // test 5) launch test program where we disconnect from it after first dlopen
+        printf("[BEGIN] dyld_process_info_notify disconnect\n");
         child = launchTest(testProgPath, "", false, true);
         if ( ! monitor(child, true, false) ) {
             printf("[FAIL] dyld_process_info_notify connect/disconnect missed some notifications\n");
@@ -333,8 +361,15 @@ int main(int argc, const char* argv[])
             exit(0);
         }
         killTest(child);
+        printf("[PASS] dyld_process_info_notify disconnect\n");
+
+        // test 6) attempt to monitor the monitoring process
+        printf("[BEGIN] dyld_process_info_notify self-attach\n");
+        if (! testSelfAttach() ) {
+            printf("[FAIL] dyld_process_info_notify self notification\n");
+        }
+        printf("[PASS] dyld_process_info_notify self-attach\n");
 
-        printf("[PASS] dyld_process_info_notify\n");
         exit(0);
     });
 
diff --git a/testing/test-cases/dyld_version_spis.dtest/main.c b/testing/test-cases/dyld_version_spis.dtest/main.c
new file mode 100644 (file)
index 0000000..f5c77ce
--- /dev/null
@@ -0,0 +1,90 @@
+
+// BUILD:  $CC main.c  -o $BUILD_DIR/versions.exe
+
+// RUN:  ./versions.exe
+
+#include <stdio.h>
+#include <string.h>
+#include <mach-o/dyld_priv.h>
+
+extern struct mach_header __dso_handle;
+
+int main()
+{
+    printf("[BEGIN] dyld_version_spi\n");
+    dyld_platform_t active = dyld_get_active_platform();
+    dyld_platform_t base = dyld_get_base_platform(active);
+    dyld_build_version_t absoluteMin = { .platform = base, .version = 0 };
+    dyld_build_version_t absoluteMax = { .platform = base, .version = 0xffffffff };
+
+#if TARGET_OS_OSX
+    if ( base != PLATFORM_MACOS ) {
+        printf("[FAIL] base.platform %u incorrect for macOS\n", base);
+        return 0;
+    }
+#elif TARGET_OS_IOS
+    if ( base != PLATFORM_IOS ) {
+        printf("[FAIL] base.platform %u incorrect for iOS\n", base);
+        return 0;
+    }
+#elif TARGET_OS_TV
+    if ( base != PLATFORM_TVOS ) {
+        printf("[FAIL] base.platform %u incorrect for tvOS\n", base);
+        return 0;
+    }
+#elif TARGET_OS_BRIDGE
+    if ( base != PLATFORM_BRIDGEOS ) {
+        printf("[FAIL] base.platform %u incorrect for wacthOS\n", base);
+        return 0;
+    }
+#elif TARGET_OS_WATCH
+    if ( base != PLATFORM_WATCHOS ) {
+        printf("[FAIL] base.platform %u incorrect for bridgeOS\n", base);
+        return 0;
+    }
+#else
+    printf("[FAIL] Running on unknown platform\n");
+    return 0;
+#endif
+
+#if TARGET_OS_SIMULATOR
+    if (dyld_is_simulator_platform(active) != true) {
+        printf("[FAIL] active platform %u should be a simulator\n", active);
+        return 0;
+    }
+#else
+    if (dyld_is_simulator_platform(active) == true) {
+        printf("[FAIL] active platform %u should not be a simulator\n", active);
+        return 0;
+    }
+#endif
+
+    if (dyld_is_simulator_platform(base) == true) {
+        printf("[FAIL] base platform %u should not be a simulator\n", base);
+        return 0;
+    }
+
+    if (!dyld_sdk_at_least(&__dso_handle, absoluteMin)) {
+        printf("[FAIL] executable sdk version should not < 1.0.0\n");
+        return 0;
+    }
+
+    if (dyld_sdk_at_least(&__dso_handle, absoluteMax)) {
+        printf("[FAIL] executable sdk version should not > 65536.0.0\n");
+        return 0;
+    }
+
+    if (!dyld_minos_at_least(&__dso_handle, absoluteMin)) {
+        printf("[FAIL] executable min version should not < 1.0.0\n");
+        return 0;
+    }
+
+    if (dyld_minos_at_least(&__dso_handle, absoluteMax)) {
+        printf("[FAIL] executable min version should not > 65536.0.0\n");
+        return 0;
+    }
+
+    printf("[PASS] dyld_version_spi\n");
+    return 0;
+}
+
index 06e73a797e695e266a6ddfd533be5a69f563aa1a..ea4d7f04141922eea5bb582217a3ffab41d48ec6 100644 (file)
@@ -1,8 +1,8 @@
 // BUILD_ONLY:      MacOSX
 // BUILD_MIN_OS:    10.5
-// BUILD:           $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
-// BUILD:           $CC foo.c -dynamiclib $BUILD_DIR/libbar.dylib -sub_library libbar -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
-// BUILD:           $CC main.c -o $BUILD_DIR/dylib-re-export.exe $BUILD_DIR/libfoo.dylib -L$BUILD_DIR
+// BUILD:           $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib -nostdlib -ldylib1.o
+// BUILD:           $CC foo.c -dynamiclib $BUILD_DIR/libbar.dylib -sub_library libbar -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib -nostdlib -ldylib1.o
+// BUILD:           $CC main.c -o $BUILD_DIR/dylib-re-export.exe $BUILD_DIR/libfoo.dylib -L$BUILD_DIR -nostdlib -lSystem -lcrt1.10.5.o
 
 // RUN:  ./dylib-re-export.exe
 
@@ -14,11 +14,11 @@ extern int bar();
 
 int main()
 {
-    printf("[BEGIN] dylib-re-export\n");
+    printf("[BEGIN] dylib-re-export-old-format\n");
     if ( bar() == 42 )
-        printf("[PASS] dylib-re-export\n");
+        printf("[PASS] dylib-re-export-old-format\n");
     else
-        printf("[FAIL] dylib-re-export, wrong value\n");
+        printf("[FAIL] dylib-re-export-old-format, wrong value\n");
 
        return 0;
 }
index 4e7aa2db08f8bceb76c5908f56b75e8b34e2e73a..2eb3393db5f7804730140b173dd6787b9d10b055 100644 (file)
@@ -1,8 +1,8 @@
 
 
-// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name libfoo.dylib
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib
 // BUILD:  $CC present.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dylib-static-present.exe
-// BUILD:  $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name libfoomissing.dylib
+// BUILD:  $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name $RUN_DIR/libfoomissing.dylib
 // BUILD:  $CC missing.c $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/dylib-static-missing.exe
 
 // RUN:  ./dylib-static-present.exe
index 1f4a6e207b223a5eeb5fdc32f666802a85eb15f7..f51e384c80fcf8e541e32eeacfbc6f50ad6c4d81 100644 (file)
@@ -1,6 +1,6 @@
-// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name libfoo.dylib
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib
 // BUILD:  $CC present.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dylib-static-weak-present.exe
-// BUILD:  $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name libfoomissing.dylib
+// BUILD:  $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name $RUN_DIR/libfoomissing.dylib
 // BUILD:  $CC missing.c $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/dylib-static-weak-missing.exe
 
 // RUN:  ./dylib-static-weak-present.exe
diff --git a/testing/test-cases/env-DYLD_IMAGE_SUFFIX.dtest/bar.c b/testing/test-cases/env-DYLD_IMAGE_SUFFIX.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/env-DYLD_IMAGE_SUFFIX.dtest/foo.c b/testing/test-cases/env-DYLD_IMAGE_SUFFIX.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_IMAGE_SUFFIX.dtest/main.c b/testing/test-cases/env-DYLD_IMAGE_SUFFIX.dtest/main.c
new file mode 100644 (file)
index 0000000..61346b8
--- /dev/null
@@ -0,0 +1,80 @@
+
+// BUILD:  mkdir -p $BUILD_DIR/Bar.framework
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib            -install_name $RUN_DIR/libfoo.dylib -DVALUE=1
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo_other.dylib      -install_name $RUN_DIR/libfoo.dylib -DVALUE=42
+// BUILD:  $CC bar.c -dynamiclib -o $BUILD_DIR/Bar.framework/Bar       -install_name $RUN_DIR/Bar.framework/Bar  -DVALUE=1
+// BUILD:  $CC bar.c -dynamiclib -o $BUILD_DIR/Bar.framework/Bar_alt   -install_name $RUN_DIR/Bar.framework/Bar  -DVALUE=42
+// BUILD:  $CC main.c            -o $BUILD_DIR/main.exe $BUILD_DIR/libfoo.dylib $BUILD_DIR/Bar.framework/Bar
+// BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main.exe
+// BUILD:  $CC main.c            -o $BUILD_DIR/main-dynamic.exe -DRUN_DIR="$RUN_DIR"
+// BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main-dynamic.exe
+
+// RUN:                                 ./main.exe
+// RUN:  DYLD_IMAGE_SUFFIX=_other       ./main.exe
+// RUN:  DYLD_IMAGE_SUFFIX=_alt         ./main.exe
+// RUN:  DYLD_IMAGE_SUFFIX=_alt:_other  ./main.exe
+// RUN:                                 ./main-dynamic.exe
+// RUN:  DYLD_IMAGE_SUFFIX=_other       ./main-dynamic.exe
+// RUN:  DYLD_IMAGE_SUFFIX=_alt         ./main-dynamic.exe
+// RUN:  DYLD_IMAGE_SUFFIX=_alt:_other  ./main-dynamic.exe
+
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+
+extern int foo();
+extern int bar();
+
+typedef int (*IntProc)();
+
+int main()
+{
+    const char* suffix = getenv("DYLD_IMAGE_SUFFIX");
+    if ( suffix == NULL )
+        suffix = "";
+    printf("[BEGIN] env-DYLD_IMAGE_SUFFIX-%s\n", suffix);
+
+    const int expectedFoo = (strstr(suffix, "_other") != NULL) ? 42 : 1;
+    const int expectedBar = (strstr(suffix, "_alt") != NULL) ? 42 : 1;;
+
+#ifdef RUN_DIR
+    void* fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY);
+    if ( fooHandle == NULL ) {
+        printf("[FAIL]  env-DYLD_IMAGE_SUFFIX-%s, libfoo.dylib could not be loaded, %s\n", suffix, dlerror());
+        return 0;
+    }
+    void* barHandle = dlopen(RUN_DIR "/Bar.framework/Bar", RTLD_LAZY);
+    if ( barHandle == NULL ) {
+        printf("[FAIL]  env-DYLD_IMAGE_SUFFIX-%s, Bar.framework/Bar could not be loaded, %s\n", suffix, dlerror());
+        return 0;
+    }
+    IntProc fooProc = (IntProc)dlsym(fooHandle, "foo");
+    if ( fooProc == NULL ) {
+        printf("[FAIL]  env-DYLD_IMAGE_SUFFIX-%s, symbol 'foo' not found %s\n", suffix, dlerror());
+        return 0;
+    }
+    IntProc barProc = (IntProc)dlsym(barHandle, "bar");
+    if ( barProc == NULL ) {
+        printf("[FAIL]  env-DYLD_IMAGE_SUFFIX-%s, symbol 'bar' not found %s\n", suffix, dlerror());
+        return 0;
+    }
+    int fooValue = (*fooProc)();
+    int barValue = (*barProc)();
+#else
+    int fooValue = foo();
+    int barValue = bar();
+#endif
+       if ( fooValue != expectedFoo )
+        printf("[FAIL]  env-DYLD_IMAGE_SUFFIX-%s, foo()=%d expected=%d\n", suffix, fooValue, expectedFoo);
+    else if ( barValue != expectedBar )
+        printf("[FAIL]  env-DYLD_IMAGE_SUFFIX-%s, bar()=%d expected=%d\n", suffix, barValue, expectedBar);
+    else
+        printf("[PASS]  env-DYLD_IMAGE_SUFFIX-%s\n", suffix);
+
+       return 0;
+}
+
diff --git a/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/main.c b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/main.c
new file mode 100644 (file)
index 0000000..37136fc
--- /dev/null
@@ -0,0 +1,33 @@
+
+// BUILD:  mkdir -p $BUILD_DIR/override
+// BUILD:  $CC myzlib.c -dynamiclib -o $BUILD_DIR/override/libz.1.dylib -install_name /usr/lib/libz.1.dylib -compatibility_version 1.0 -framework CoreFoundation
+// BUILD:  $CC main.c  -o $BUILD_DIR/main.exe -lz
+// BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main.exe
+
+// RUN:  ./main.exe
+// RUN:  DYLD_LIBRARY_PATH=$RUN_DIR/override/ ./main.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+#include <stdbool.h>
+
+// The test here is to override libz.1.dylib which is in the dyld cache with our own implementation.
+
+int main()
+{
+    bool expectMyDylib = (getenv("DYLD_LIBRARY_PATH") != NULL);
+
+    printf("[BEGIN] env-DYLD_LIBRARY_PATH-cache, %s\n", expectMyDylib ? "my" : "os");
+
+    bool usingMyDylib = (strcmp(zlibVersion(), "my") == 0);
+
+       if ( usingMyDylib == expectMyDylib )
+        printf("[PASS] env-DYLD_LIBRARY_PATH-cache, %s\n", expectMyDylib ? "my" : "os");
+    else
+        printf("[FAIL] env-DYLD_LIBRARY_PATH-cache, %s\n", expectMyDylib ? "my" : "os");
+
+       return 0;
+}
+
diff --git a/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/myzlib.c b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache.dtest/myzlib.c
new file mode 100644 (file)
index 0000000..5bd0c77
--- /dev/null
@@ -0,0 +1,4 @@
+const char* zlibVersion()
+{
+    return "my";
+}
index ceac97988525c22e66b84bb62054f4b2e4ca0dd4..a9e5c362af3a9b04b504306c2300978d9cfe01de 100644 (file)
@@ -3,22 +3,18 @@
 #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
+    // bump ptr allocate twice the size and fills with '#'
+    char* result = malloc(size*2);
+    memset(result, '#', size*2);
     return result;
 }
 
 void myfree(void* p)
 {
+    free(p);
 }
 
 DYLD_INTERPOSE(mymalloc, malloc)
diff --git a/testing/test-cases/no-shared-cache.dtest/main.c b/testing/test-cases/no-shared-cache.dtest/main.c
new file mode 100644 (file)
index 0000000..9407740
--- /dev/null
@@ -0,0 +1,31 @@
+// BUILD_ONLY: MacOSX
+
+// BUILD:  $CC main.c  -framework AppKit         -o $BUILD_DIR/no_shared_cache.exe
+
+// RUN:  DYLD_SHARED_REGION=avoid ./no_shared_cache.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h> 
+#include <mach-o/dyld_priv.h>
+#include <dlfcn.h>
+
+
+// This program links with AppKit which in dyld3 mode stress tests building closures when there is no dyld shared cache
+
+int main()
+{
+    printf("[BEGIN] no-shared-cache\n");
+
+    size_t cacheLen;
+    const void* cacheStart = _dyld_get_shared_cache_range(&cacheLen);
+
+    if ( cacheStart != NULL ) {
+        printf("[FAIL] no-shared-cache: _dyld_get_shared_cache_range() returned %p even though we are not using a dyld cache\n", cacheStart);
+        return 0;
+    }
+
+    printf("[PASS] no-shared-cache\n");
+    return 0;
+}
+
index d985e8ffefe9d69ea0fe7b1c247724db17ce1584..1773a03486de1593476477bb9313f3e8f2ac8065 100644 (file)
 // will turn around and call operator new in this main exectuable
 //
 
-static void* ptr;
+static void* myLastNewAllocation;
+static void* myLastDelete;
 
+// Note: this is not weak.  That is specifically suppported
 void* operator new(size_t s) throw (std::bad_alloc)
 {
-  ptr = malloc(s);
-  return ptr;
+  myLastNewAllocation = malloc(s);
+  return myLastNewAllocation;
+}
+
+struct Foo {
+    int  bytes[10];
+};
+
+// Note: this is weak and because it is in main executable should override OS
+__attribute__((weak))
+void operator delete(void* p) throw()
+{
+    myLastDelete = p;
+    ::free(p);
 }
 
 int main()
 {
        printf("[BEGIN] operator-new\n");
 
+    // test that OS's operator new[] redirects to my operator new
+    myLastNewAllocation = NULL;
     char* stuff = new char[24];
-    if ( (void*)stuff == ptr )
-        printf("[PASS] operator-new\n");
-    else
-        printf("[FAIL] operator-new\n");
+    if ( (void*)stuff != myLastNewAllocation ) {
+        printf("[FAIL] operator-new system array allocator not redirected through my operator new\n");
+        return 0;
+    }
+
+    // test that program uses my operator new
+    myLastNewAllocation = NULL;
+    Foo* foo = new Foo();
+    if ( (void*)foo != myLastNewAllocation ) {
+        printf("[FAIL] operator-new allocation not redirected though my operator new\n");
+        return 0;
+    }
+
+    //
+    delete foo;
+    if ( (void*)foo != myLastDelete ) {
+        printf("[FAIL] operator-new deallocation not redirected though my operator delete\n");
+        return 0;
+    }
 
+    printf("[PASS] operator-new\n");
        return 0;
 }
 
index 693de548b8b85acb089e6c71f17d38f4a932838e..1e08b62365ce355ea15b105d9bca83626be4268f 100644 (file)
@@ -9,6 +9,18 @@
 #include <mach-o/dyld_priv.h>
 #include <dlfcn.h>
 
+#if __has_feature(ptrauth_calls)
+    #include <ptrauth.h>
+#endif
+
+static const void *stripPointer(const void *ptr) {
+#if __has_feature(ptrauth_calls)
+    return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
+#else
+    return ptr;
+#endif
+}
+
 
 int main()
 {
@@ -41,7 +53,7 @@ int main()
         const void* cacheEnd = (char*)cacheStart + cacheLen;
 
         // verify malloc is in shared cache
-        if ( ((void*)&malloc < cacheStart) || ((void*)&malloc > cacheEnd) ) {
+        if ( (stripPointer((void*)&malloc) < cacheStart) || (stripPointer((void*)&malloc) > cacheEnd) ) {
             printf("[FAIL] shared_cache_range: malloc is outside range of cache\n");
             return 0;
         }
diff --git a/testing/test-cases/symbol-resolver-basic.dtest/foo.c b/testing/test-cases/symbol-resolver-basic.dtest/foo.c
new file mode 100644 (file)
index 0000000..0ff5b93
--- /dev/null
@@ -0,0 +1,25 @@
+#include <stdlib.h>
+
+
+static int foo_ten()
+{
+       return 10;
+}
+
+static int foo_zero()
+{
+       return 0;
+}
+
+
+// This foo is a "resolver" function that return the actual address of "foo"
+void* foo()
+{
+       __asm__(".symbol_resolver _foo");  // magic until we have compiler support
+       if ( getenv("TEN") != NULL )
+               return &foo_ten;
+       else
+               return &foo_zero;
+}
+
+
diff --git a/testing/test-cases/symbol-resolver-basic.dtest/foo2.c b/testing/test-cases/symbol-resolver-basic.dtest/foo2.c
new file mode 100644 (file)
index 0000000..e32024e
--- /dev/null
@@ -0,0 +1,11 @@
+
+#include <stdlib.h>
+
+extern int foo();
+
+// test that calls to resolver based function in same dylib work
+int fooPlusOne()
+{
+       return foo() + 1;
+}
+
diff --git a/testing/test-cases/symbol-resolver-basic.dtest/main.c b/testing/test-cases/symbol-resolver-basic.dtest/main.c
new file mode 100644 (file)
index 0000000..896221b
--- /dev/null
@@ -0,0 +1,36 @@
+
+// BUILD:  $CC foo.c foo2.c -dynamiclib  -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD:  $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/symbol-resolver.exe
+
+// RUN:  ./symbol-resolver.exe
+// RUN:  TEN=1 ./symbol-resolver.exe
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern int foo();
+extern int fooPlusOne();
+
+
+int main()
+{
+       if ( getenv("TEN") != NULL ) {
+               if ( foo() != 10 )
+                       printf("[FAIL] symbol-resolver-basic: foo() != 10\n");
+               else if ( fooPlusOne() != 11 )
+                       printf("[FAIL] symbol-resolver-basic: fooPlusOne() != 11\n");
+               else
+                       printf("[PASS] symbol-resolver-basic\n");
+       }
+       else {
+               if ( foo() != 0 )
+                       printf("[FAIL] symbol-resolver-basic: foo() != 0\n");
+               else if ( fooPlusOne() != 1 )
+                       printf("[FAIL] symbol-resolver-basic: fooPlusOne() != 1\n");
+               else
+            printf("[PASS] symbol-resolver-basic\n");
+       }
+  
+       return 0;
+}
diff --git a/testing/test-cases/weak-coalesce.dtest/Makefile b/testing/test-cases/weak-coalesce.dtest/Makefile
new file mode 100644 (file)
index 0000000..543a5be
--- /dev/null
@@ -0,0 +1,52 @@
+##
+# Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+# 
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+# 
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+# 
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+all-check: all check
+
+check:
+       ./main
+
+all: main
+
+main: main.c libfoo1.dylib libbase.dylib
+       ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo1.dylib libbase.dylib 
+
+libfoo1.dylib: foo1.c libfoo2.dylib libbase.dylib
+       ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libfoo1.dylib foo1.c libfoo2.dylib libbase.dylib 
+
+libfoo2.dylib: foo2.c libfoo3.dylib libbase.dylib
+       ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libfoo2.dylib foo2.c libfoo3.dylib libbase.dylib 
+
+libfoo3.dylib: foo3.c libbase.dylib
+       ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libfoo3.dylib foo3.c libbase.dylib -prebind -seg1addr 20000 
+
+libbase.dylib: base.c
+       ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libbase.dylib base.c -prebind -seg1addr 10000 
+
+
+
+clean:
+       ${RM} ${RMFLAGS} *~ main  libfoo1.dylib libfoo2.dylib libfoo3.dylib libbase.dylib
+
diff --git a/testing/test-cases/weak-coalesce.dtest/base.c b/testing/test-cases/weak-coalesce.dtest/base.c
new file mode 100644 (file)
index 0000000..b8a04c9
--- /dev/null
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <stdbool.h>
+
+#include "base.h"
+
+static bool         wasProblem        = false;
+static const char*     coal1Where        = NULL;
+static int*                    coal1Addr         = NULL;
+static int                     checkInCountCoal1 = 0;
+
+void baseVerifyCoal1(const char* where, int* addr)
+{
+       //fprintf(stderr, "baseVerifyCoal1(%s, %p)\n", where, addr);
+       ++checkInCountCoal1;
+       if ( coal1Where == NULL ) {
+               coal1Where = where;
+               coal1Addr = addr;
+       }
+       else {
+               if ( addr != coal1Addr ) {
+                       fprintf(stderr, "coal1 resolved to different locations.  %p in %s and %p in %s\n", 
+                               coal1Addr, coal1Where, addr, where);
+                       wasProblem = true;
+               }       
+       }
+}
+
+
+static const char*     coal2Where        = NULL;
+static int*                    coal2Addr         = NULL;
+static int                     checkInCountCoal2 = 0;
+
+void baseVerifyCoal2(const char* where, int* addr)
+{
+       //fprintf(stderr, "baseVerifyCoal2(%s, %p)\n", where, addr);
+       ++checkInCountCoal2;
+       if ( coal2Where == NULL ) {
+               coal2Where = where;
+               coal2Addr = addr;
+       }
+       else {
+               if ( addr != coal2Addr ) {
+                       fprintf(stderr, "coal2 resolved to different locations.  %p in %s and %p in %s\n", 
+                               coal2Addr, coal2Where, addr, where);
+                       wasProblem = true;
+               }       
+       }
+}
+
+
+
+void baseCheck()
+{
+       if ( wasProblem )
+        printf("[FAIL] weak-coalesce: was problem\n");
+    else if ( checkInCountCoal1 != 4 )
+        printf("[FAIL] weak-coalesce: checkInCountCoal1 != 4\n");
+    else if ( checkInCountCoal2 != 4 )
+        printf("[FAIL] weak-coalesce: checkInCountCoal2 != 2\n");
+       else
+        printf("[PASS] weak-coalesce\n");
+}
+
diff --git a/testing/test-cases/weak-coalesce.dtest/base.h b/testing/test-cases/weak-coalesce.dtest/base.h
new file mode 100644 (file)
index 0000000..0c03cdd
--- /dev/null
@@ -0,0 +1,9 @@
+
+
+extern void baseCheck();
+
+extern int coal1;
+extern int coal2;
+
+extern void baseVerifyCoal1(const char* where, int* addr);
+extern void baseVerifyCoal2(const char* where, int* addr);
diff --git a/testing/test-cases/weak-coalesce.dtest/foo1.c b/testing/test-cases/weak-coalesce.dtest/foo1.c
new file mode 100644 (file)
index 0000000..0707e0d
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h>
+#include "base.h"
+
+
+
+int __attribute__((weak)) coal1 = 1;
+int __attribute__((weak)) coal2 = 1;
+
+
+static __attribute__((constructor)) void myinit() 
+{
+       //fprintf(stderr, "myinit() in foo1.c\n");
+       baseVerifyCoal1("in foo1", &coal1);
+       baseVerifyCoal2("in foo1", &coal2);
+}
+
+
diff --git a/testing/test-cases/weak-coalesce.dtest/foo2.c b/testing/test-cases/weak-coalesce.dtest/foo2.c
new file mode 100644 (file)
index 0000000..74d884b
--- /dev/null
@@ -0,0 +1,14 @@
+#include <stdio.h>
+
+#include "base.h"
+
+int                                                    coal1 = 2;  // note: this is not weak and therefore should win
+int __attribute__((weak))      coal2 = 2;
+
+static __attribute__((constructor))
+void myinit()
+{
+       //fprintf(stderr, "myinit() in foo1.c\n");
+       baseVerifyCoal1("in foo2", &coal1);
+       baseVerifyCoal2("in foo2", &coal2);
+}
diff --git a/testing/test-cases/weak-coalesce.dtest/foo3.c b/testing/test-cases/weak-coalesce.dtest/foo3.c
new file mode 100644 (file)
index 0000000..e086d5c
--- /dev/null
@@ -0,0 +1,15 @@
+#include <stdio.h>
+
+#include "base.h"
+
+int __attribute__((weak))      coal1 = 3;
+int __attribute__((weak))      coal2 = 2;
+
+static __attribute__((constructor))
+void myinit() 
+{
+       //fprintf(stderr, "myinit() in foo1.c\n");
+       baseVerifyCoal1("in foo3", &coal1);
+       baseVerifyCoal2("in foo3", &coal2);
+}
+
diff --git a/testing/test-cases/weak-coalesce.dtest/main.c b/testing/test-cases/weak-coalesce.dtest/main.c
new file mode 100644 (file)
index 0000000..4d09091
--- /dev/null
@@ -0,0 +1,26 @@
+
+// BUILD:  $CC base.c -dynamiclib -install_name $RUN_DIR/libbase.dylib -o $BUILD_DIR/libbase.dylib
+// BUILD:  $CC foo3.c -dynamiclib -install_name $RUN_DIR/libfoo3.dylib -o $BUILD_DIR/libfoo3.dylib $BUILD_DIR/libbase.dylib
+// BUILD:  $CC foo2.c -dynamiclib -install_name $RUN_DIR/libfoo2.dylib -o $BUILD_DIR/libfoo2.dylib $BUILD_DIR/libfoo3.dylib $BUILD_DIR/libbase.dylib
+// BUILD:  $CC foo1.c -dynamiclib -install_name $RUN_DIR/libfoo1.dylib -o $BUILD_DIR/libfoo1.dylib $BUILD_DIR/libfoo2.dylib $BUILD_DIR/libbase.dylib
+// BUILD:  $CC main.c  $BUILD_DIR/libfoo1.dylib $BUILD_DIR/libbase.dylib -o $BUILD_DIR/weak-coalesce.exe
+
+// RUN:  ./weak-coalesce.exe
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "base.h"
+
+int main()
+{
+    printf("[BEGIN] weak-coalesce\n");
+
+       baseVerifyCoal1("in main", &coal1);
+       baseVerifyCoal2("in main", &coal2);
+
+       baseCheck();
+       return 0;
+}
+