]> git.saurik.com Git - apple/dyld.git/commitdiff
dyld-832.7.1.tar.gz macos-1101 v832.7.1
authorApple <opensource@apple.com>
Thu, 19 Nov 2020 01:06:42 +0000 (01:06 +0000)
committerApple <opensource@apple.com>
Thu, 19 Nov 2020 01:06:42 +0000 (01:06 +0000)
1057 files changed:
.gitignore
IMPCaches.md [new file with mode: 0644]
bin/expand.rb [deleted file]
build-scripts/ContainerizedTestRunner-build-everything.sh [new file with mode: 0755]
build-scripts/configure-dyld-archives.sh [new file with mode: 0755]
build-scripts/dyld_tests-build.sh [new file with mode: 0755]
build-scripts/dyld_tests-install.sh [new file with mode: 0755]
build-scripts/generate-cache-config-header.sh [new file with mode: 0755]
build-scripts/include.sh [new file with mode: 0644]
build-scripts/libdyld-generate-version-headers.sh [new file with mode: 0755]
build-scripts/update_dyld_shared_cache-build.sh [new file with mode: 0755]
configs/libdyld.xcconfig
configs/update_dyld_sim_shared_cache.xcconfig
doc/man/man1/closured.1 [deleted file]
doc/man/man1/dyld.1
doc/man/man1/dyld_usage.1
doc/man/man1/update_dyld_shared_cache.1 [deleted file]
doc/man/man3/dlopen_preflight.3
doc/man/man3/dyld.3
doc/rst/conf.py
doc/rst/dyld_usage.rst
dyld.xcodeproj/project.pbxproj
dyld3/APIs.cpp
dyld3/APIs.h
dyld3/APIs_macOS.cpp
dyld3/AllImages.cpp
dyld3/AllImages.h
dyld3/Array.h
dyld3/BootArgs.cpp
dyld3/BootArgs.h
dyld3/Closure.cpp
dyld3/Closure.h
dyld3/ClosureBuilder.cpp
dyld3/ClosureBuilder.h
dyld3/ClosureFileSystem.h
dyld3/ClosureFileSystemNull.cpp
dyld3/ClosureFileSystemPhysical.cpp
dyld3/ClosurePrinter.cpp
dyld3/ClosureWriter.cpp
dyld3/ClosureWriter.h
dyld3/Defines.h [new file with mode: 0644]
dyld3/Diagnostics.cpp
dyld3/Diagnostics.h
dyld3/JSON.h
dyld3/JSONReader.h
dyld3/JSONReader.mm
dyld3/JSONWriter.h
dyld3/Loading.cpp
dyld3/Loading.h
dyld3/MachOAnalyzer.cpp
dyld3/MachOAnalyzer.h
dyld3/MachOAnalyzerSet.cpp [new file with mode: 0644]
dyld3/MachOAnalyzerSet.h [new file with mode: 0644]
dyld3/MachOAppCache.cpp [new file with mode: 0644]
dyld3/MachOAppCache.h [new file with mode: 0644]
dyld3/MachOFile.cpp
dyld3/MachOFile.h
dyld3/MachOLoaded.cpp
dyld3/MachOLoaded.h
dyld3/PathOverrides.cpp
dyld3/PathOverrides.h
dyld3/RootsChecker.cpp [new file with mode: 0644]
dyld3/RootsChecker.h [new file with mode: 0644]
dyld3/SharedCacheRuntime.cpp
dyld3/SharedCacheRuntime.h
dyld3/SupportedArchs.h
dyld3/dyld_app_cache_util.cpp [new file with mode: 0644]
dyld3/libdyldEntryVector.cpp
dyld3/libdyldEntryVector.h
dyld3/shared-cache/AdjustDylibSegments.cpp
dyld3/shared-cache/AppCacheBuilder.cpp [new file with mode: 0644]
dyld3/shared-cache/AppCacheBuilder.h [new file with mode: 0644]
dyld3/shared-cache/Architectures.hpp [new file with mode: 0644]
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/IMPCaches.cpp [new file with mode: 0644]
dyld3/shared-cache/IMPCaches.hpp [new file with mode: 0644]
dyld3/shared-cache/IMPCachesBuilder.hpp [new file with mode: 0644]
dyld3/shared-cache/MachOFileAbstraction.hpp
dyld3/shared-cache/ObjC2Abstraction.hpp
dyld3/shared-cache/OptimizerBranches.cpp
dyld3/shared-cache/OptimizerLinkedit.cpp
dyld3/shared-cache/OptimizerObjC.cpp
dyld3/shared-cache/SharedCacheBuilder.cpp
dyld3/shared-cache/SharedCacheBuilder.h
dyld3/shared-cache/StringUtils.h
dyld3/shared-cache/dsc_extractor.cpp [new file with mode: 0644]
dyld3/shared-cache/dsc_extractor.h [new file with mode: 0644]
dyld3/shared-cache/dsc_iterator.cpp [new file with mode: 0644]
dyld3/shared-cache/dsc_iterator.h [new file with mode: 0644]
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/dyld_shared_cache_util.cpp [new file with mode: 0644]
dyld3/shared-cache/dyldinfo.cpp
dyld3/shared-cache/kernel_collection_builder.cpp [new file with mode: 0644]
dyld3/shared-cache/kernel_collection_builder.h [new file with mode: 0644]
dyld3/shared-cache/mrm_shared_cache_builder.cpp
dyld3/shared-cache/mrm_shared_cache_builder.h
dyld3/shared-cache/update_dyld_shared_cache.cpp
dyld3/shared-cache/update_dyld_sim_shared_cache.cpp
include/dlfcn_private.h [new file with mode: 0644]
include/mach-o/dyld.h
include/mach-o/dyld.modulemap [new file with mode: 0644]
include/mach-o/dyld_images.h
include/mach-o/dyld_priv.h
include/mach-o/dyld_process_info.h
include/mach-o/fixup-chains.h
launch-cache/Architectures.hpp [deleted file]
launch-cache/CacheFileAbstraction.hpp [deleted file]
launch-cache/FileAbstraction.hpp [deleted file]
launch-cache/MachOFileAbstraction.hpp [deleted file]
launch-cache/MachOTrie.hpp [deleted file]
launch-cache/dsc_extractor.cpp [deleted file]
launch-cache/dsc_extractor.h [deleted file]
launch-cache/dsc_iterator.cpp [deleted file]
launch-cache/dsc_iterator.h [deleted file]
launch-cache/dyld_cache_format.h [deleted file]
launch-cache/dyld_shared_cache_util.cpp [deleted file]
local_test_runner/ContainerizedTestRunner.mm
src/ImageLoader.cpp
src/ImageLoader.h
src/ImageLoaderMachO.cpp
src/ImageLoaderMachO.h
src/ImageLoaderMachOClassic.cpp
src/ImageLoaderMachOClassic.h
src/ImageLoaderMachOCompressed.cpp
src/ImageLoaderMachOCompressed.h
src/ImageLoaderMegaDylib.cpp
src/ImageLoaderMegaDylib.h
src/dyld2.cpp
src/dyld2.h
src/dyldAPIs.cpp
src/dyldAPIsInLibSystem.cpp
src/dyldInitialization.cpp
src/dyldLibSystemGlue.c
src/dyldNew.cpp
src/dyldStartup.s
src/dyldSyscallInterface.h
src/dyld_debugger.cpp
src/dyld_process_info.cpp
src/dyld_process_info_internal.h
src/dyld_usage.cpp
src/glue.c
testing/README.txt
testing/build_ninja.py
testing/include/test_support.h
testing/kernel-cache-tests/KernelCollection.py [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-bind-to-pageablekc/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-bind-to-pageablekc/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-bind-to-pageablekc/main.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-bind-to-pageablekc/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-bind-to-pageablekc/test.py [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-branch-fixups/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-branch-fixups/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-branch-fixups/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-branch-fixups/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-branch-fixups/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-branch-fixups/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-branch-fixups/main.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-branch-fixups/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-branch-fixups/test.py [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/bar.exports [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/extensions-auxkc/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/extensions-auxkc/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/extensions-basekc/codeless.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/main.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/test.py [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/bar.exports [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/main.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/test.py [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-auxkc/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-auxkc/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc/codeless.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/main.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/test.py [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-missing-weak-bind/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-missing-weak-bind/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-missing-weak-bind/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-missing-weak-bind/main.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-missing-weak-bind/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-missing-weak-bind/test.py [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-no-split-seg/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-no-split-seg/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-no-split-seg/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-no-split-seg/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-no-split-seg/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-no-split-seg/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-no-split-seg/main.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-no-split-seg/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-no-split-seg/test.py [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-uuid/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-pageablekc-uuid/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-uuid/main.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-uuid/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-pageablekc-uuid/test.py [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/bar1.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/bar1.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/bar2.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar1.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar1.kext/bar1 [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar2.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar2.kext/bar2 [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo1.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo1.kext/foo1 [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo2.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo2.kext/foo2 [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo1.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo1.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo2.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo2.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/kernel.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/kernel.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/test.py [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/bar1.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/bar1.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/bar2.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar1.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar1.kext/bar1 [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar2.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar2.kext/bar2 [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo1.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo1.kext/foo1 [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo2.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo2.kext/foo2 [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo1.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo1.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo2.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo2.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/kernel.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/kernel.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/test.py [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/bar1.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/bar1.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/bar2.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar1.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar1.kext/bar1 [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar2.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar2.kext/bar2 [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo1.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo1.kext/foo1 [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo2.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo2.kext/foo2 [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo1.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo1.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo2.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo2.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/kernel.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/kernel.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/test.py [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-uuid/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-uuid/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-uuid/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-uuid/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-uuid/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-uuid/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-uuid/main.c [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-uuid/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-uuid/test.py [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/foo.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/foo.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/metaclass.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/osobject.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/test.py [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/bar.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/foo.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/foo.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/test.py [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/bar.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/foo.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/foo.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/test.py [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching/bar.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-vtable-patching/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-vtable-patching/foo.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching/foo.h [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/auxkc-vtable-patching/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/auxkc-vtable-patching/test.py [new file with mode: 0644]
testing/kernel-cache-tests/bundle-ids/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/bundle-ids/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/bundle-ids/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/bundle-ids/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/bundle-ids/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/bundle-ids/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/bundle-ids/main.c [new file with mode: 0644]
testing/kernel-cache-tests/bundle-ids/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/bundle-ids/test.py [new file with mode: 0644]
testing/kernel-cache-tests/bundle-libraries-arch/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/bundle-libraries-arch/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/bundle-libraries-arch/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/bundle-libraries-arch/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/bundle-libraries-arch/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/bundle-libraries-arch/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/bundle-libraries-arch/main.c [new file with mode: 0644]
testing/kernel-cache-tests/bundle-libraries-arch/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/bundle-libraries-arch/test.py [new file with mode: 0644]
testing/kernel-cache-tests/codeless-kexts/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/codeless-kexts/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/codeless-kexts/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/codeless-kexts/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/codeless-kexts/test.py [new file with mode: 0644]
testing/kernel-cache-tests/ctf-arm64e/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/ctf-arm64e/ctf.txt [new file with mode: 0644]
testing/kernel-cache-tests/ctf-arm64e/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/ctf-arm64e/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/ctf-arm64e/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/ctf-arm64e/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/ctf-arm64e/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/ctf-arm64e/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/ctf-arm64e/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/ctf-arm64e/test.py [new file with mode: 0644]
testing/kernel-cache-tests/ctf-x86_64/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/ctf-x86_64/ctf.txt [new file with mode: 0644]
testing/kernel-cache-tests/ctf-x86_64/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/ctf-x86_64/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/ctf-x86_64/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/ctf-x86_64/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/ctf-x86_64/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/ctf-x86_64/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/ctf-x86_64/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/ctf-x86_64/test.py [new file with mode: 0644]
testing/kernel-cache-tests/data-const/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/data-const/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/data-const/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/data-const/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/data-const/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/data-const/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/data-const/main.c [new file with mode: 0644]
testing/kernel-cache-tests/data-const/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/data-const/test.py [new file with mode: 0644]
testing/kernel-cache-tests/empty-bundle-dir/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/empty-bundle-dir/extensions/empty.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/empty-bundle-dir/extensions/empty.kext/PlugIns/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/empty-bundle-dir/extensions/empty.kext/PlugIns/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/empty-bundle-dir/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/empty-bundle-dir/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/empty-bundle-dir/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/empty-bundle-dir/main.c [new file with mode: 0644]
testing/kernel-cache-tests/empty-bundle-dir/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/empty-bundle-dir/test.py [new file with mode: 0644]
testing/kernel-cache-tests/extra-prelink-info/main.c [new file with mode: 0644]
testing/kernel-cache-tests/extra-prelink-info/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/extra-prelink-info/prelink-info.plist [new file with mode: 0644]
testing/kernel-cache-tests/extra-prelink-info/test.py [new file with mode: 0755]
testing/kernel-cache-tests/fixups-arm64e/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/fixups-arm64e/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/fixups-arm64e/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/fixups-arm64e/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/fixups-arm64e/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/fixups-arm64e/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/fixups-arm64e/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/fixups-arm64e/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/fixups-arm64e/test.py [new file with mode: 0644]
testing/kernel-cache-tests/fixups-x86_64-unaligned/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/fixups-x86_64-unaligned/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/fixups-x86_64-unaligned/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/fixups-x86_64-unaligned/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/fixups-x86_64-unaligned/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/fixups-x86_64-unaligned/test.py [new file with mode: 0644]
testing/kernel-cache-tests/fixups-x86_64/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/fixups-x86_64/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/fixups-x86_64/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/fixups-x86_64/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/fixups-x86_64/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/fixups-x86_64/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/fixups-x86_64/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/fixups-x86_64/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/fixups-x86_64/test.py [new file with mode: 0644]
testing/kernel-cache-tests/hello-world-auxkc/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/hello-world-auxkc/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/hello-world-auxkc/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/hello-world-auxkc/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/hello-world-auxkc/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/hello-world-auxkc/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/hello-world-auxkc/main.c [new file with mode: 0644]
testing/kernel-cache-tests/hello-world-auxkc/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/hello-world-auxkc/test.py [new file with mode: 0644]
testing/kernel-cache-tests/hello-world-kernel/main.c [new file with mode: 0644]
testing/kernel-cache-tests/hello-world-kernel/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/hello-world-kernel/test.py [new file with mode: 0755]
testing/kernel-cache-tests/hello-world-pageablekc/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/hello-world-pageablekc/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/hello-world-pageablekc/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/hello-world-pageablekc/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/hello-world-pageablekc/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/hello-world-pageablekc/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/hello-world-pageablekc/main.c [new file with mode: 0644]
testing/kernel-cache-tests/hello-world-pageablekc/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/hello-world-pageablekc/test.py [new file with mode: 0644]
testing/kernel-cache-tests/implicit-dependencies/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/implicit-dependencies/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/implicit-dependencies/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/implicit-dependencies/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/implicit-dependencies/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/implicit-dependencies/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/implicit-dependencies/main.c [new file with mode: 0644]
testing/kernel-cache-tests/implicit-dependencies/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/implicit-dependencies/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kernel-base-address-x86_64/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kernel-base-address-x86_64/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kernel-base-address-x86_64/main.kernel.ld_m2hzX0 [new file with mode: 0644]
testing/kernel-cache-tests/kernel-base-address-x86_64/test.py [new file with mode: 0755]
testing/kernel-cache-tests/kernel-base-address/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kernel-base-address/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kernel-base-address/test.py [new file with mode: 0755]
testing/kernel-cache-tests/kernel-uuid/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kernel-uuid/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kernel-uuid/test.py [new file with mode: 0755]
testing/kernel-cache-tests/kernel-vtable-patching-arm64e/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching-arm64e/bar.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching-arm64e/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching-arm64e/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kernel-vtable-patching-arm64e/foo.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching-arm64e/foo.h [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching-arm64e/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching-arm64e/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kernel-vtable-patching-arm64e/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching-error/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching-error/bar.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching-error/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching-error/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kernel-vtable-patching-error/foo.h [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching-error/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching-error/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kernel-vtable-patching-error/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/bar.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/foo.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/foo.h [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching/bar.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kernel-vtable-patching/foo.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching/foo.h [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kernel-vtable-patching/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kernel-vtable-patching/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-missing-dep/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-missing-dep/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-missing-dep/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-missing-dep/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-missing-dep/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-missing-dep/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/exports.txt [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kext/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kext/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/exports.txt [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kext-missing-weak-bind/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-missing-weak-bind/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-missing-weak-bind/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-missing-weak-bind/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kext-missing-weak-bind/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-missing-weak-bind/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/kext-missing-weak-bind/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-missing-weak-bind/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-missing-weak-bind/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kext-missing-weak-bind/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kext-receipts/large.txt [new file with mode: 0644]
testing/kernel-cache-tests/kext-receipts/large2.txt [new file with mode: 0644]
testing/kernel-cache-tests/kext-receipts/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-receipts/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kext-receipts/small.txt [new file with mode: 0644]
testing/kernel-cache-tests/kext-receipts/test.py [new file with mode: 0755]
testing/kernel-cache-tests/kext-relative-paths/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-relative-paths/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-relative-paths/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kext-relative-paths/extensions/foo.kext/Contents/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-relative-paths/extensions/foo.kext/Contents/MacOS/foo [new file with mode: 0755]
testing/kernel-cache-tests/kext-relative-paths/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-relative-paths/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-relative-paths/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kext-relative-paths/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-arm64e/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-arm64e/bar.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-arm64e/bar.h [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-arm64e/baz.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/baz.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/baz.kext/baz [new file with mode: 0755]
testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/kext-vtable-patching-arm64e/foo.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-arm64e/foo.h [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-arm64e/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-arm64e/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kext-vtable-patching-arm64e/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/bar.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/foo.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/foo.h [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-overrides/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-overrides/bar.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/kext-vtable-patching-overrides/foo.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-overrides/foo.h [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-overrides/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching-overrides/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kext-vtable-patching-overrides/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching/bar.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kext-vtable-patching/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/kext-vtable-patching/foo.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching/foo.h [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/kext-vtable-patching/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kext-vtable-patching/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kext-weak-bind-chained/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-weak-bind-chained/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-weak-bind-chained/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kext-weak-bind-chained/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-weak-bind-chained/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/kext-weak-bind-chained/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-weak-bind-chained/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-weak-bind-chained/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kext-weak-bind-chained/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kext-weak-bind/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-weak-bind/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-weak-bind/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kext-weak-bind/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kext-weak-bind/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/kext-weak-bind/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-weak-bind/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kext-weak-bind/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kext-weak-bind/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kexts-missing-split-seg-error/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/kexts-missing-split-seg-error/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/kexts-missing-split-seg-error/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kexts-missing-split-seg-error/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kexts-missing-split-seg-error/test.py [new file with mode: 0644]
testing/kernel-cache-tests/kmod-info-errors/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/kmod-info-errors/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kmod-info-errors/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kmod-info-errors/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kmod-info-errors/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/kmod-info-errors/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/kmod-info-errors/kmod.h [new file with mode: 0644]
testing/kernel-cache-tests/kmod-info-errors/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kmod-info-errors/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kmod-info-errors/test.py [new file with mode: 0755]
testing/kernel-cache-tests/kmod-info-local-symbols/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/kmod-info-local-symbols/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kmod-info-local-symbols/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kmod-info-local-symbols/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kmod-info-local-symbols/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/kmod-info-local-symbols/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/kmod-info-local-symbols/kmod.h [new file with mode: 0644]
testing/kernel-cache-tests/kmod-info-local-symbols/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kmod-info-local-symbols/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kmod-info-local-symbols/test.py [new file with mode: 0755]
testing/kernel-cache-tests/kmod-info/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/kmod-info/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kmod-info/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/kmod-info/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/kmod-info/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/kmod-info/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/kmod-info/kmod.h [new file with mode: 0644]
testing/kernel-cache-tests/kmod-info/main.c [new file with mode: 0644]
testing/kernel-cache-tests/kmod-info/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/kmod-info/test.py [new file with mode: 0755]
testing/kernel-cache-tests/large-auxkc-errors/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/large-auxkc-errors/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/large-auxkc-errors/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/large-auxkc-errors/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/large-auxkc-errors/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/large-auxkc-errors/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/large-auxkc-errors/main.c [new file with mode: 0644]
testing/kernel-cache-tests/large-auxkc-errors/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/large-auxkc-errors/test.py [new file with mode: 0644]
testing/kernel-cache-tests/large-auxkc-no-errors/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/large-auxkc-no-errors/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/large-auxkc-no-errors/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/large-auxkc-no-errors/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/large-auxkc-no-errors/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/large-auxkc-no-errors/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/large-auxkc-no-errors/main.c [new file with mode: 0644]
testing/kernel-cache-tests/large-auxkc-no-errors/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/large-auxkc-no-errors/test.py [new file with mode: 0644]
testing/kernel-cache-tests/large-kcs-errors/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/large-kcs-errors/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/large-kcs-errors/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/large-kcs-errors/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/large-kcs-errors/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/large-kcs-errors/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/large-kcs-errors/main.c [new file with mode: 0644]
testing/kernel-cache-tests/large-kcs-errors/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/large-kcs-errors/test.py [new file with mode: 0755]
testing/kernel-cache-tests/last-data-const/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/last-data-const/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/last-data-const/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/last-data-const/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/last-data-const/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/last-data-const/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/last-data-const/main.c [new file with mode: 0644]
testing/kernel-cache-tests/last-data-const/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/last-data-const/test.py [new file with mode: 0644]
testing/kernel-cache-tests/packed-kext-data/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/packed-kext-data/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/packed-kext-data/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/packed-kext-data/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/packed-kext-data/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/packed-kext-data/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/packed-kext-data/main.c [new file with mode: 0644]
testing/kernel-cache-tests/packed-kext-data/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/packed-kext-data/test.py [new file with mode: 0644]
testing/kernel-cache-tests/packed-kext-text/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/packed-kext-text/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/packed-kext-text/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/packed-kext-text/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/packed-kext-text/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/packed-kext-text/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/packed-kext-text/main.c [new file with mode: 0644]
testing/kernel-cache-tests/packed-kext-text/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/packed-kext-text/test.py [new file with mode: 0644]
testing/kernel-cache-tests/pageablekc-bind-to-basekc/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/pageablekc-bind-to-basekc/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/pageablekc-bind-to-basekc/main.c [new file with mode: 0644]
testing/kernel-cache-tests/pageablekc-bind-to-basekc/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/pageablekc-bind-to-basekc/test.py [new file with mode: 0644]
testing/kernel-cache-tests/pageablekc-uuid/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/pageablekc-uuid/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/pageablekc-uuid/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/pageablekc-uuid/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/pageablekc-uuid/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/pageablekc-uuid/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/pageablekc-uuid/main.c [new file with mode: 0644]
testing/kernel-cache-tests/pageablekc-uuid/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/pageablekc-uuid/test.py [new file with mode: 0644]
testing/kernel-cache-tests/pageablekc-vtable-patching/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/pageablekc-vtable-patching/bar.cpp [new file with mode: 0644]
testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/pageablekc-vtable-patching/foo.cpp [new file with mode: 0644]
testing/kernel-cache-tests/pageablekc-vtable-patching/foo.h [new file with mode: 0644]
testing/kernel-cache-tests/pageablekc-vtable-patching/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/pageablekc-vtable-patching/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/pageablekc-vtable-patching/test.py [new file with mode: 0644]
testing/kernel-cache-tests/plugins/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/plugins/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/plugins/extensions/foo.kext/PlugIns/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/plugins/extensions/foo.kext/PlugIns/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/plugins/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/plugins/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/plugins/main.c [new file with mode: 0644]
testing/kernel-cache-tests/plugins/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/plugins/test.py [new file with mode: 0644]
testing/kernel-cache-tests/ppl-data-const/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/ppl-data-const/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/ppl-data-const/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/ppl-data-const/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/ppl-data-const/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/ppl-data-const/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/ppl-data-const/main.c [new file with mode: 0644]
testing/kernel-cache-tests/ppl-data-const/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/ppl-data-const/test.py [new file with mode: 0644]
testing/kernel-cache-tests/strip-all-kexts/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/strip-all-kexts/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/strip-all-kexts/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/strip-all-kexts/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/strip-all-kexts/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/strip-all-kexts/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/strip-all-kexts/main.c [new file with mode: 0644]
testing/kernel-cache-tests/strip-all-kexts/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/strip-all-kexts/test.py [new file with mode: 0644]
testing/kernel-cache-tests/strip-all/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/strip-all/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/strip-all/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/strip-all/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/strip-all/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/strip-all/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/strip-all/main.c [new file with mode: 0644]
testing/kernel-cache-tests/strip-all/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/strip-all/test.py [new file with mode: 0644]
testing/kernel-cache-tests/strip-kexts-all/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/strip-kexts-all/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/strip-kexts-all/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/strip-kexts-all/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/strip-kexts-all/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/strip-kexts-all/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/strip-kexts-all/main.c [new file with mode: 0644]
testing/kernel-cache-tests/strip-kexts-all/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/strip-kexts-all/test.py [new file with mode: 0644]
testing/kernel-cache-tests/strip-kexts-exports/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/strip-kexts-exports/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/strip-kexts-exports/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/strip-kexts-exports/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/strip-kexts-exports/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/strip-kexts-exports/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/strip-kexts-exports/main.c [new file with mode: 0644]
testing/kernel-cache-tests/strip-kexts-exports/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/strip-kexts-exports/test.py [new file with mode: 0644]
testing/kernel-cache-tests/strip-kexts-locals/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/strip-kexts-locals/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/strip-kexts-locals/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/strip-kexts-locals/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/strip-kexts-locals/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/strip-kexts-locals/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/strip-kexts-locals/main.c [new file with mode: 0644]
testing/kernel-cache-tests/strip-kexts-locals/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/strip-kexts-locals/test.py [new file with mode: 0644]
testing/kernel-cache-tests/strip-none/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/strip-none/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/strip-none/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/strip-none/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/strip-none/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/strip-none/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/strip-none/main.c [new file with mode: 0644]
testing/kernel-cache-tests/strip-none/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/strip-none/test.py [new file with mode: 0644]
testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/baz.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/main.c [new file with mode: 0644]
testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/test.py [new file with mode: 0644]
testing/kernel-cache-tests/symbol-sets-prefix/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/symbol-sets-prefix/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/symbol-sets-prefix/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/symbol-sets-prefix/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/symbol-sets-prefix/main.c [new file with mode: 0644]
testing/kernel-cache-tests/symbol-sets-prefix/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/symbol-sets-prefix/test.py [new file with mode: 0644]
testing/kernel-cache-tests/symbol-sets/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/symbol-sets/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/symbol-sets/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/symbol-sets/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/symbol-sets/main.c [new file with mode: 0644]
testing/kernel-cache-tests/symbol-sets/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/symbol-sets/test.py [new file with mode: 0644]
testing/kernel-cache-tests/testall.py [new file with mode: 0755]
testing/kernel-cache-tests/text-fixups-x86_64-auxkc/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/text-fixups-x86_64-auxkc/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/text-fixups-x86_64-auxkc/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/text-fixups-x86_64-auxkc/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/text-fixups-x86_64-auxkc/test.py [new file with mode: 0644]
testing/kernel-cache-tests/text-fixups-x86_64/bar.c [new file with mode: 0644]
testing/kernel-cache-tests/text-fixups-x86_64/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/text-fixups-x86_64/extensions/bar.kext/bar [new file with mode: 0755]
testing/kernel-cache-tests/text-fixups-x86_64/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/text-fixups-x86_64/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/text-fixups-x86_64/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/text-fixups-x86_64/main.cpp [new file with mode: 0644]
testing/kernel-cache-tests/text-fixups-x86_64/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/text-fixups-x86_64/test.py [new file with mode: 0644]
testing/kernel-cache-tests/vtable-patching-metaclass-alias/SymbolSets.plist [new file with mode: 0644]
testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo [new file with mode: 0755]
testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/filelist [new file with mode: 0644]
testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/files/foo-f6e015.o [new file with mode: 0644]
testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/link_command [new file with mode: 0644]
testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/orig_command_line [new file with mode: 0644]
testing/kernel-cache-tests/vtable-patching-metaclass-alias/foo.c [new file with mode: 0644]
testing/kernel-cache-tests/vtable-patching-metaclass-alias/main.c [new file with mode: 0644]
testing/kernel-cache-tests/vtable-patching-metaclass-alias/main.kernel [new file with mode: 0755]
testing/kernel-cache-tests/vtable-patching-metaclass-alias/test.py [new file with mode: 0644]
testing/lib/test_support.cpp
testing/run-static/jit_entitlement.plist [new file with mode: 0644]
testing/run-static/run-static.cpp [new file with mode: 0644]
testing/test-cases/NSAddImage-basic.dtest/main.c
testing/test-cases/NSAddImage-fail.dtest/main.cpp
testing/test-cases/NSAddImage-loaded.dtest/main.c
testing/test-cases/NSAddressOfSymbol-basic.dtest/main.c
testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/main.c
testing/test-cases/NSCreateObjectFileImageFromFile-stress.dtest/main.cpp
testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/main.c
testing/test-cases/NSLookupSymbolInImage-basic.dtest/main.c
testing/test-cases/_dyld_for_each_objc_class-duplicates.dtest/main.m
testing/test-cases/_dyld_for_each_objc_class-missing-weak-chained.dtest/main.mm
testing/test-cases/_dyld_for_each_objc_class-missing-weak.dtest/main.mm
testing/test-cases/_dyld_for_each_objc_class.dtest/main.m
testing/test-cases/_dyld_for_each_objc_protocol.dtest/main.m
testing/test-cases/_dyld_get_image_name-cache-symlink.dtest/foo.c [new file with mode: 0644]
testing/test-cases/_dyld_get_image_name-cache-symlink.dtest/main.c [new file with mode: 0644]
testing/test-cases/_dyld_get_objc_selector-chained.dtest/main.m
testing/test-cases/_dyld_get_prog_image_header.dtest/foo.c [new file with mode: 0644]
testing/test-cases/_dyld_get_prog_image_header.dtest/main.c [new file with mode: 0644]
testing/test-cases/_dyld_launch_mode.dtest/main.c [new file with mode: 0644]
testing/test-cases/_dyld_shared_cache_contains_path.dtest/main.c [new file with mode: 0644]
testing/test-cases/_dyld_shared_cache_real_path.dtest/main.c [new file with mode: 0644]
testing/test-cases/amfi-hardened-dlopen-leaf.dtest/main.c
testing/test-cases/amfi-interpose.dtest/main.c
testing/test-cases/bind-addend.dtest/main.c
testing/test-cases/bind-rebase.dtest/main.c
testing/test-cases/chained-fixups-many-binds.dtest/foo.c
testing/test-cases/chained-fixups-many-binds.dtest/foo.h
testing/test-cases/chained-fixups-many-binds.dtest/main.c
testing/test-cases/chained-fixups-many-binds.dtest/uses.h
testing/test-cases/crt-old-mac10.5-vars-libSystem.dtest/main.c
testing/test-cases/crt-old-mac10.6-vars-libSystem.dtest/main.c
testing/test-cases/dlopen-RTLD_LOCAL-coalesce.dtest/main.c
testing/test-cases/dlopen-atpath-restricted.dtest/main.c
testing/test-cases/dlopen-dyld.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-haswell.dtest/main.c
testing/test-cases/dlopen-iOSMac.dtest/cat.c [new file with mode: 0644]
testing/test-cases/dlopen-iOSMac.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlopen-iOSMac.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen-indirect-groupNum.dtest/main.c
testing/test-cases/dlopen-jna.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dlopen-jna.dtest/jna.c [new file with mode: 0644]
testing/test-cases/dlopen-jna.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen_from-basic.dtest/bar.c [new file with mode: 0644]
testing/test-cases/dlopen_from-basic.dtest/main.c [new file with mode: 0644]
testing/test-cases/dlopen_from-basic.dtest/test.c [new file with mode: 0644]
testing/test-cases/dlopen_preflight.dtest/main.c [new file with mode: 0644]
testing/test-cases/dtrace.dtest/main.c
testing/test-cases/dyld_abort_payload.dtest/main.cpp
testing/test-cases/dyld_need_closure.dtest/main.c
testing/test-cases/dyld_process_info.dtest/main.cpp
testing/test-cases/dyld_usage_json.dtest/foo.c [new file with mode: 0644]
testing/test-cases/dyld_usage_json.dtest/main.mm [new file with mode: 0644]
testing/test-cases/dyld_usage_json.dtest/target.c [new file with mode: 0644]
testing/test-cases/dyld_version_spis.dtest/main.c
testing/test-cases/dyld_version_spis.dtest/version_set.c [new file with mode: 0644]
testing/test-cases/dylib-re-export-old-format.dtest/main.c
testing/test-cases/env-DYLD_FORCE_PLATFORM.dtest/main.c
testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/main.c [new file with mode: 0644]
testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/myzlib.c [new file with mode: 0644]
testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/reexported-myzlib.c [new file with mode: 0644]
testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/reexporter.c [new file with mode: 0644]
testing/test-cases/env-DYLD_VERSIONED_FRAMEWORK_PATH.dtest/main.c
testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.dtest/foo.c [new file with mode: 0644]
testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.dtest/main.c [new file with mode: 0644]
testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH.dtest/main.c
testing/test-cases/flat-namespace-absolute-symbol.dtest/main.c
testing/test-cases/flat-namespace.dtest/main.c
testing/test-cases/kernel-auxkc-fixups.dtest/bar.c [new file with mode: 0644]
testing/test-cases/kernel-auxkc-fixups.dtest/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/test-cases/kernel-auxkc-fixups.dtest/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/test-cases/kernel-auxkc-fixups.dtest/extensions/kernel-export.kext/Info.plist [new file with mode: 0644]
testing/test-cases/kernel-auxkc-fixups.dtest/foo.c [new file with mode: 0644]
testing/test-cases/kernel-auxkc-fixups.dtest/kernel-export.c [new file with mode: 0644]
testing/test-cases/kernel-auxkc-fixups.dtest/main.c [new file with mode: 0644]
testing/test-cases/kernel-classic-relocs.h [new file with mode: 0644]
testing/test-cases/kernel-fixups-x86_64.dtest/main.c [new file with mode: 0644]
testing/test-cases/kernel-fixups.dtest/main.c [new file with mode: 0644]
testing/test-cases/kernel-fixups.h [new file with mode: 0644]
testing/test-cases/kernel-hello-world.dtest/main.c [new file with mode: 0644]
testing/test-cases/kernel-helpers.h [new file with mode: 0644]
testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/bar.c [new file with mode: 0644]
testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/extensions/pageable.kext/Info.plist [new file with mode: 0644]
testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/foo.c [new file with mode: 0644]
testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/main.c [new file with mode: 0644]
testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/pageable.c [new file with mode: 0644]
testing/test-cases/kernel-pageablekc-fixups.dtest/bar.c [new file with mode: 0644]
testing/test-cases/kernel-pageablekc-fixups.dtest/extensions/bar.kext/Info.plist [new file with mode: 0644]
testing/test-cases/kernel-pageablekc-fixups.dtest/extensions/foo.kext/Info.plist [new file with mode: 0644]
testing/test-cases/kernel-pageablekc-fixups.dtest/extensions/kernel-export.kext/Info.plist [new file with mode: 0644]
testing/test-cases/kernel-pageablekc-fixups.dtest/foo.c [new file with mode: 0644]
testing/test-cases/kernel-pageablekc-fixups.dtest/kernel-export.c [new file with mode: 0644]
testing/test-cases/kernel-pageablekc-fixups.dtest/main.c [new file with mode: 0644]
testing/test-cases/kernel-test-runner.h [new file with mode: 0644]
testing/test-cases/large-load-commands.dtest/extra.cmds [new file with mode: 0644]
testing/test-cases/large-load-commands.dtest/foo.c [new file with mode: 0644]
testing/test-cases/large-load-commands.dtest/main.c [new file with mode: 0644]
testing/test-cases/lazy-symbol-missing.dtest/main-call.c
testing/test-cases/lazy-symbol-missing.dtest/main.c
testing/test-cases/lazy-symbol-missing.dtest/runner.cpp
testing/test-cases/libdsc.dtest/main.c [new file with mode: 0644]
testing/test-cases/macOS-cache-rebuild.dtest/main.cpp
testing/test-cases/missing-weak-def.dtest/bar.c
testing/test-cases/missing-weak-def.dtest/main.c
testing/test-cases/no-shared-cache.dtest/main.c [deleted file]
testing/test-cases/restrict-search.dtest/main.c
testing/test-cases/thread-local-atexit-macOS.dtest/main.cpp
testing/test-cases/unix-conformance.dtest/main.c
testing/test-cases/upward-links-initializers.dtest/init1.c [new file with mode: 0644]
testing/test-cases/upward-links-initializers.dtest/init2.c [new file with mode: 0644]
testing/test-cases/upward-links-initializers.dtest/init3.c [new file with mode: 0644]
testing/test-cases/upward-links-initializers.dtest/init4.c [new file with mode: 0644]
testing/test-cases/upward-links-initializers.dtest/init5.c [new file with mode: 0644]
testing/test-cases/upward-links-initializers.dtest/init6.c [new file with mode: 0644]
testing/test-cases/upward-links-initializers.dtest/main.c [new file with mode: 0644]
testing/test-cases/upward-links-initializers.dtest/value.c [new file with mode: 0644]
testing/test-cases/weak-def-bind-old-format.dtest/main.c
testing/test-cases/weak-def-unload.dtest/bar.c [new file with mode: 0644]
testing/test-cases/weak-def-unload.dtest/foo.c [new file with mode: 0644]
testing/test-cases/weak-def-unload.dtest/main.c [new file with mode: 0644]

index dfac31e309f9fecb8b924955759646ba481f544f..286ea56342d81cd195fd4285e2b5c4ba5471732a 100644 (file)
@@ -7,4 +7,5 @@ dyld.xcodeproj/xcuserdata/
 dyld.xcodeproj/xcshareddata/
 .DS_Store
 .pyc
-
+*.apc
+*.kc
diff --git a/IMPCaches.md b/IMPCaches.md
new file mode 100644 (file)
index 0000000..d77afe0
--- /dev/null
@@ -0,0 +1,166 @@
+# IMP caches generation
+
+## Principle
+
+When you call an Objective-C method, Objective-C looks for the actual IMP (function pointer) given the class and selector. It then
+stores, in malloced memory, this [selector, IMP] pair in a hash table. 
+
+These hash tables are:
+
+* imperfect: the hash function is just a mask applied to the selector's
+address, so if two selectors have the same low bits, you will get a collision.
+Collisions are resolved through linear probing
+* expensive: the footprint of these hash tables is often around 70MB total on a carry device.
+
+We want to replace this with static hash tables, inside the shared cache, where you have:
+
+* a perfect hashing function
+* and the tables are in clean memory
+
+## Hash function
+
+Because the hash function is executed as part of `objc_msgSend`, it has to be blazingly fast. The algorithm below explains how we can
+make a hash function of the form `h(x) = (x >> shift) & mask` work, where `shift` and `mask` are two class-specific parameters.
+
+Note that we cannot use traditional perfect hash tables techniques as:
+
+* they require at least [1.44 bits of information per key](https://arxiv.org/pdf/1910.06416.pdf). 
+* they often require multiple lookups (two-level hashing).
+
+The idea is that because the input of the hash function is a selector's address, we have some control over the input... because the
+dyld shared cache builder is the one placing all the selectors.
+
+So now the problem becomes : given a set of classes and the selectors they implement, how do you place the selectors, and for each
+class, how do you find a shift and mask so that the hash table generated by (selector >> shift) & mask is perfect?
+
+(Note that the shift + mask idiom lets us use various "bit stripes" of the selector's address for various hash tables).
+
+## Algorithm
+
+There are basically two steps to the main algorithm:
+
+### Finding shifts and masks
+
+Assign some of the high bits of the selector's address, and find a shift and a mask for each class. This is a backtracking
+algorithm which goes through each class, one after another. As it goes through classes, it finds a shift and a mask compatible
+with the bit ranges that are already set on each selector's address, and assigns the corresponding bits of the selector's address.
+Note that because addresses are partial at this point (some of the bits are unset) it's very difficult to check for collisions,
+so selectors will end up at the same address.
+
+At each step of the algorithm, we go through a set of possible shifts and masks until we find one which works. If none work, we let
+the hash table grow to one more bit to make our job slightly easier. In practice few hash tables grow one more bit (82 out of 18k).
+If we cannot find a suitable shift and mask after backtracking a few times, we'll also allow ourselves to drop a class from the set
+and not generate an IMP cache for that one. This happens for a dozen classes or so with the current data set.
+
+Next we have to deal with collisions, and constraints on the addresses themselves because each selector is... a `char*`, which has
+a length. You cannot have an overlap between two selectors. So the idea here is to try to get rid of this constraint by assigning
+in step 1 just the address of a "bucket" which is 128 bytes long (7 bits). So step 1 will assign the high bits of the address
+(which will be the address of the bucket) and we can then place the selectors linearly in the buckets.
+
+### Shuffling selectors around
+
+Then you have to deal with address collisions. If the lengths of selectors in a given bucket add up to more than 128, you have
+to move some selectors out. So step 2 goes through all the selectors, checks if it fits within the bucket it's supposed to be in,
+and if it doesn't finds another suitable bucket.
+
+To do so, it iterates through all the classes the selector is in, and builds a "constraint set" applying to that selector's
+bucket's address (by basically looking at which slots in each hash table are free and combining all these constraints, which 
+impact a different "stripe" of the address due to the shifts and masks). Once we have the constraint set, we can find a different
+bucket for the selector without changing the shifts and masks. (If there is no other possible bucket, we allow ourselves to drop the
+classes involving this selector, too).
+
+Once each selector has a valid bucket, you can simply assign the low 7 bits of each address by looking at which selectors are in 
+each bucket and looking at their lengths.
+
+The problem is hard but not that hard given the number of classes we are targeting with this optimization:
+we are roughly targeting ~ 20k classes out of 120k and ~ 200k selectors out of 900k, so we have lots of "free space".
+
+Note that any holes we leave in the bucketization of the selectors can be filled later by placing all the selectors not targeted
+by the optimization and any selectors from OS executables inside them.
+
+## Shared cache builder setup
+
+The optimization is guided by a file dropped by the OrderFiles project (`/AppleInternal/OrderFiles/shared-cache-objc-optimizations.json`).
+The performance team is responsible for updating this file by looking at the caches on live or StressCycler devices with `objcdt dump-imp-caches`.
+
+This file specifies a list of classes and metaclasses the algorithm targets, and a list of flattening roots.
+
+We haven't explained flattening yet. When a class D(aughter) inherits from a class M(other), we should in theory add all of M's methods
+to D's IMP cache. However
+
+* This constrains the problem too much. The solver's job will be too difficult if the same selectors are in thousands of classes.
+* This makes the IMP cache sizes blow up. 
+
+So our scheme is to target only classes which are leaves in the inheritance tree with this optimization. For any selector that comes from
+parent classes, the cache lookup will fail and fall back to looking for the method in `super`. Because `super` is not a leaf class,
+it will have a dynamically allocated IMP cache and can cache there any selector that comes from the parents.
+
+However, this means that some very interesting classes from a memory standpoint (because we find their caches in many processes)
+get excluded because they have child classes. A solution to this is to turn on selector inheritance (add Mother's selectors
+to Daughter's cache) starting at some flattening root. Then the IMP cache will have a "fallback class" that is the superclass
+of the flattening root, and `objc_msgSend` will fallback to that class if it cannot find the selector in the child cache, 
+skipping over all the classes up to the flattening root (because we know all the selectors in that chain will be present
+in the child cache).
+
+Very early into the shared cache builder's life, the algorithm described above runs. To do so, it parses all of the source dylibs
+to find out which methods will end up in which class's cache (see section "what ends up in each cache" below).
+The output of the algorithm is:
+
+- for each class:
+  * a shift and a mask
+  * a list of methods that will end up in the cache with (source install name, source class name, source category name)
+- for each selector: an offset at which it needs to be placed relative to the beginning of the selectors section
+- a list of holes in the selector address space (or more accurately offset space) that can be used to add additional selectors
+    not targeted by the algorithm
+  
+Then, we do all the selector deduping work, and when we get to the ObjC optimizer:
+
+- we go through all the classes again to build a map from (source install name, source class name, source category name) to the 
+    actual IMP's address (which is not known before the shared cache is laid out)
+- we go through all the IMP caches returned by the algorithm, find the corresponding IMP, and emit the actual IMP cache in the 
+    objc optimizer's RO region.
+
+## Some code pointers
+
+Most of the logic lives in the single C++ file `IMPCaches.cpp` and the two headers `IMPCaches.hpp` and `IMPCachesBuilder.hpp`. A tour:
+
+### Types
+
+`IMPCaches::Selector` : A unique selector. Has a name, a list of classes it's used in, and an address (which is actually an offset from
+the beginning of the selectors section). Due to how the placement algorithm work, it also has a current
+partial address and the corresponding bitmask of fixed bits. The algorithm adds bits to the partial address
+as it makes progress and updates the mask accordingly
+
+`IMPCaches::AddressSpace` : a representation of the address space available in the selectors section, so that step 2 of the algorithm
+can check if selector buckets are overflowing.
+
+`IMPCaches::HoleMap` : represents the holes left by the selector placement algorithm, to be filled later with other selectors we did not target.
+
+`IMPCaches::Constraint` : represents a constraint on some of the bits of an address. It stores a set of allowed values for a given range of bits (shift and mask)
+
+`IMPCachesBuilder` : what actually computes the caches (part of the algorithm ; what actually lays down the caches lives in the
+ObjC optimizer)
+
+### Entry points
+
+* `IMPCachesBuilder::parseDylibs` : parses the source dylibs to figure out which method ends up in which cache.
+
+* `IMPCachesBuilder::findShiftsAndMasks` : finds the shift and mask for each class (see "Findings shifts and masks" above).
+
+* `IMPCachesBuilder::solveGivenShiftsAndMasks` : moves selectors around to make sure each bucket only contains 128 bytes worth of selectors.
+
+Then in `OptimizerObjC`:
+
+* `IMPMapBuilder` : Builds a map from (install name, class name, method name) to actual IMPs
+
+* `IMPCachesEmitter` : emits the actual IMP caches in the shared cache.
+
+### What ends up in each cache?
+
+`IMPCachesBuilder::parseDylibs` goes through all the classes and categories to decide, for each (class,selector) pair, which implementation we will actually call at runtime.
+The sequence is:
+
+* Get all the methods from the method lists
+* Attach any methods from non-cross-image categories (when we have cross-image categories, we prevent the class from getting an IMP cache). If there is any collision at this point we'll
+use the implementation from the main class (or from the first category we found).
+* Then we may inline some of the implementations from superclasses, see the explanation on flattening above.
diff --git a/bin/expand.rb b/bin/expand.rb
deleted file mode 100755 (executable)
index 01c7542..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/usr/bin/env ruby
-
-require 'yaml'
-
-if ENV["DRIVERKITROOT"]
-    $availCmd = ENV["SDKROOT"] + ENV["DRIVERKITROOT"] + "/usr/local/libexec/availability.pl";
-else
-    $availCmd = ENV["SDKROOT"] + "/usr/local/libexec/availability.pl";
-end
-
-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 =~ /^\/\/\@TVOS_VERSION_DEFS\@$/
-        expandVersions("DYLD_TVOS_VERSION_", "--appletvos")
-    elsif line =~ /^\/\/\@BRIDGEOS_VERSION_DEFS\@$/
-        expandVersions("DYLD_BRIDGEOS_VERSION_", "--bridgeos")
-    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
diff --git a/build-scripts/ContainerizedTestRunner-build-everything.sh b/build-scripts/ContainerizedTestRunner-build-everything.sh
new file mode 100755 (executable)
index 0000000..d2b7d71
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+source $SRCROOT/build-scripts/include.sh
+
+env -i PATH="${PATH}" xcodebuild install -sdk ${SDKROOT} -configuration ${CONFIGURATION} -target dyld -target libdyld -target dyld_tests -target dyld_usage TOOLCHAINS="${TOOLCHAINS}" DSTROOT=${DERIVED_FILES_DIR}/TestRoot OBJROOT=${DERIVED_FILES_DIR}/objroot DISABLE_SDK_METADATA_PARSING=YES XCTestGenPath=${DERIVED_FILES_DIR}/XCTestGenerated.h GCC_PREPROCESSOR_DEFINITIONS='$GCC_PREPROCESSOR_DEFINITIONS BUILD_FOR_TESTING=1' || exit_if_error $? "Build failed"
+${BUILT_PRODUCTS_DIR}/chroot_util -chroot ${DERIVED_FILES_DIR}/TestRoot -fallback / -add_file /bin/echo -add_file /bin/sh -add_file /bin/bash -add_file /bin/ls -add_file /usr/sbin/dtrace -add_file /sbin/mount -add_file /sbin/mount_devfs -add_file /usr/lib/libobjc-trampolines.dylib -add_file /usr/bin/leaks -add_file /System/iOSSupport/System/Library/Frameworks/UIKit.framework/UIKit || exit_if_error $? "Chroot build failed"
+/bin/mkdir -p ${DERIVED_FILES_DIR}/TestRoot/dev
+/bin/mkdir -m 777 -p ${DERIVED_FILES_DIR}/TestRoot/tmp
+
diff --git a/build-scripts/configure-dyld-archives.sh b/build-scripts/configure-dyld-archives.sh
new file mode 100755 (executable)
index 0000000..60a663e
--- /dev/null
@@ -0,0 +1,15 @@
+# link with all .a files in /usr/local/lib/dyld
+ls -1 ${SDKROOT}/usr/local/lib/dyld/*.a | grep -v libcompiler_rt > ${DERIVED_SOURCES_DIR}/archives.txt
+
+# link with crash report archive if it exists
+if [ -f ${SDKROOT}/usr/local/lib/libCrashReporterClient.a ]
+then
+  echo \"${SDKROOT}/usr/local/lib/libCrashReporterClient.a\" >> ${DERIVED_SOURCES_DIR}/archives.txt
+fi
+
+# link with crypto archive if it exists
+if [ -f ${SDKROOT}/usr/local/lib/libcorecrypto_static.a ]
+then
+  echo \"${SDKROOT}/usr/local/lib/libcorecrypto_static.a\" >> ${DERIVED_SOURCES_DIR}/archives.txt
+fi
+
diff --git a/build-scripts/dyld_tests-build.sh b/build-scripts/dyld_tests-build.sh
new file mode 100755 (executable)
index 0000000..eda854f
--- /dev/null
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+set -e
+
+source $SRCROOT/build-scripts/include.sh
+
+# Exit on failure
+
+OBJROOT_DYLD_APP_CACHE_UTIL="${TARGET_TEMP_DIR}/Objects_Dyld_App_Cache_Util"
+OBJROOT_RUN_STATIC="${TARGET_TEMP_DIR}/Objects_Run_Static"
+
+SYMROOT=${BUILD_DIR}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/dyld_tests
+OBJROOT=${PROJECT_TEMP_DIR}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}
+SDKROOT=${SDKROOT:-$(xcrun -sdk macosx.internal --show-sdk-path)}
+DEPLOYMENT_TARGET_CLANG_FLAG_NAME=${DEPLOYMENT_TARGET_CLANG_FLAG_NAME:-"mmacosx-version-min"}
+DERIVED_FILES_DIR=${DERIVED_FILES_DIR}
+LDFLAGS="-L$BUILT_PRODUCTS_DIR"
+#LLBUILD=$(xcrun --sdk $SDKROOT --find llbuild 2> /dev/null)
+NINJA=${LLBUILD:-`xcrun  --sdk $SDKROOT --find ninja  2> /dev/null`}
+BUILD_TARGET=${ONLY_BUILD_TEST:-all}
+
+if [ ! -z "$LLBUILD" ]; then
+  NINJA="$LLBUILD ninja build"
+fi
+
+OSVERSION="10.14"
+if [ ! -z "$DEPLOYMENT_TARGET_CLANG_ENV_NAME" ]; then
+    OSVERSION=${!DEPLOYMENT_TARGET_CLANG_ENV_NAME}
+fi
+
+if [ -z "$SRCROOT" ]; then
+    echo "Error $$SRCROOT must be set"
+fi
+
+if [ -z "$ARCHS" ]; then
+    PLATFORM_NAME=${PLATFORM_NAME:macosx}
+    case "$PLATFORM_NAME" in
+       "watchos")   ARCHS="armv7k arm64_32"
+       ;;
+       "appletvos") ARCHS="arm64"
+       ;;
+       *)    ARCHS=${ARCHS_STANDARD}
+       ;;
+    esac
+fi
+
+if [ -z "$ARCHS" ]; then
+    ARCHS="x86_64"
+fi
+
+/bin/mkdir -p ${DERIVED_FILES_DIR}
+TMPFILE=$(mktemp ${DERIVED_FILES_DIR}/config.ninja.XXXXXX)
+
+echo "OBJROOT = $OBJROOT" >> $TMPFILE
+echo "OSFLAG = $DEPLOYMENT_TARGET_CLANG_FLAG_NAME" >> $TMPFILE
+echo "OSVERSION = $OSVERSION" >> $TMPFILE
+echo "SDKROOT = $SDKROOT" >> $TMPFILE
+echo "SRCROOT = $SRCROOT" >> $TMPFILE
+echo "SYMROOT = $SYMROOT" >> $TMPFILE
+echo "BUILT_PRODUCTS_DIR = $BUILT_PRODUCTS_DIR" >> $TMPFILE
+echo "INSTALL_GROUP = $INSTALL_GROUP" >> $TMPFILE
+echo "INSTALL_MODE_FLAG = $INSTALL_MODE_FLAG" >> $TMPFILE
+echo "INSTALL_OWNER = $INSTALL_OWNER" >> $TMPFILE
+echo "INSTALL_DIR = $INSTALL_DIR" >> $TMPFILE
+echo "USER_HEADER_SEARCH_PATHS = $USER_HEADER_SEARCH_PATHS" >> $TMPFILE
+echo "SYSTEM_HEADER_SEARCH_PATHS = $SYSTEM_HEADER_SEARCH_PATHS" >> $TMPFILE
+echo "ARCHS = $ARCHS" >> $TMPFILE
+echo "DERIVED_FILES_DIR = $DERIVED_FILES_DIR" >> $TMPFILE
+echo "LDFLAGS = $LDFLAGS" >> $TMPFILE
+
+/usr/bin/rsync -vc $TMPFILE ${DERIVED_FILES_DIR}/config.ninja
+/bin/rm -f $TMPFILE
+
+xcodebuild install -target run-static SDKROOT="${SDKROOT}" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT="${OBJROOT_RUN_STATIC}" SRCROOT="${SRCROOT}" DSTROOT="${DSTROOT}" SYMROOT="${SYMROOT}" RC_ProjectSourceVersion="${RC_ProjectSourceVersion}" DISABLE_SDK_METADATA_PARSING=YES
+
+xcodebuild install -target dyld_app_cache_util -sdk macosx.internal -configuration ${CONFIGURATION} MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT="${OBJROOT_DYLD_APP_CACHE_UTIL}" SRCROOT="${SRCROOT}" DSTROOT="${BUILT_PRODUCTS_DIR}" SYMROOT="${SYMROOT}" RC_ProjectSourceVersion="${RC_ProjectSourceVersion}" INSTALL_PATH="/host_tools" RC_ARCHS="${NATIVE_ARCH_ACTUAL}" DISABLE_SDK_METADATA_PARSING=YES
+
+${SRCROOT}/testing/build_ninja.py ${DERIVED_FILES_DIR}/config.ninja || exit_if_error $? "Generating build.ninja failed"
+${NINJA} -C ${DERIVED_FILES_DIR} ${BUILD_TARGET} || exit_if_error $? "Ninja build failed"
diff --git a/build-scripts/dyld_tests-install.sh b/build-scripts/dyld_tests-install.sh
new file mode 100755 (executable)
index 0000000..4e79b0b
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+
+#LLBUILD=$(xcrun --sdk $SDKROOT --find llbuild 2> /dev/null)
+NINJA=${LLBUILD:-`xcrun  --sdk $SDKROOT --find ninja  2> /dev/null`}
+INSTALL_TARGET="install"
+
+if [ ! -z "$LLBUILD" ]; then
+  NINJA="$LLBUILD ninja build"
+fi
+
+if [ ! -z "$ONLY_BUILD_TEST" ]; then
+    INSTALL_TARGET="install-$BUILD_ONLY"
+fi
+
+${NINJA} -C ${DERIVED_FILES_DIR} ${INSTALL_TARGET} || exit_if_error $? "Ninja install failed"
diff --git a/build-scripts/generate-cache-config-header.sh b/build-scripts/generate-cache-config-header.sh
new file mode 100755 (executable)
index 0000000..dac8049
--- /dev/null
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+/bin/echo "" > ${DERIVED_FILE_DIR}/dyld_cache_config.h
+
+if [ -z "${ARM_SDK}" ]; then
+    # if iOS SDK not available, use MacOSX SDK
+    ARM_SDK=`xcrun -sdk macosx.internal --show-sdk-path`
+fi
+
+SHARED_REGION_FILE="${ARM_SDK}/usr/include/mach/shared_region.h"
+
+
+if [ -r "${SHARED_REGION_FILE}" ]; then
+    /bin/echo -n "#define ARM_SHARED_REGION_START "  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h
+    awk '/define SHARED_REGION_BASE_ARM[ \t]/ { print $3;}' "${SHARED_REGION_FILE}" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h
+    /bin/echo ""  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h
+
+    /bin/echo -n "#define ARM_SHARED_REGION_SIZE "  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h
+    awk '/define SHARED_REGION_SIZE_ARM[ \t]/ { print $3;}' "${SHARED_REGION_FILE}" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h
+    /bin/echo ""  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h
+
+    /bin/echo -n "#define ARM64_SHARED_REGION_START "  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h
+    awk '/define SHARED_REGION_BASE_ARM64[ \t]/ { print $3;}' "${SHARED_REGION_FILE}" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h
+    /bin/echo ""  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h
+
+    /bin/echo -n "#define ARM64_SHARED_REGION_SIZE "  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h
+    awk '/define SHARED_REGION_SIZE_ARM64[ \t]/ { print $3;}' "${SHARED_REGION_FILE}" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h
+    /bin/echo ""  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h
+
+    grep SHARED_REGION_BASE_ARM64_32 "${SHARED_REGION_FILE}" > /dev/null 2>&1
+    if [ "$?" -eq "0" ]; then
+        /bin/echo -n "#define ARM64_32_SHARED_REGION_START "  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h
+        awk '/define SHARED_REGION_BASE_ARM64_32/ { print $3;}' "${SHARED_REGION_FILE}" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h
+        /bin/echo ""  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h
+
+        /bin/echo -n "#define ARM64_32_SHARED_REGION_SIZE "  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h
+        awk '/define SHARED_REGION_SIZE_ARM64_32/ { print $3;}' "${SHARED_REGION_FILE}" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h
+        /bin/echo ""  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h
+    fi
+else
+    /bin/echo "ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'"
+    exit 1
+fi
+
+if [ -r "${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt" ]; then
+    mkdir -p "${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin"
+    cp "${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt"  "${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin"
+fi
+if [ -r "${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt" ]; then
+    mkdir -p "${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin"
+    cp "${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt"  "${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin"
+fi
+
diff --git a/build-scripts/include.sh b/build-scripts/include.sh
new file mode 100644 (file)
index 0000000..4b666a7
--- /dev/null
@@ -0,0 +1,10 @@
+exit_if_error() {
+  local exit_code=$1
+  shift
+  [[ $exit_code ]] &&               # do nothing if no error code passed
+    ((exit_code != 0)) && {         # do nothing if error code is 0
+      printf 'ERROR: %s\n' "$@" >&2 # we can use better logging here
+      exit "$exit_code"             # we could also check to make sure
+                                    # error code is numeric when passed
+    }
+}
diff --git a/build-scripts/libdyld-generate-version-headers.sh b/build-scripts/libdyld-generate-version-headers.sh
new file mode 100755 (executable)
index 0000000..45685c8
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+if [ "${DRIVERKIT}" = 1 ]; then
+    RUNTIME_PREFIX="/System/DriverKit/Runtime"
+else
+    RUNTIME_PREFIX=""
+fi
+
+/bin/mkdir -p ${DERIVED_FILES_DIR}
+/bin/mkdir -p ${DSTROOT}${RUNTIME_PREFIX}/usr/local/include/mach-o/
+
+VERSIONS=${SDKROOT}${RUNTIME_PREFIX}/usr/local/include/dyld/for_dyld_priv.inc
+DYLD_PRIV_IN=${SRCROOT}/include/mach-o/dyld_priv.h
+DYLD_PRIV_OUT=${DSTROOT}${RUNTIME_PREFIX}/usr/local/include/mach-o/dyld_priv.h
+TMPFILE=$(mktemp ${DERIVED_FILES_DIR}/dyld_priv.h.XXXXXX)
+
+/bin/chmod 0644 $TMPFILE
+
+while IFS="" read -r p || [ -n "$p" ]
+do
+  case "$p" in
+    *@VERSION_DEFS* ) cat "$VERSIONS" >> $TMPFILE ;;
+    *               ) echo "$p" >> $TMPFILE ;;
+  esac
+done < $DYLD_PRIV_IN
+
+/usr/bin/rsync -vc $TMPFILE $DYLD_PRIV_OUT
+/bin/rm -f $TMPFILE
diff --git a/build-scripts/update_dyld_shared_cache-build.sh b/build-scripts/update_dyld_shared_cache-build.sh
new file mode 100755 (executable)
index 0000000..4031c59
--- /dev/null
@@ -0,0 +1,43 @@
+
+if [ "${ACTION}" = "install" ]
+then
+    OBJROOT_LOCAL="${TARGET_TEMP_DIR}/Objects_Local"
+    xcodebuild install -target dyld_shared_cache_builder SDKROOT="${SDKROOT}" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT="${OBJROOT_LOCAL}" SRCROOT="${SRCROOT}" DSTROOT="${DSTROOT}" SYMROOT="${SYMROOT}" RC_ProjectSourceVersion="${RC_ProjectSourceVersion}" DISABLE_SDK_METADATA_PARSING=YES
+
+    # On macOS, also install dyld_shared_cache_builder to the platform so that root_util can find it.
+    if [ "${RC_PURPLE}" = "" ]
+    then
+      if [ "${PLATFORM_DIR}" != "" ]
+      then
+        # Note this is set to something like DEVELOPER_INSTALL_DIR=/Applications/Xcode.app/Contents/Developer
+        mkdir -p ${DSTROOT}/${DEVELOPER_INSTALL_DIR}/Platforms/MacOSX.platform/usr/local/bin/
+        cp ${DSTROOT}/usr/local/bin/dyld_shared_cache_builder ${DSTROOT}/${DEVELOPER_INSTALL_DIR}/Platforms/MacOSX.platform/usr/local/bin/dyld_shared_cache_builder
+      fi
+    fi
+
+    if [ "${RC_PURPLE}" = "YES" ]
+    then
+        OBJROOT_UTILS="${TARGET_TEMP_DIR}/Objects_Utils"
+        xcodebuild install -target dyld_closure_util -target dyld_shared_cache_util SDKROOT="${SDKROOT}" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT="${OBJROOT_UTILS}" SRCROOT="${SRCROOT}" DSTROOT="${DSTROOT}" SYMROOT="${SYMROOT}" RC_ProjectSourceVersion="${RC_ProjectSourceVersion}" DISABLE_SDK_METADATA_PARSING=YES
+        if [ "${RC_BRIDGE}" != "YES" ]
+        then
+            OBJROOT_SIM="${TARGET_TEMP_DIR}/Objects_Sim"
+            xcodebuild install -target update_dyld_sim_shared_cache SDKROOT="${SDKROOT}" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT="${OBJROOT_SIM}" SRCROOT="${SRCROOT}" DSTROOT="${DSTROOT}" SYMROOT="${SYMROOT}" RC_ProjectSourceVersion="${RC_ProjectSourceVersion}" DISABLE_SDK_METADATA_PARSING=YES
+        fi
+    else
+        OBJROOT_MAC="${TARGET_TEMP_DIR}/Objects_Mac"
+        xcodebuild install -target update_dyld_shared_cache_tool SDKROOT="${SDKROOT}" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT="${OBJROOT_MAC}" SRCROOT="${SRCROOT}" DSTROOT="${DSTROOT}" SYMROOT="${SYMROOT}" RC_ProjectSourceVersion="${RC_ProjectSourceVersion}" DISABLE_SDK_METADATA_PARSING=YES
+        OBJROOT_MAC="${TARGET_TEMP_DIR}/Objects2_Mac"
+        xcodebuild install -target update_dyld_shared_cache_root_mode_tool SDKROOT="${SDKROOT}" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT="${OBJROOT_MAC}" SRCROOT="${SRCROOT}" DSTROOT="${DSTROOT}" SYMROOT="${SYMROOT}" RC_ProjectSourceVersion="${RC_ProjectSourceVersion}" DISABLE_SDK_METADATA_PARSING=YES
+    fi
+fi
+
+# On macOS build the kernel linker in to /usr/lib too.  It defaults to the toolchain
+if [ "${ACTION}" != "installhdrs" ]
+then
+  if [ "${RC_PURPLE}" = "" ]
+  then
+    OBJROOT_MAC="${TARGET_TEMP_DIR}/Objects_Linker_Mac"
+    xcodebuild ${ACTION} -target libKernelCollectionBuilder SDKROOT="${SDKROOT}" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT="${OBJROOT_MAC}" SRCROOT="${SRCROOT}" DSTROOT="${DSTROOT}" SYMROOT="${SYMROOT}/usr/lib" RC_ProjectSourceVersion="${RC_ProjectSourceVersion}" LD_DYLIB_INSTALL_NAME="/usr/lib/libKernelCollectionBuilder.dylib" INSTALL_PATH="/usr/lib"
+  fi
+fi
index 6917378f47b14ac21dd83cebf71cc8ab37790a98..6e56b8ad1a990ff75f88df6759259c9d6d896e52 100644 (file)
@@ -4,7 +4,10 @@ LIBSYSTEM_LIBS[sdk=embedded*]       = -Wl,-upward-lsystem_platform     -Wl,-upwa
 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-lcorecrypto  -Wl,-upward-lcompiler_rt
 LIBSYSTEM_LIBS[sdk=driverkit*]      = -Wl,-upward-lsystem_platform     -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread  -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel    -Wl,-upward-lcompiler_rt
 
-USE_CHAINED_FIXUPS                  = 
+EXTRA_SECTIONS                      = 
+EXTRA_SECTIONS[arch=arm64e]         = -Wl,-sectcreate,__SHARED_CACHE,__cfstring,/dev/null
+
+USE_CHAINED_FIXUPS                  =
 
 INSTALL_PATH = /usr/lib/system
 
index 1a04e7b026cfeb96a452ad6eed8f7ab8c3eae8fe..f744df6c9f5c840c7b4803ad243d2480bbef1e52 100644 (file)
@@ -1,6 +1,6 @@
-
 #include "<DEVELOPER_DIR>/AppleInternal/XcodeConfig/PlatformSupportHost.xcconfig"
 
+INSTALL_PATH = $(SIMULATOR_RUNTIME_BUNDLE_INSTALL_DIR)/Contents/Resources
 
 //:configuration = Debug
 GCC_PREPROCESSOR_DEFINITIONS = BUILDING_CACHE_BUILDER=1 DEBUG=1
diff --git a/doc/man/man1/closured.1 b/doc/man/man1/closured.1
deleted file mode 100644 (file)
index a13b005..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-.Dd 3/1/17
-.Dt closured 1
-.Os Darwin
-.Sh NAME
-.Nm closured
-.Nd Daemon for building dyld closures.
-.Sh SYNOPSIS
-.Nm closured is a launchd managed daemon.
-.Sh DESCRIPTION
-.Nm closured is a launchd managed daemon.
index 530f708330320ba248317154523a44099c29a36a..605f4549bfead34be4fa47e56106ee738899d316 100644 (file)
@@ -1,4 +1,4 @@
-.TH DYLD 1 "June 1, 2017" "Apple Inc."
+.TH DYLD 1 "June 1, 2020" "Apple Inc."
 .SH NAME
 dyld \- the dynamic linker
 .SH SYNOPSIS
@@ -51,8 +51,6 @@ DYLD_PRINT_DOFS
 DYLD_PRINT_RPATHS
 .br
 DYLD_SHARED_CACHE_DIR
-.br
-DYLD_SHARED_CACHE_DONT_VALIDATE
 .SH DESCRIPTION
 The dynamic linker checks the following environment variables during the launch
 of each process.
@@ -229,13 +227,7 @@ that expansion was successful or not.
 .TP
 .B DYLD_SHARED_CACHE_DIR
 This is a directory containing dyld shared cache files.  This variable can be used in
-conjunction with DYLD_SHARED_REGION=private and DYLD_SHARED_CACHE_DONT_VALIDATE
-to run a process with an alternate shared cache.
-.TP
-.B DYLD_SHARED_CACHE_DONT_VALIDATE
-Causes dyld to not check that the inode and mod-time of files in the shared cache match
-the requested dylib on disk. Thus a program can be made to run with the dylib in the
-shared cache even though the real dylib has been updated on disk.
+conjunction with DYLD_SHARED_REGION=private to run a process with an alternate shared cache.
 .TP
 .SH DYNAMIC LIBRARY LOADING
 Unlike many other operating systems, Darwin does not locate dependent dynamic libraries
index e35e51bcf4205f93caaf6d79613b6fa0f86370a5..c31d3c4b9763e416274edd44e08f54c1066b67af 100644 (file)
@@ -1,6 +1,6 @@
 .\" Man page generated from reStructuredText.
 .
-.TH "DYLD_USAGE" "1" "2018-07-28" "" "dyld"
+.TH "DYLD_USAGE" "1" "2020-04-13" "" "dyld"
 .SH NAME
 dyld_usage \- report dynamic linker activity in real-time
 .
@@ -32,7 +32,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
 ..
 .SH SYNOPSIS
 .sp
-\fBdyld_usage\fP \fB[\-e] [\-f mode] [\-t seconds] [\-R rawfile [\-S start_time]
+\fBdyld_usage\fP \fB[\-e] [\-f mode] [\-j] [\-h] [\-t seconds] [\-R rawfile [\-S start_time]
 [\-E end_time]] [pid | cmd [pid | cmd] ...]\fP
 .SH DESCRIPTION
 .sp
@@ -56,16 +56,27 @@ for maximum data display.
 .B \-e
 Exclude the specified list of pids and commands from the sample, and exclude
 \fBdyld_usage\fP by default.
+.INDENT 7.0
+.TP
+.B \-j
+.UNINDENT
+.sp
+Display output in JSON format.
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-h
+Display usage information and exit.
 .UNINDENT
 .INDENT 0.0
 .TP
 .B \-R
-specifies a raw trace file to process.
+Specify a raw trace file to process.
 .UNINDENT
 .INDENT 0.0
 .TP
 .B \-t
-specifies timeout in seconds (for use in automated tools).
+Specify timeout in seconds (for use in automated tools).
 .UNINDENT
 .SH DISPLAY
 .sp
@@ -107,6 +118,6 @@ processes named Mail.
 .SH AUTHOR
 Apple, Inc.
 .SH COPYRIGHT
-2000-2018, Apple, Inc.
+2000-2020, Apple, Inc.
 .\" Generated by docutils manpage writer.
 .
diff --git a/doc/man/man1/update_dyld_shared_cache.1 b/doc/man/man1/update_dyld_shared_cache.1
deleted file mode 100644 (file)
index f1c2330..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-.Dd June 1, 2017
-.Dt update_dyld_shared_cache 1
-.Os Darwin
-.Sh NAME
-.Nm update_dyld_shared_cache
-.Nd "Updates dyld's shared cache"
-.Sh SYNOPSIS
-.Nm
-.Op Fl root Ar directory 
-.Op Fl overlay Ar directory 
-.Op Fl arch Ar arch 
-.Op Fl force 
-.Op Fl debug
-.Op Fl universal_boot
-.Sh DESCRIPTION
-.Nm update_dyld_shared_cache
-ensures that dyld's shared cache is up-to-date.  This tool is normally
-only run by Apple's Installer and Software Update, as they are the only
-official ways for OS dylibs to be updated.  But if for some reason you
-used another mechanism to alter an OS dylib, you should manually run
-.Nm update_dyld_shared_cache . 
-.Pp
-Note that the new cache does not take effect until the OS is rebooted.  
-.Pp
-The dyld shared cache
-is mapped by dyld into a process at launch time. Later, when loading
-any mach-o image, dyld will first check if is in the share cache, and if
-it is will use that pre-bound version instead of opening, mapping, and binding
-the original file.  This results in significant performance improvements to
-launch time.
-.Pp
-.Nm update_dyld_shared_cache
-scans the directory /System/Library/Receipts/ for .bom files which list all files
-installed.  From that info it creates the set of OS dylibs to build into the dyld cache.
-.Pp
-.Nm update_dyld_shared_cache
-builds a separate cache file for each architecture.  The cache files and a readable text
-map of the cached are generated to /var/db/dyld.
-.Pp
-You must be root to run this tool.
-.Pp
-The options are as follows:
-.Bl -tag
-.It Fl root Ar directory
-This option specifies the root of an OS installation for which dyld's
-shared cache should be updated.  This is used by the Installer to update the
-dyld shared cache in a partition other than the one you into which you are currently 
-booted.  The cache files are created in the var/db/dyld directory of the specified directory.
-Note: if you are manually doing this, be sure to run the update_dyld_shared_cache tool
-that is in the partition being updated.  This assures the cache format created will
-match that expected when booting off that partition. 
-.It Fl overlay Ar directory
-This option specifies the root of a sparse directory tree.  When building
-the dyld shared cache, any corresponding mach-o files in the sparse directory 
-will override those in the boot partition.  This is used by Software
-Update to build a dyld shared cache for the update that is about to be
-installed.  The cache files
-are created in the var/db/dyld directory of the specified directory.
-.It Fl arch Ar arch
-By default 
-.Nm update_dyld_shared_cache 
-generates cache files for all architecture that the current machine
-can execute.  You can override this behavior by specifying one or more -arch options and list
-exactly which architectures should have their shared caches updated.
-.It Fl force
-This option will cause 
-.Nm update_dyld_shared_cache
-to regenerated the shared cache files even if they appear to be already up-to-date. 
-.It Fl debug
-This option prints out additional information about the work being done.
-.It Fl universal_boot
-This option builds caches for all machines.
-.El
-.Sh SEE ALSO
-.Xr dyld 1
index aa27dbfc76d7569b3bb6128a791e98432be87e4d..38ed7fcd0a342c6eef4b7df0e0c7ac68ec4e1d7b 100644 (file)
@@ -1,4 +1,4 @@
-.Dd April 17, 2006
+.Dd May 11, 2020
 .Os
 .Dt DLOPEN_PREFLIGHT 3
 .Sh NAME
 examines the mach-o file specified by 
 .Fa path .
 It checks if the file and libraries it depends on are all compatible with the current process. 
-That is, they contain the correct architecture and are not otherwise ABI incompatible. 
+That is, they contain the correct architecture and are not otherwise ABI incompatible.
+.Pp
+.Fn dlopen_preflight
+was created for the PowerPC to Intel transition for use by apps with plugins that the user chooses to load.
+The app could use dlopen_preflight() to show only loadable plugins to the user (such as in a menu).
+.Pp
+This is potentially an expensive call because it may internally do the same as dlopen/dlclose.  Only
+use dlopen_preflight() if you need to show the user a list of potentially loadable plugins.
 .Pp
 .Fn dlopen_preflight
 was first available in Mac OS X 10.5.
index 8dfb86df7c444ff79d7c60ae021691ddeb62d7f1..c01dab267f2922ce66b3ad8c849031f28fd60f26 100644 (file)
@@ -76,7 +76,7 @@ is out of range zero is returned.
 .Fn _dyld_get_image_name
 returns the name of the image indexed by
 .Fa image_index.
-The C-string continues to be owned by dyld and should not deleted.
+The C-string continues to be owned by dyld and should not be deleted.
 If 
 .Fa image_index
 is out of range NULL is returned.
index 91545177b7f064a8ccb95600ad0cbd2c85bbb897..5169a9428bedbb39f38472fa7e473188b42582a6 100644 (file)
@@ -232,18 +232,15 @@ for name in os.listdir(command_guide_path):
         header = f.readline().rstrip('\n')
 
         if len(header) != len(title):
-            print >>sys.stderr, (
-                "error: invalid header in %r (does not match title)" % (
-                    file_subpath,))
+            print("error: invalid header in {} (does not match title)".
+            format(file_subpath), file=sys.stderr)
         if ' - ' not in title:
-            print >>sys.stderr, (
-                ("error: invalid title in %r "
-                 "(expected '<name> - <description>')") % (
-                    file_subpath,))
+            print("error: invalid title in {} (expected '<name> - <description>')".
+            format(file_subpath), file=sys.stderr)
 
         # Split the name out of the title.
         name,description = title.split(' - ', 1)
-        man_pages.append((file_subpath.replace('.rst',''), name,
+        man_pages.append((name.replace('.rst',''), name,
                           description, man_page_authors, 1))
 
 # If true, show URL addresses after external links.
index 18822d902a59fbe9777b6fca569fb541f2380bdf..1527c2afb43ca49eea3b9ee9d918a4dc25cef1ba 100644 (file)
@@ -1,10 +1,10 @@
 dyld_usage - report dynamic linker activity in real-time
-==========================================================
+========================================================
 
 SYNOPSIS
 --------
 
-:program:`dyld_usage` **[-e] [-f mode] [-t seconds] [-R rawfile [-S start_time]
+:program:`dyld_usage` **[-e] [-f mode] [-j] [-h] [-t seconds] [-R rawfile [-S start_time]
 [-E end_time]] [pid | cmd [pid | cmd] ...]**
 
 DESCRIPTION
@@ -31,13 +31,21 @@ OPTIONS
   Exclude the specified list of pids and commands from the sample, and exclude
   :program:`dyld_usage` by default.
 
+  .. option:: -j
+
+  Display output in JSON format.
+
+.. option:: -h
+
+  Display usage information and exit.
+
 .. option:: -R
 
-  specifies a raw trace file to process.
+  Specify a raw trace file to process.
 
 .. option:: -t
 
-  specifies timeout in seconds (for use in automated tools).
+  Specify timeout in seconds (for use in automated tools).
 
 DISPLAY
 -------
index c8b67886d851cd6f3740492127874fa38846622d..f0de62a49dc1a62974f6dae81e11f378f4d5d121 100644 (file)
@@ -14,8 +14,7 @@
                                F94182D61E60E74E00D8EF25 /* pre-platform builds */,
                        );
                        dependencies = (
-                               D8668AD01ECE335F005E7D31 /* PBXTargetDependency */,
-                               F94182D81E60F0BE00D8EF25 /* PBXTargetDependency */,
+                               C1BF4DC32359390A00B0F1AE /* PBXTargetDependency */,
                                F94182DA1E60F0C000D8EF25 /* PBXTargetDependency */,
                                F94182DC1E60F16900D8EF25 /* PBXTargetDependency */,
                                C187B90C1FE067590042D3B7 /* PBXTargetDependency */,
                        name = update_dyld_shared_cache;
                        productName = update_dyld_shared_cache;
                };
+               C1BDD43C234E8FA00095C7DC /* dyld_executables */ = {
+                       isa = PBXAggregateTarget;
+                       buildConfigurationList = C1BDD43F234E8FA00095C7DC /* Build configuration list for PBXAggregateTarget "dyld_executables" */;
+                       buildPhases = (
+                       );
+                       dependencies = (
+                               F9A8E1B024120DD000CEB6BF /* PBXTargetDependency */,
+                               C1B4759723E65F9600515793 /* PBXTargetDependency */,
+                               C1C6403B23E4EC3000ED4B46 /* PBXTargetDependency */,
+                               C1C6403923E4EC1C00ED4B46 /* PBXTargetDependency */,
+                               C1C6403723E4EC1300ED4B46 /* PBXTargetDependency */,
+                       );
+                       name = dyld_executables;
+                       productName = dyld_executables;
+               };
                F908134211D3ED0B00626CC1 /* libdyld */ = {
                        isa = PBXAggregateTarget;
                        buildConfigurationList = F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */;
@@ -60,6 +74,7 @@
                        );
                        dependencies = (
                                37382F6A230CB46500E375CE /* PBXTargetDependency */,
+                               C14C3568230531EA0059E04C /* PBXTargetDependency */,
                        );
                        name = dyld_tests;
                        productName = dyld_tests;
                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 */; };
-               C11ECA90233C307C0011726F /* SharedCacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C11ECA8E233C307C0011726F /* SharedCacheBuilder.cpp */; };
-               C11ECA91233C307C0011726F /* SharedCacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C11ECA8E233C307C0011726F /* SharedCacheBuilder.cpp */; };
+               C116F19C23F4B11B002D386B /* RootsChecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C116F19A23F4B11B002D386B /* RootsChecker.cpp */; };
+               C116F19D23F4B11B002D386B /* RootsChecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C116F19A23F4B11B002D386B /* RootsChecker.cpp */; };
+               C116F19E23F4B11B002D386B /* RootsChecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C116F19A23F4B11B002D386B /* RootsChecker.cpp */; };
+               C116F19F23F4B11B002D386B /* RootsChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = C116F19B23F4B11B002D386B /* RootsChecker.h */; };
+               C116F1A223F4D73A002D386B /* RootsChecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C116F19A23F4B11B002D386B /* RootsChecker.cpp */; };
+               C116F1A323F4D73B002D386B /* RootsChecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C116F19A23F4B11B002D386B /* RootsChecker.cpp */; };
+               C116F1A423F4D742002D386B /* RootsChecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C116F19A23F4B11B002D386B /* RootsChecker.cpp */; };
                C11ECA92233C307C0011726F /* SharedCacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C11ECA8E233C307C0011726F /* SharedCacheBuilder.cpp */; };
                C11ECA93233C307C0011726F /* SharedCacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C11ECA8E233C307C0011726F /* SharedCacheBuilder.cpp */; };
                C11ECA94233C307C0011726F /* SharedCacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C11ECA8E233C307C0011726F /* SharedCacheBuilder.cpp */; };
                C123176B22B9B4F00046E3E5 /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; };
                C1436B2C203BE67D00028AF1 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
+               C14965E722BDCF8300568D15 /* dyld_app_cache_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C14965DB22BDCE7C00568D15 /* dyld_app_cache_util.cpp */; };
+               C14965E822BEBF2300568D15 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+               C14965E922BEBF2800568D15 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
+               C14965EA22BEC04800568D15 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+               C14965EB22BEC05000568D15 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
+               C14965EC22BEC05800568D15 /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
+               C14965ED22C09B6100568D15 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
+               C14965EE22C31F7C00568D15 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; };
+               C14965F022C3203200568D15 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
+               C14965F122C3203E00568D15 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
+               C14965F622C327FB00568D15 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
+               C14965F722C3280B00568D15 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
+               C14965F822C3281A00568D15 /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; };
+               C14C3563230531830059E04C /* testing/run-static/run-static.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C14C3562230531830059E04C /* testing/run-static/run-static.cpp */; };
+               C14C3569230537630059E04C /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+               C14C356A2305376A0059E04C /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+               C14C356B230539BE0059E04C /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
+               C14C356C230539C20059E04C /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
                C172C9DD20252CB500159311 /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
                C176CB5C2321AB74009C1259 /* 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 */; };
                C187B9181FE068260042D3B7 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
                C187B91B1FE0683F0042D3B7 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
                C187B91E1FE0684C0042D3B7 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
+               C18839E523480866004E30FA /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
                C18A75F9209A1AF600DC01BB /* JSONReader.mm in Sources */ = {isa = PBXBuildFile; fileRef = C18A75F8209A1AF600DC01BB /* JSONReader.mm */; };
+               C18F05372374D5B700DC6CCA /* libtest_support.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3721A635230CABAF00594066 /* libtest_support.a */; };
                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 */; };
+               C1BDD443234EA7DD0095C7DC /* AppCacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1BDD441234EA7DD0095C7DC /* AppCacheBuilder.cpp */; };
+               C1BDD446234EAF500095C7DC /* MachOAppCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1BDD444234EAF500095C7DC /* MachOAppCache.cpp */; };
+               C1BF4DAB2357B14700B0F1AE /* kernel_collection_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1BF4DA92357B14700B0F1AE /* kernel_collection_builder.cpp */; };
+               C1BF4DB4235925A000B0F1AE /* kernel_collection_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1BF4DA92357B14700B0F1AE /* kernel_collection_builder.cpp */; };
+               C1BF4DB6235925B400B0F1AE /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; };
+               C1BF4DB7235925BA00B0F1AE /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+               C1BF4DB82359385700B0F1AE /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; };
+               C1BF4DB92359386C00B0F1AE /* AppCacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1BDD441234EA7DD0095C7DC /* AppCacheBuilder.cpp */; };
+               C1BF4DBA2359389E00B0F1AE /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F94C22241E513CA90079E5DD /* CoreFoundation.framework */; };
+               C1BF4DBB235938AC00B0F1AE /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
+               C1BF4DBC235938B000B0F1AE /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
+               C1BF4DBD235938B600B0F1AE /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
+               C1BF4DBE235938BA00B0F1AE /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
+               C1BF4DBF235938BD00B0F1AE /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+               C1BF4DC0235938CB00B0F1AE /* MachOAppCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1BDD444234EAF500095C7DC /* MachOAppCache.cpp */; };
+               C1BF4DC1235938D400B0F1AE /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
+               C1BF4DC623594FBC00B0F1AE /* kernel_collection_builder.h in usr|local|include */ = {isa = PBXBuildFile; fileRef = C1BF4DAA2357B14700B0F1AE /* kernel_collection_builder.h */; };
+               C1BFD0502307CE99007D7CDC /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F94C22241E513CA90079E5DD /* CoreFoundation.framework */; };
                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 */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; };
-               C1D2683F1FE98D4F009F115B /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
-               C1F003CC213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; };
                C1F003CE213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; };
                C1F003CF213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; };
                C1F003D0213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; };
+               DE49C496238EC55300CD7FFB /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
+               DE49C497238EC57B00CD7FFB /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+               DE49C499238EC59500CD7FFB /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+               DE49C49A238EC5AD00CD7FFB /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
+               DE49C49C238EC60D00CD7FFB /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
+               DE49C4A3238EEE3400CD7FFB /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; };
                DE728E53210CD74E00EB5409 /* dyld_usage.1 in Install man page */ = {isa = PBXBuildFile; fileRef = DE728E52210CD6B700EB5409 /* dyld_usage.1 */; };
+               DE9A811323982A3A00664840 /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; };
+               E9C2FAD823AA8B5C0077E966 /* IMPCaches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E9C2FAD723AA8B5C0077E966 /* IMPCaches.cpp */; };
+               E9EC3199240B0B95001705D6 /* IMPCaches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E9C2FAD723AA8B5C0077E966 /* IMPCaches.cpp */; };
+               E9EC319A240B0BA8001705D6 /* IMPCaches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E9C2FAD723AA8B5C0077E966 /* IMPCaches.cpp */; };
+               E9EC319D240B18DC001705D6 /* JSONReader.mm in Sources */ = {isa = PBXBuildFile; fileRef = C18A75F8209A1AF600DC01BB /* JSONReader.mm */; };
+               E9EC319E240B18E6001705D6 /* JSONReader.mm in Sources */ = {isa = PBXBuildFile; fileRef = C18A75F8209A1AF600DC01BB /* JSONReader.mm */; };
+               E9EC31A0240B19C1001705D6 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E9EC319F240B19C1001705D6 /* Foundation.framework */; };
                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 */; };
                F908136E11D3FB3A00626CC1 /* dlopen_preflight.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */; };
                F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */; };
                F91BFAC821684FCC007F10AB /* fixup-chains.h in Headers */ = {isa = PBXBuildFile; fileRef = F91BFAC721684FCC007F10AB /* fixup-chains.h */; settings = {ATTRIBUTES = (Public, ); }; };
-               F92015701DDFEBAF00816A4A /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; };
                F92015711DE3F3B000816A4A /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.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 */; };
                F92C7DE721E59840000D12B5 /* start_glue.s in Sources */ = {isa = PBXBuildFile; fileRef = F9D49CCB1458B95200F86ADD /* start_glue.s */; };
                F92C7DE821E59840000D12B5 /* Tracing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37D7DAFE1E96F0ED00D52CEA /* Tracing.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 */; };
                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 */; };
-               F94182D51E60A2F100D8EF25 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
                F9460DCE1E0A000600FEC613 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
-               F94C22251E513CA90079E5DD /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F94C22241E513CA90079E5DD /* CoreFoundation.framework */; };
                F94DB9040F0A9B1700323715 /* ImageLoaderMachOClassic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F94DB9000F0A9B1700323715 /* ImageLoaderMachOClassic.cpp */; };
                F94DB9050F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */; settings = {COMPILER_FLAGS = "-O3"; }; };
                F95090E51C5AD1E80031F81D /* dyld_process_info.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F95090E41C5AD1B30031F81D /* dyld_process_info.cpp */; };
                F9653F941FAE51ED008B5D93 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
                F96D19A81D93661A007AF3CE /* APIs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19A51D9363D6007AF3CE /* APIs.cpp */; };
                F96D19C01D94BFCE007AF3CE /* AllImages.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19A61D9363D6007AF3CE /* AllImages.cpp */; };
+               F971EC752342D373000BCEAA /* MachOAnalyzerSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EC742342D2CF000BCEAA /* MachOAnalyzerSet.cpp */; };
+               F971EC762342DF03000BCEAA /* MachOAnalyzerSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EC742342D2CF000BCEAA /* MachOAnalyzerSet.cpp */; };
+               F971EC772343CD46000BCEAA /* MachOAnalyzerSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EC742342D2CF000BCEAA /* MachOAnalyzerSet.cpp */; settings = {COMPILER_FLAGS = "-fno-exceptions"; }; };
+               F971EC782343CD63000BCEAA /* MachOAnalyzerSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EC742342D2CF000BCEAA /* MachOAnalyzerSet.cpp */; };
+               F971EC7B2343CD9A000BCEAA /* MachOAnalyzerSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EC742342D2CF000BCEAA /* MachOAnalyzerSet.cpp */; };
+               F971EC7C2343CDA4000BCEAA /* MachOAnalyzerSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EC742342D2CF000BCEAA /* MachOAnalyzerSet.cpp */; };
+               F971EC7D2343CDB9000BCEAA /* MachOAnalyzerSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F971EC742342D2CF000BCEAA /* MachOAnalyzerSet.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 */; };
+               F97D395523A1CBB600BD3B5A /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
+               F97D395623A1CBB600BD3B5A /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
+               F97D395723A1CBCA00BD3B5A /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
+               F97D395823A97B2C00BD3B5A /* dsc_extractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9CE30781208F1B50098B590 /* dsc_extractor.cpp */; };
                F97FF3611C23640C000ACDD2 /* nocr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F97FF35F1C236402000ACDD2 /* nocr.cpp */; };
                F97FF3641C237F68000ACDD2 /* nocr.1 in install man page */ = {isa = PBXBuildFile; fileRef = F97FF3631C237F5C000ACDD2 /* nocr.1 */; };
                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 */; };
-               F98692231DC403F900CBEDE6 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
                F98C78F00F7C02E8006257D2 /* dsc_iterator.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */; };
-               F98E37792332D048003706B4 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
-               F98E377A2332D048003706B4 /* ClosureFileSystemNull.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1F003CB213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp */; };
-               F98E377B2332D048003706B4 /* Closure.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA6F1F50FDE5003BF8A7 /* Closure.cpp */; };
-               F98E377C2332D048003706B4 /* ClosureWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */; };
-               F98E377D2332D048003706B4 /* ClosureFileSystemPhysical.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C1D268341FE0A52D009F115B /* ClosureFileSystemPhysical.cpp */; };
-               F98E377E2332D048003706B4 /* ClosureBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DFEA771F54FACF003BF8A7 /* ClosureBuilder.cpp */; };
-               F98E377F2332D048003706B4 /* MachOFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6191F5F1BFA0030C490 /* MachOFile.cpp */; };
-               F98E37802332D048003706B4 /* MachOLoaded.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6151F5C967C0030C490 /* MachOLoaded.cpp */; };
-               F98E37812332D048003706B4 /* MachOAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */; };
                F98E37822332D048003706B4 /* update_dyld_shared_cache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692151DC3EF6C00CBEDE6 /* update_dyld_shared_cache.cpp */; };
-               F98E37832332D048003706B4 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
-               F98E37842332D048003706B4 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; };
-               F98E37852332D048003706B4 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; };
-               F98E37862332D048003706B4 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; };
-               F98E37872332D048003706B4 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; };
-               F98E37882332D048003706B4 /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; };
-               F98E37892332D048003706B4 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; };
-               F98E378A2332D048003706B4 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
-               F98E378C2332D048003706B4 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F94C22241E513CA90079E5DD /* CoreFoundation.framework */; };
-               F98E378D2332D048003706B4 /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; };
-               F98E378F2332D048003706B4 /* update_dyld_shared_cache.1 in usr|share|man|man1 */ = {isa = PBXBuildFile; fileRef = F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */; };
                F98F1FBF1E4031F800EF868D /* dyld_process_info.h in Headers */ = {isa = PBXBuildFile; fileRef = F95090D01C5AB89A0031F81D /* dyld_process_info.h */; settings = {ATTRIBUTES = (Private, ); }; };
                F99006DD1E411BA70013456D /* dyld_images.h in Headers */ = {isa = PBXBuildFile; fileRef = F98D274C0AA79D7400416316 /* dyld_images.h */; settings = {ATTRIBUTES = (Public, ); }; };
                F99006DE1E411BBC0013456D /* dyld.h in Headers */ = {isa = PBXBuildFile; fileRef = F9ED4CEA0630A80600DF4E74 /* dyld.h */; settings = {ATTRIBUTES = (Public, ); }; };
                F9CE307A1208F1B50098B590 /* dsc_extractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9CE30781208F1B50098B590 /* dsc_extractor.cpp */; };
                F9CE307B1208F1C60098B590 /* dsc_extractor.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9CE30791208F1B50098B590 /* dsc_extractor.h */; };
                F9D1001814D8D13D00099D91 /* dsc_extractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9CE30781208F1B50098B590 /* dsc_extractor.cpp */; };
-               F9D1001D14D8D19500099D91 /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; };
-               F9D238DB0A9E2FD0002B55C7 /* update_dyld_shared_cache.1 in usr|share|man|man1 */ = {isa = PBXBuildFile; fileRef = F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */; };
                F9D49CCC1458B95200F86ADD /* start_glue.s in Sources */ = {isa = PBXBuildFile; fileRef = F9D49CCB1458B95200F86ADD /* start_glue.s */; };
-               F9D8623F1DC41043000A199A /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; };
-               F9D862401DC57A27000A199A /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; };
                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 */; };
                F9D8624C1DC97717000A199A /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
                F9ED4CE30630A7F100DF4E74 /* ImageLoaderMachO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD30630A7F100DF4E74 /* ImageLoaderMachO.cpp */; };
                F9ED4CE50630A7F100DF4E74 /* stub_binding_helper.s in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */; };
                F9F256360639DBCC00A7427D /* dyldLock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CCC0630A7F100DF4E74 /* dyldLock.cpp */; };
-               F9F2A5700F7AEEE300B7C9EB /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; };
                F9F76FB01E09CDF400828678 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; };
+               F9FA17F4235A71DB009B0907 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXBuildRule section */
                        remoteGlobalIDString = 37A0AD0A1C15FFF500731E50;
                        remoteInfo = update_dyld_shared_cache;
                };
-               C1033EA722611306004407FB /* PBXContainerItemProxy */ = {
+               C14C3567230531EA0059E04C /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        proxyType = 1;
-                       remoteGlobalIDString = F9D1001114D8D0BA00099D91;
-                       remoteInfo = dsc_extractor;
+                       remoteGlobalIDString = C14C355F230531820059E04C;
+                       remoteInfo = "run-static";
                };
                C187B90B1FE067590042D3B7 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        remoteGlobalIDString = C187B8FF1FE063A40042D3B7;
                        remoteInfo = libslc_builder;
                };
-               D8668ACF1ECE335F005E7D31 /* PBXContainerItemProxy */ = {
+               C18F05352374D5B100DC6CCA /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        proxyType = 1;
-                       remoteGlobalIDString = F97C61A61DBAD1A900A84CD7;
-                       remoteInfo = dyld_closure_util;
+                       remoteGlobalIDString = 3721A634230CABAF00594066;
+                       remoteInfo = test_support;
                };
-               F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */ = {
+               C1B4759623E65F9600515793 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        proxyType = 1;
-                       remoteGlobalIDString = F9ED4C9E0630A76B00DF4E74;
-                       remoteInfo = libdyld.dylib;
+                       remoteGlobalIDString = 37F597CC2061EB4200F9B6F9;
+                       remoteInfo = dyld_usage;
                };
-               F94182D71E60F0BE00D8EF25 /* PBXContainerItemProxy */ = {
+               C1BF4DC22359390A00B0F1AE /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = C1BF4DAF2359254500B0F1AE;
+                       remoteInfo = libKernelCollectionBuilder;
+               };
+               C1C6403623E4EC1300ED4B46 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = F9D1001114D8D0BA00099D91;
+                       remoteInfo = dsc_extractor;
+               };
+               C1C6403823E4EC1C00ED4B46 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        proxyType = 1;
                        remoteGlobalIDString = F99B8E550FEC10F600701838;
                        remoteInfo = dyld_shared_cache_util;
                };
+               C1C6403A23E4EC3000ED4B46 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = F97C61A61DBAD1A900A84CD7;
+                       remoteInfo = dyld_closure_util;
+               };
+               F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = F9ED4C9E0630A76B00DF4E74;
+                       remoteInfo = libdyld.dylib;
+               };
                F94182D91E60F0C000D8EF25 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        remoteGlobalIDString = F9D1001114D8D0BA00099D91;
                        remoteInfo = dsc_extractor;
                };
-               F96543A01E343601003C5540 /* PBXContainerItemProxy */ = {
-                       isa = PBXContainerItemProxy;
-                       containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
-                       proxyType = 1;
-                       remoteGlobalIDString = F97C61A61DBAD1A900A84CD7;
-                       remoteInfo = dyld_closure_util;
-               };
-               F99B8EB10FEC220C00701838 /* PBXContainerItemProxy */ = {
+               F9A8E1AF24120DD000CEB6BF /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
                        proxyType = 1;
-                       remoteGlobalIDString = F99B8E550FEC10F600701838;
-                       remoteInfo = dyld_shared_cache_util;
+                       remoteGlobalIDString = F9556D3820C1F896004DF62A;
+                       remoteInfo = dyld_info;
                };
                F9B4D77F12AD9736000605A6 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        name = "install ktrace codes file";
                        runOnlyForDeploymentPostprocessing = 1;
                };
+               37D47F8923EDFA2400C5C000 /* CopyFiles */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 2147483647;
+                       dstPath = "";
+                       dstSubfolderSpec = 7;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                37F597CB2061EB4200F9B6F9 /* Install man page */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 2147483647;
                        name = "Install man page";
                        runOnlyForDeploymentPostprocessing = 1;
                };
+               C14965DE22BDCF6800568D15 /* CopyFiles */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 2147483647;
+                       dstPath = /usr/share/man/man1/;
+                       dstSubfolderSpec = 0;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 1;
+               };
+               C14C355E230531820059E04C /* 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;
                        name = "usr|local|include|mach-o";
                        runOnlyForDeploymentPostprocessing = 1;
                };
+               C1BF4DC523594FA200B0F1AE /* usr|local|include */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 8;
+                       dstPath = "$(INSTALL_PATH_PREFIX)$(INSTALL_LOCATION)/usr/local/include";
+                       dstSubfolderSpec = 0;
+                       files = (
+                               C1BF4DC623594FBC00B0F1AE /* kernel_collection_builder.h in usr|local|include */,
+                       );
+                       name = "usr|local|include";
+                       runOnlyForDeploymentPostprocessing = 1;
+               };
                F908137211D3FB5000626CC1 /* usr|share|man|man1 */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 8;
                        name = "usr|local|include|mach-o";
                        runOnlyForDeploymentPostprocessing = 1;
                };
-               F98E378E2332D048003706B4 /* usr|share|man|man1 */ = {
-                       isa = PBXCopyFilesBuildPhase;
-                       buildActionMask = 8;
-                       dstPath = "$(INSTALL_PATH_PREFIX)$(INSTALL_LOCATION)/usr/$(LOCAL)/share/man/man1";
-                       dstSubfolderSpec = 0;
-                       files = (
-                               F98E378F2332D048003706B4 /* update_dyld_shared_cache.1 in usr|share|man|man1 */,
-                       );
-                       name = "usr|share|man|man1";
-                       runOnlyForDeploymentPostprocessing = 1;
-               };
                F9C69EFC14EC8AB8009CAE2E /* usr|local|include */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 8;
                        name = "usr|local|include";
                        runOnlyForDeploymentPostprocessing = 1;
                };
-               F9D238DD0A9E2FEE002B55C7 /* usr|share|man|man1 */ = {
-                       isa = PBXCopyFilesBuildPhase;
-                       buildActionMask = 8;
-                       dstPath = "$(INSTALL_PATH_PREFIX)$(INSTALL_LOCATION)/usr/$(LOCAL)/share/man/man1";
-                       dstSubfolderSpec = 0;
-                       files = (
-                               F9D238DB0A9E2FD0002B55C7 /* update_dyld_shared_cache.1 in usr|share|man|man1 */,
-                       );
-                       name = "usr|share|man|man1";
-                       runOnlyForDeploymentPostprocessing = 1;
-               };
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
                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>"; };
+               37B5254F2475E91F00404300 /* generate-cache-config-header.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "generate-cache-config-header.sh"; sourceTree = "<group>"; };
                37CE9D192321A7EB001FBA91 /* chroot_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = chroot_util.cpp; sourceTree = "<group>"; };
+               37D47F8823EC961500C5C000 /* libdyld-generate-version-headers.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "libdyld-generate-version-headers.sh"; sourceTree = "<group>"; };
+               37D5ACEC23A830C200AE4F57 /* Defines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Defines.h; path = dyld3/Defines.h; sourceTree = "<group>"; };
+               37D7995623AAA8AD00B314BC /* include.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = include.sh; sourceTree = "<group>"; };
+               37D7995723AAA8AD00B314BC /* dyld_tests-build.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "dyld_tests-build.sh"; sourceTree = "<group>"; };
+               37D7995823AAA8AD00B314BC /* ContainerizedTestRunner-build-everything.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "ContainerizedTestRunner-build-everything.sh"; sourceTree = "<group>"; };
+               37D7995923AAA8AD00B314BC /* dyld_tests-install.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "dyld_tests-install.sh"; 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; };
+               A5E247802369485F00BDED2F /* dlfcn_private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = dlfcn_private.h; path = include/dlfcn_private.h; sourceTree = "<group>"; };
+               C116F19A23F4B11B002D386B /* RootsChecker.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = RootsChecker.cpp; path = dyld3/RootsChecker.cpp; sourceTree = "<group>"; };
+               C116F19B23F4B11B002D386B /* RootsChecker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = RootsChecker.h; path = dyld3/RootsChecker.h; sourceTree = "<group>"; };
+               C116F1A523F5BB39002D386B /* update_dyld_shared_cache-build.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "update_dyld_shared_cache-build.sh"; sourceTree = "<group>"; };
                C11ECA8E233C307C0011726F /* SharedCacheBuilder.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SharedCacheBuilder.cpp; path = "dyld3/shared-cache/SharedCacheBuilder.cpp"; sourceTree = "<group>"; };
                C11ECA8F233C307C0011726F /* SharedCacheBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SharedCacheBuilder.h; path = "dyld3/shared-cache/SharedCacheBuilder.h"; sourceTree = "<group>"; };
+               C14965DB22BDCE7C00568D15 /* dyld_app_cache_util.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_app_cache_util.cpp; path = dyld3/dyld_app_cache_util.cpp; sourceTree = "<group>"; };
+               C14965E022BDCF6800568D15 /* dyld_app_cache_util */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_app_cache_util; sourceTree = BUILT_PRODUCTS_DIR; };
+               C14C3560230531820059E04C /* run-static */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "run-static"; sourceTree = BUILT_PRODUCTS_DIR; };
+               C14C3562230531830059E04C /* testing/run-static/run-static.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = "testing/run-static/run-static.cpp"; sourceTree = "<group>"; };
                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>"; };
                C18A75F6209A18AC00DC01BB /* JSONReader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = JSONReader.h; path = dyld3/JSONReader.h; sourceTree = "<group>"; };
                C18A75F8209A1AF600DC01BB /* JSONReader.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = JSONReader.mm; path = dyld3/JSONReader.mm; sourceTree = "<group>"; };
                C18F095221925E7600034B68 /* Map.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Map.h; path = dyld3/Map.h; sourceTree = "<group>"; };
                C19D50142087E4BC00563DAF /* SupportedArchs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SupportedArchs.h; path = dyld3/SupportedArchs.h; sourceTree = "<group>"; };
+               C1BDD441234EA7DD0095C7DC /* AppCacheBuilder.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = AppCacheBuilder.cpp; path = "dyld3/shared-cache/AppCacheBuilder.cpp"; sourceTree = "<group>"; };
+               C1BDD442234EA7DD0095C7DC /* AppCacheBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AppCacheBuilder.h; path = "dyld3/shared-cache/AppCacheBuilder.h"; sourceTree = "<group>"; };
+               C1BDD444234EAF500095C7DC /* MachOAppCache.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = MachOAppCache.cpp; path = dyld3/MachOAppCache.cpp; sourceTree = "<group>"; };
+               C1BDD445234EAF500095C7DC /* MachOAppCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MachOAppCache.h; path = dyld3/MachOAppCache.h; sourceTree = "<group>"; };
+               C1BF4DA92357B14700B0F1AE /* kernel_collection_builder.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = kernel_collection_builder.cpp; path = "dyld3/shared-cache/kernel_collection_builder.cpp"; sourceTree = "<group>"; };
+               C1BF4DAA2357B14700B0F1AE /* kernel_collection_builder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = kernel_collection_builder.h; path = "dyld3/shared-cache/kernel_collection_builder.h"; sourceTree = "<group>"; };
+               C1BF4DB02359254500B0F1AE /* libKernelCollectionBuilder.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libKernelCollectionBuilder.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
                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>"; };
                DE728E4E210CD6A100EB5409 /* conf.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = conf.py; sourceTree = "<group>"; };
                DE728E51210CD6A100EB5409 /* dyld_usage.rst */ = {isa = PBXFileReference; lastKnownFileType = text; path = dyld_usage.rst; sourceTree = "<group>"; };
                DE728E52210CD6B700EB5409 /* dyld_usage.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = dyld_usage.1; sourceTree = "<group>"; };
+               E90E790923D5F142005F5995 /* IMPCachesBuilder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = IMPCachesBuilder.hpp; path = "dyld3/shared-cache/IMPCachesBuilder.hpp"; sourceTree = "<group>"; };
+               E9C2FAD623AA8B5C0077E966 /* IMPCaches.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = IMPCaches.hpp; path = "dyld3/shared-cache/IMPCaches.hpp"; sourceTree = "<group>"; };
+               E9C2FAD723AA8B5C0077E966 /* IMPCaches.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = IMPCaches.cpp; path = "dyld3/shared-cache/IMPCaches.cpp"; sourceTree = "<group>"; };
+               E9EC319F240B19C1001705D6 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
                EF799FE9070D27BB00F78484 /* dyld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = dyld.1; path = doc/man/man1/dyld.1; sourceTree = SOURCE_ROOT; };
                EF799FEB070D27BB00F78484 /* dladdr.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dladdr.3; path = doc/man/man3/dladdr.3; sourceTree = SOURCE_ROOT; };
                EF799FEC070D27BB00F78484 /* dlclose.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlclose.3; path = doc/man/man3/dlclose.3; sourceTree = SOURCE_ROOT; };
                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>"; };
                F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldAPIsInLibSystem.cpp; path = src/dyldAPIsInLibSystem.cpp; sourceTree = "<group>"; };
+               F91491A823EE52D300782334 /* configure-dyld-archives.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "configure-dyld-archives.sh"; 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>"; };
                F91BFAC721684FCC007F10AB /* fixup-chains.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "fixup-chains.h"; path = "include/mach-o/fixup-chains.h"; sourceTree = "<group>"; };
                F92756871F7098FB000820EE /* Array.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Array.h; path = dyld3/Array.h; sourceTree = "<group>"; };
                F9280B7A1AB9DCA000B18AEC /* ImageLoaderMegaDylib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMegaDylib.h; path = src/ImageLoaderMegaDylib.h; sourceTree = "<group>"; usesTabs = 1; };
                F92C7E1421E59840000D12B5 /* libdyld.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libdyld.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
                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>"; };
+               F939373E0A94FC4700070A07 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = Architectures.hpp; path = "dyld3/shared-cache/Architectures.hpp"; sourceTree = "<group>"; };
                F93F46511FA420630060D9F9 /* execserver.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = execserver.defs; path = testing/lib/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>"; };
                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; };
-               F9556D3920C1F896004DF62A /* dyldinfo */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyldinfo; sourceTree = BUILT_PRODUCTS_DIR; };
+               F9556D3920C1F896004DF62A /* dyld_info */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_info; sourceTree = BUILT_PRODUCTS_DIR; };
                F9556D4120C20C79004DF62A /* dyldinfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyldinfo.cpp; path = "dyld3/shared-cache/dyldinfo.cpp"; sourceTree = "<group>"; };
                F958D4751C7FCD4A00A0B199 /* dyld_process_info_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyld_process_info_internal.h; path = src/dyld_process_info_internal.h; sourceTree = "<group>"; };
                F958D4761C7FCD4A00A0B199 /* dyld_process_info_notify.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_process_info_notify.cpp; path = src/dyld_process_info_notify.cpp; sourceTree = "<group>"; usesTabs = 0; };
-               F95C95160E994796007B7CB8 /* MachOTrie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachOTrie.hpp; sourceTree = "<group>"; };
                F963542E1DCD736000895049 /* update_dyld_sim_shared_cache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = update_dyld_sim_shared_cache.cpp; path = "dyld3/shared-cache/update_dyld_sim_shared_cache.cpp"; sourceTree = "<group>"; usesTabs = 0; };
                F96354451DCD74A400895049 /* update_dyld_sim_shared_cache */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = update_dyld_sim_shared_cache; sourceTree = BUILT_PRODUCTS_DIR; };
-               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; };
                F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = dyld.xcconfig; sourceTree = "<group>"; };
                F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = libdyld.xcconfig; sourceTree = "<group>"; };
                F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = update_dyld_shared_cache.xcconfig; sourceTree = "<group>"; };
+               F971EC732342D2CF000BCEAA /* MachOAnalyzerSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MachOAnalyzerSet.h; path = dyld3/MachOAnalyzerSet.h; sourceTree = "<group>"; };
+               F971EC742342D2CF000BCEAA /* MachOAnalyzerSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MachOAnalyzerSet.cpp; path = dyld3/MachOAnalyzerSet.cpp; sourceTree = "<group>"; };
                F976F548127B90F8004BA2A5 /* dyld.order */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dyld.order; path = src/dyld.order; sourceTree = "<group>"; };
                F977DDC91E53BEA700609230 /* SharedCacheRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SharedCacheRuntime.cpp; path = dyld3/SharedCacheRuntime.cpp; sourceTree = "<group>"; };
                F977DDCA1E53BEA700609230 /* SharedCacheRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SharedCacheRuntime.h; path = dyld3/SharedCacheRuntime.h; sourceTree = "<group>"; };
                F97C61A01D9CA6B800A84CD7 /* Logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Logging.cpp; path = dyld3/Logging.cpp; sourceTree = "<group>"; usesTabs = 0; };
                F97C61A11D9CA6B800A84CD7 /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = dyld3/Logging.h; sourceTree = "<group>"; usesTabs = 0; };
                F97C61A71DBAD1A900A84CD7 /* dyld_closure_util */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_closure_util; sourceTree = BUILT_PRODUCTS_DIR; };
+               F97D394E23A08B5500BD3B5A /* dyld.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; name = dyld.modulemap; path = "include/mach-o/dyld.modulemap"; sourceTree = "<group>"; };
                F97FF35F1C236402000ACDD2 /* nocr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = nocr.cpp; path = testing/nocr/nocr.cpp; 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>"; };
                F98692221DC4028B00CBEDE6 /* CodeSigningTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CodeSigningTypes.h; path = dyld3/CodeSigningTypes.h; sourceTree = "<group>"; usesTabs = 0; };
                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>"; };
                F98E37952332D048003706B4 /* update_dyld_shared_cache_root_mode */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = update_dyld_shared_cache_root_mode; sourceTree = BUILT_PRODUCTS_DIR; };
-               F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dyld_shared_cache_util.cpp; sourceTree = "<group>"; };
+               F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_shared_cache_util.cpp; path = "dyld3/shared-cache/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>"; };
                F9C275581DA71A13007A5D8A /* Loading.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Loading.cpp; path = dyld3/Loading.cpp; sourceTree = "<group>"; usesTabs = 0; };
                F9C275591DA71A13007A5D8A /* Loading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Loading.h; path = dyld3/Loading.h; sourceTree = "<group>"; usesTabs = 0; };
                F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-shared-cache.h"; path = "include/objc-shared-cache.h"; sourceTree = "<group>"; usesTabs = 0; };
-               F9CE30781208F1B50098B590 /* dsc_extractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsc_extractor.cpp; sourceTree = "<group>"; };
-               F9CE30791208F1B50098B590 /* dsc_extractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsc_extractor.h; sourceTree = "<group>"; };
+               F9CE30781208F1B50098B590 /* dsc_extractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dsc_extractor.cpp; path = "dyld3/shared-cache/dsc_extractor.cpp"; sourceTree = "<group>"; };
+               F9CE30791208F1B50098B590 /* dsc_extractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dsc_extractor.h; path = "dyld3/shared-cache/dsc_extractor.h"; sourceTree = "<group>"; };
                F9CF4C8121E59D060013ACDF /* libdyld_driverkit.exp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.exports; name = libdyld_driverkit.exp; path = src/libdyld_driverkit.exp; sourceTree = "<group>"; };
                F9D1001214D8D0BA00099D91 /* dsc_extractor.bundle */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = dsc_extractor.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
                F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; path = update_dyld_shared_cache.1; sourceTree = "<group>"; };
                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>"; };
                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>"; };
+               F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dsc_iterator.cpp; path = "dyld3/shared-cache/dsc_iterator.cpp"; sourceTree = "<group>"; };
+               F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dsc_iterator.h; path = "dyld3/shared-cache/dsc_iterator.h"; sourceTree = "<group>"; };
                F9F6F4261C1FAF8000BD8FED /* testing */ = {isa = PBXFileReference; lastKnownFileType = folder; path = testing; sourceTree = "<group>"; };
                F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PathOverrides.cpp; path = dyld3/PathOverrides.cpp; sourceTree = "<group>"; };
                F9F76FAF1E08CFF200828678 /* PathOverrides.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PathOverrides.h; path = dyld3/PathOverrides.h; sourceTree = "<group>"; };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               C14965DD22BDCF6800568D15 /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               C1BFD0502307CE99007D7CDC /* CoreFoundation.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               C14C355D230531820059E04C /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               C18F05372374D5B700DC6CCA /* libtest_support.a in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                C187B9031FE063A40042D3B7 /* Frameworks */ = {
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               E9EC31A0240B19C1001705D6 /* Foundation.framework in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               C1BF4DAE2359254500B0F1AE /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               C1BF4DBA2359389E00B0F1AE /* CoreFoundation.framework in Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               F94C22251E513CA90079E5DD /* CoreFoundation.framework in Frameworks */,
-                               F92015701DDFEBAF00816A4A /* Bom.framework in Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               F98E378C2332D048003706B4 /* CoreFoundation.framework in Frameworks */,
-                               F98E378D2332D048003706B4 /* Bom.framework in Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        path = doc/tracing;
                        sourceTree = SOURCE_ROOT;
                };
+               37D7995523AAA86800B314BC /* build scripts */ = {
+                       isa = PBXGroup;
+                       children = (
+                               F91491A823EE52D300782334 /* configure-dyld-archives.sh */,
+                               37D7995823AAA8AD00B314BC /* ContainerizedTestRunner-build-everything.sh */,
+                               37D7995723AAA8AD00B314BC /* dyld_tests-build.sh */,
+                               37D47F8823EC961500C5C000 /* libdyld-generate-version-headers.sh */,
+                               37D7995923AAA8AD00B314BC /* dyld_tests-install.sh */,
+                               37D7995623AAA8AD00B314BC /* include.sh */,
+                               C116F1A523F5BB39002D386B /* update_dyld_shared_cache-build.sh */,
+                               37B5254F2475E91F00404300 /* generate-cache-config-header.sh */,
+                       );
+                       name = "build scripts";
+                       path = "build-scripts";
+                       sourceTree = "<group>";
+               };
                DE728E4B210CD6A100EB5409 /* rst */ = {
                        isa = PBXGroup;
                        children = (
                F91BFAC52166CED7007F10AB /* mach-o */ = {
                        isa = PBXGroup;
                        children = (
+                               F97D394E23A08B5500BD3B5A /* dyld.modulemap */,
                                F95090D01C5AB89A0031F81D /* dyld_process_info.h */,
                                F98D274C0AA79D7400416316 /* dyld_images.h */,
                                F918691408B16D2500E0F9DB /* dyld-interposing.h */,
                        name = "mach-o";
                        sourceTree = "<group>";
                };
-               F939373D0A94FC4700070A07 /* launch-cache */ = {
-                       isa = PBXGroup;
-                       children = (
-                               F939373E0A94FC4700070A07 /* Architectures.hpp */,
-                               F939373F0A94FC4700070A07 /* CacheFileAbstraction.hpp */,
-                               F93937400A94FC4700070A07 /* dyld_cache_format.h */,
-                               F93937410A94FC4700070A07 /* FileAbstraction.hpp */,
-                               F93937430A94FC4700070A07 /* MachOFileAbstraction.hpp */,
-                               F95C95160E994796007B7CB8 /* MachOTrie.hpp */,
-                               F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */,
-                               F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */,
-                               F9CE30781208F1B50098B590 /* dsc_extractor.cpp */,
-                               F9CE30791208F1B50098B590 /* dsc_extractor.h */,
-                               F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */,
-                       );
-                       path = "launch-cache";
-                       sourceTree = "<group>";
-               };
                F94C22231E513CA90079E5DD /* Frameworks */ = {
                        isa = PBXGroup;
                        children = (
+                               E9EC319F240B19C1001705D6 /* Foundation.framework */,
                                37F597D62061ED3200F9B6F9 /* libktrace.tbd */,
                                37F7A5961BB363820039043A /* Bom.framework */,
                                376ED1D71C46F2710051DD54 /* Metabom.framework */,
                                F9DFEA7C1F588506003BF8A7 /* ClosurePrinter.cpp */,
                                F9DFEA711F54BD83003BF8A7 /* ClosureWriter.h */,
                                F9DFEA731F54DB25003BF8A7 /* ClosureWriter.cpp */,
+                               37D5ACEC23A830C200AE4F57 /* Defines.h */,
                                F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */,
                                F98692001DC3EF4800CBEDE6 /* Diagnostics.h */,
                                C18A75F7209A19E200DC01BB /* JSON.h */,
                                F9A5E6161F5C967C0030C490 /* MachOLoaded.h */,
                                F9A5E6181F5F1BFA0030C490 /* MachOAnalyzer.cpp */,
                                F9A5E61B1F5F1BFB0030C490 /* MachOAnalyzer.h */,
+                               F971EC742342D2CF000BCEAA /* MachOAnalyzerSet.cpp */,
+                               F971EC732342D2CF000BCEAA /* MachOAnalyzerSet.h */,
+                               C1BDD444234EAF500095C7DC /* MachOAppCache.cpp */,
+                               C1BDD445234EAF500095C7DC /* MachOAppCache.h */,
                                C18F095221925E7600034B68 /* Map.h */,
                                F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */,
                                F9F76FAF1E08CFF200828678 /* PathOverrides.h */,
+                               C116F19A23F4B11B002D386B /* RootsChecker.cpp */,
+                               C116F19B23F4B11B002D386B /* RootsChecker.h */,
                                F977DDC91E53BEA700609230 /* SharedCacheRuntime.cpp */,
                                F977DDCA1E53BEA700609230 /* SharedCacheRuntime.h */,
                                C19D50142087E4BC00563DAF /* SupportedArchs.h */,
                F98692161DC3EF7700CBEDE6 /* shared-cache */ = {
                        isa = PBXGroup;
                        children = (
-                               37908A2A1E3A85A4009613FA /* FileAbstraction.hpp */,
-                               37908A2B1E3A85A4009613FA /* MachOFileAbstraction.hpp */,
-                               F986921E1DC3F86C00CBEDE6 /* dyld_cache_format.h */,
                                F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */,
-                               F986921D1DC3F86C00CBEDE6 /* CacheBuilder.h */,
+                               C1BDD441234EA7DD0095C7DC /* AppCacheBuilder.cpp */,
+                               C1BDD442234EA7DD0095C7DC /* AppCacheBuilder.h */,
+                               F939373E0A94FC4700070A07 /* Architectures.hpp */,
                                F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */,
-                               F986920C1DC3EF6C00CBEDE6 /* DyldSharedCache.h */,
+                               F986921D1DC3F86C00CBEDE6 /* CacheBuilder.h */,
+                               F9CE30781208F1B50098B590 /* dsc_extractor.cpp */,
+                               F9CE30791208F1B50098B590 /* dsc_extractor.h */,
+                               F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */,
+                               F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */,
+                               C14965DB22BDCE7C00568D15 /* dyld_app_cache_util.cpp */,
+                               F986921E1DC3F86C00CBEDE6 /* dyld_cache_format.h */,
+                               F9D862441DC9759C000A199A /* dyld_closure_util.cpp */,
+                               37908A271E3A853E009613FA /* dyld_shared_cache_builder.mm */,
+                               F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */,
+                               F9556D4120C20C79004DF62A /* dyldinfo.cpp */,
                                F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */,
-                               F986920E1DC3EF6C00CBEDE6 /* FileUtils.h */,
+                               F986920C1DC3EF6C00CBEDE6 /* DyldSharedCache.h */,
+                               37908A2A1E3A85A4009613FA /* FileAbstraction.hpp */,
                                F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */,
+                               F986920E1DC3EF6C00CBEDE6 /* FileUtils.h */,
+                               C1BF4DA92357B14700B0F1AE /* kernel_collection_builder.cpp */,
+                               C1BF4DAA2357B14700B0F1AE /* kernel_collection_builder.h */,
+                               37908A2B1E3A85A4009613FA /* MachOFileAbstraction.hpp */,
+                               C1D2682E1FE08918009F115B /* mrm_shared_cache_builder.cpp */,
+                               C1D2682F1FE08918009F115B /* mrm_shared_cache_builder.h */,
                                F986920F1DC3EF6C00CBEDE6 /* ObjC1Abstraction.hpp */,
                                F98692101DC3EF6C00CBEDE6 /* ObjC2Abstraction.hpp */,
                                F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */,
                                F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */,
                                F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */,
-                               C11ECA8F233C307C0011726F /* SharedCacheBuilder.h */,
+                               E9C2FAD623AA8B5C0077E966 /* IMPCaches.hpp */,
+                               E90E790923D5F142005F5995 /* IMPCachesBuilder.hpp */,
+                               E9C2FAD723AA8B5C0077E966 /* IMPCaches.cpp */,
                                C11ECA8E233C307C0011726F /* SharedCacheBuilder.cpp */,
+                               C11ECA8F233C307C0011726F /* SharedCacheBuilder.h */,
                                F902031F1DEE83C000AC3F76 /* StringUtils.h */,
                                37908A2D1E3A85A4009613FA /* Trie.hpp */,
-                               F9D862441DC9759C000A199A /* dyld_closure_util.cpp */,
-                               37908A271E3A853E009613FA /* dyld_shared_cache_builder.mm */,
-                               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 */,
-                               F9556D4120C20C79004DF62A /* dyldinfo.cpp */,
+                               F98692151DC3EF6C00CBEDE6 /* update_dyld_shared_cache.cpp */,
+                               F963542E1DCD736000895049 /* update_dyld_sim_shared_cache.cpp */,
                        );
                        name = "shared-cache";
                        sourceTree = "<group>";
                        children = (
                                376AA37C23305CE10070C28C /* ContainerizedTestRunner.xctestplan */,
                                F9F6F4261C1FAF8000BD8FED /* testing */,
+                               37D7995523AAA86800B314BC /* build scripts */,
                                F971DD121A4A0E0700BBDD52 /* configs */,
                                F9ED4CBB0630A7AA00DF4E74 /* src */,
                                F9ED4CC30630A7BE00DF4E74 /* doc */,
                                3715A2FF232320BC0059433D /* local_test_runner */,
                                F9ED4C990630A76000DF4E74 /* Products */,
                                F96D19A41D9363B7007AF3CE /* dyld3 */,
-                               F939373D0A94FC4700070A07 /* launch-cache */,
                                F94C22231E513CA90079E5DD /* Frameworks */,
                        );
                        indentWidth = 4;
                                C187B90A1FE063A40042D3B7 /* slc_builder.dylib */,
                                37F597CD2061EB4200F9B6F9 /* dyld_usage */,
                                F92C7E1421E59840000D12B5 /* libdyld.dylib */,
-                               F9556D3920C1F896004DF62A /* dyldinfo */,
+                               F9556D3920C1F896004DF62A /* dyld_info */,
                                F98E37952332D048003706B4 /* update_dyld_shared_cache_root_mode */,
                                3721A635230CABAF00594066 /* libtest_support.a */,
                                37065AA72310856E00A20034 /* nocr */,
                                3721B6A82321A75B006F6AB7 /* chroot_util */,
                                3715A2FE232320BC0059433D /* ContainerizedTestRunner.xctest */,
+                               C14965E022BDCF6800568D15 /* dyld_app_cache_util */,
+                               C14C3560230531820059E04C /* run-static */,
+                               C1BF4DB02359254500B0F1AE /* libKernelCollectionBuilder.dylib */,
                        );
                        name = Products;
                        sourceTree = "<group>";
                                F958D4751C7FCD4A00A0B199 /* dyld_process_info_internal.h */,
                                F958D4761C7FCD4A00A0B199 /* dyld_process_info_notify.cpp */,
                                F95090E41C5AD1B30031F81D /* dyld_process_info.cpp */,
+                               C14C3562230531830059E04C /* testing/run-static/run-static.cpp */,
                        );
                        name = src;
                        sourceTree = "<group>";
                F9ED4CBE0630A7B100DF4E74 /* include */ = {
                        isa = PBXGroup;
                        children = (
-                               F96D19711D7F63EE007AF3CE /* expand.rb */,
                                F91BFAC52166CED7007F10AB /* mach-o */,
                                F99EE6AE06B48D4200BF1992 /* dlfcn.h */,
+                               A5E247802369485F00BDED2F /* dlfcn_private.h */,
                                F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */,
                        );
                        name = include;
                                F91BFAC821684FCC007F10AB /* fixup-chains.h in Headers */,
                                F99006DD1E411BA70013456D /* dyld_images.h in Headers */,
                                F99006DE1E411BBC0013456D /* dyld.h in Headers */,
+                               C116F19F23F4B11B002D386B /* RootsChecker.h in Headers */,
                                F9DFEA761F54FAAB003BF8A7 /* ClosureBuilder.h in Headers */,
                                F960A78A1E40569400840176 /* dyld-interposing.h in Headers */,
                                F9DFEA721F54BD83003BF8A7 /* ClosureWriter.h in Headers */,
                        productReference = 37F597CD2061EB4200F9B6F9 /* dyld_usage */;
                        productType = "com.apple.product-type.tool";
                };
+               C14965DF22BDCF6800568D15 /* dyld_app_cache_util */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = C14965E422BDCF6900568D15 /* Build configuration list for PBXNativeTarget "dyld_app_cache_util" */;
+                       buildPhases = (
+                               C14965DC22BDCF6800568D15 /* Sources */,
+                               C14965DD22BDCF6800568D15 /* Frameworks */,
+                               C14965DE22BDCF6800568D15 /* CopyFiles */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = dyld_app_cache_util;
+                       productName = dyld_app_cache_util;
+                       productReference = C14965E022BDCF6800568D15 /* dyld_app_cache_util */;
+                       productType = "com.apple.product-type.tool";
+               };
+               C14C355F230531820059E04C /* run-static */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = C14C3564230531830059E04C /* Build configuration list for PBXNativeTarget "run-static" */;
+                       buildPhases = (
+                               C14C355C230531820059E04C /* Sources */,
+                               C14C355D230531820059E04C /* Frameworks */,
+                               C14C355E230531820059E04C /* CopyFiles */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                               C18F05362374D5B100DC6CCA /* PBXTargetDependency */,
+                       );
+                       name = "run-static";
+                       productName = "run-static";
+                       productReference = C14C3560230531820059E04C /* run-static */;
+                       productType = "com.apple.product-type.tool";
+               };
                C187B8FF1FE063A40042D3B7 /* libslc_builder.dylib */ = {
                        isa = PBXNativeTarget;
                        buildConfigurationList = C187B9071FE063A40042D3B7 /* Build configuration list for PBXNativeTarget "libslc_builder.dylib" */;
                        productReference = C187B90A1FE063A40042D3B7 /* slc_builder.dylib */;
                        productType = "com.apple.product-type.library.dynamic";
                };
+               C1BF4DAF2359254500B0F1AE /* libKernelCollectionBuilder */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = C1BF4DB12359254500B0F1AE /* Build configuration list for PBXNativeTarget "libKernelCollectionBuilder" */;
+                       buildPhases = (
+                               C1BF4DAD2359254500B0F1AE /* Sources */,
+                               C1BF4DAE2359254500B0F1AE /* Frameworks */,
+                               C1BF4DC523594FA200B0F1AE /* usr|local|include */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = libKernelCollectionBuilder;
+                       productName = libKernelCollectionBuilder;
+                       productReference = C1BF4DB02359254500B0F1AE /* libKernelCollectionBuilder.dylib */;
+                       productType = "com.apple.product-type.library.dynamic";
+               };
                F92C7DE521E59840000D12B5 /* libdyld_driverkit */ = {
                        isa = PBXNativeTarget;
                        buildConfigurationList = F92C7E1121E59840000D12B5 /* Build configuration list for PBXNativeTarget "libdyld_driverkit" */;
                        buildPhases = (
                                F92C7DE621E59840000D12B5 /* Sources */,
                                F92C7E0221E59840000D12B5 /* Headers */,
+                               37D47F8923EDFA2400C5C000 /* CopyFiles */,
                                F951DA862228E5560057BA43 /* install headers */,
                        );
                        buildRules = (
                        isa = PBXNativeTarget;
                        buildConfigurationList = F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache_tool" */;
                        buildPhases = (
-                               F91083C91702592700831889 /* create dyld_cache_config.h */,
                                F939372F0A94FAF700070A07 /* Sources */,
                                F93937300A94FAF700070A07 /* Frameworks */,
-                               F9D238DD0A9E2FEE002B55C7 /* usr|share|man|man1 */,
                                F991E3030FF1A4EC0082CCC9 /* do not install duplicates */,
-                               F981C8C21F058F8200452F35 /* mkdir /var/db/dyld */,
                        );
                        buildRules = (
                        );
                        productReference = F93937320A94FAF700070A07 /* update_dyld_shared_cache */;
                        productType = "com.apple.product-type.tool";
                };
-               F9556D3820C1F896004DF62A /* dyldinfo */ = {
+               F9556D3820C1F896004DF62A /* dyld_info */ = {
                        isa = PBXNativeTarget;
-                       buildConfigurationList = F9556D3F20C1F896004DF62A /* Build configuration list for PBXNativeTarget "dyldinfo" */;
+                       buildConfigurationList = F9556D3F20C1F896004DF62A /* Build configuration list for PBXNativeTarget "dyld_info" */;
                        buildPhases = (
                                F9556D3520C1F896004DF62A /* Sources */,
                                F9556D3620C1F896004DF62A /* Frameworks */,
                        );
                        dependencies = (
                        );
-                       name = dyldinfo;
+                       name = dyld_info;
                        productName = dyldinfo;
-                       productReference = F9556D3920C1F896004DF62A /* dyldinfo */;
+                       productReference = F9556D3920C1F896004DF62A /* dyld_info */;
                        productType = "com.apple.product-type.tool";
                };
                F963542F1DCD74A400895049 /* update_dyld_sim_shared_cache */ = {
                        isa = PBXNativeTarget;
                        buildConfigurationList = F98E37922332D048003706B4 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache_root_mode_tool" */;
                        buildPhases = (
-                               F98E37772332D048003706B4 /* create dyld_cache_config.h */,
                                F98E37782332D048003706B4 /* Sources */,
                                F98E378B2332D048003706B4 /* Frameworks */,
-                               F98E378E2332D048003706B4 /* usr|share|man|man1 */,
-                               F98E37902332D048003706B4 /* do not install duplicates */,
-                               F98E37912332D048003706B4 /* mkdir /var/db/dyld */,
                        );
                        buildRules = (
                        );
                                F9D050C811DD701A00FB0A29 /* configure archives */,
                                F9ED4C950630A76000DF4E74 /* Sources */,
                                F907E2490FA6469000BFEDBD /* install iPhone file */,
-                               F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */,
-                               371C117D208ADFC700FD9036 /* Suppress simulator dyld_usage */,
                        );
                        buildRules = (
                                F921D318070376B0000D1056 /* PBXBuildRule */,
                                F921D3160703769A000D1056 /* PBXBuildRule */,
                        );
                        dependencies = (
-                               C1033EA822611306004407FB /* PBXTargetDependency */,
-                               F99B8EB20FEC220C00701838 /* PBXTargetDependency */,
-                               F96543A11E343601003C5540 /* PBXTargetDependency */,
                        );
                        name = dyld;
                        productName = dyld;
                        buildConfigurationList = F9D8C7E1087B087300E93EFB /* Build configuration list for PBXNativeTarget "libdyld.dylib" */;
                        buildPhases = (
                                F9ED4C9C0630A76B00DF4E74 /* Sources */,
-                               F959621018849DF20003E4D4 /* add dyld symlink */,
                                F98F1FBB1E4029CA00EF868D /* Headers */,
-                               F960A78C1E405E2300840176 /* expand dyld_priv.h macros */,
+                               F960A78C1E405E2300840176 /* generate version headers */,
                                F99006DF1E411C500013456D /* install dlfcn.h */,
                                37918AC32058912100F39A77 /* install ktrace codes file */,
                        );
                                                CreatedOnToolsVersion = 10.0;
                                                ProvisioningStyle = Automatic;
                                        };
+                                       C14965DF22BDCF6800568D15 = {
+                                               CreatedOnToolsVersion = 11.0;
+                                               ProvisioningStyle = Automatic;
+                                       };
+                                       C14C355F230531820059E04C = {
+                                               CreatedOnToolsVersion = 10.3;
+                                               ProvisioningStyle = Automatic;
+                                       };
+                                       C1BDD43C234E8FA00095C7DC = {
+                                               CreatedOnToolsVersion = 11.2;
+                                               ProvisioningStyle = Automatic;
+                                       };
+                                       C1BF4DAF2359254500B0F1AE = {
+                                               CreatedOnToolsVersion = 11.2;
+                                               ProvisioningStyle = Automatic;
+                                       };
                                        F9556D3820C1F896004DF62A = {
                                                CreatedOnToolsVersion = 10.0;
                                        };
                                F9ED4C920630A73900DF4E74 /* all */,
                                37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */,
                                F908134211D3ED0B00626CC1 /* libdyld */,
+                               C1BDD43C234E8FA00095C7DC /* dyld_executables */,
                                F9ED4C970630A76000DF4E74 /* dyld */,
                                F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */,
                                F92C7DE521E59840000D12B5 /* libdyld_driverkit */,
                                F98E37762332D048003706B4 /* update_dyld_shared_cache_root_mode_tool */,
                                3703A1111B38C1B300ADBA7F /* dyld_shared_cache_builder */,
                                F963542F1DCD74A400895049 /* update_dyld_sim_shared_cache */,
+                               C14965DF22BDCF6800568D15 /* dyld_app_cache_util */,
                                F99B8E550FEC10F600701838 /* dyld_shared_cache_util */,
                                F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */,
                                F9F2A5580F7AEE9800B7C9EB /* libdsc */,
                                F9F6F4271C1FB0A700BD8FED /* dyld_tests */,
                                F97FF3551C23638F000ACDD2 /* nocr */,
                                37F597CC2061EB4200F9B6F9 /* dyld_usage */,
-                               F9556D3820C1F896004DF62A /* dyldinfo */,
+                               F9556D3820C1F896004DF62A /* dyld_info */,
                                3721A634230CABAF00594066 /* test_support */,
                                3721B6A72321A75B006F6AB7 /* chroot_util */,
                                3715A2FD232320BC0059433D /* ContainerizedTestRunner */,
+                               C14C355F230531820059E04C /* run-static */,
+                               C1BF4DAF2359254500B0F1AE /* libKernelCollectionBuilder */,
                        );
                };
 /* 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[ \\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";
+                       shellScript = "$SRCROOT/build-scripts/generate-cache-config-header.sh\n";
                        showEnvVarsInLog = 0;
                };
                3715A3092327003C0059433D /* Build Everything */ = {
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        shellPath = /bin/sh;
-                       shellScript = "env -i PATH=\"${PATH}\" xcodebuild install -sdk ${SDKROOT} -configuration ${CONFIGURATION} -target dyld -target libdyld -target dyld_tests TOOLCHAINS=\"${TOOLCHAINS}\" DSTROOT=${DERIVED_FILES_DIR}/TestRoot OBJROOT=${DERIVED_FILES_DIR}/objroot XCTestGenPath=${DERIVED_FILES_DIR}/XCTestGenerated.h\n${BUILT_PRODUCTS_DIR}/chroot_util -chroot ${DERIVED_FILES_DIR}/TestRoot -fallback / -add_file /bin/echo -add_file /bin/sh -add_file /bin/bash -add_file /bin/ls -add_file /usr/sbin/dtrace -add_file /sbin/mount -add_file /sbin/mount_devfs -add_file /usr/lib/libobjc-trampolines.dylib -add_file /usr/bin/leaks\n/bin/mkdir -p ${DERIVED_FILES_DIR}/TestRoot/dev\n/bin/mkdir -m 777 -p ${DERIVED_FILES_DIR}/TestRoot/tmp\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 simulator\n# The target builds a dummy app that we delete\nif [ \"${PRODUCT_NAME}\" != \"dyld_sim\" ]\nthen\nOBJROOT_USAGE=\"${TARGET_TEMP_DIR}/Objects_Usage\"\nxcodebuild install -target dyld_usage SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT_USAGE}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\nfi\n";
+                       shellScript = "$SRCROOT/build-scripts/ContainerizedTestRunner-build-everything.sh\n";
                        showEnvVarsInLog = 0;
                };
                37E2FC9D22F62FE1004AF213 /* install */ = {
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
-                       shellScript = "#LLBUILD=$(xcrun --sdk $SDKROOT --find llbuild 2> /dev/null)\nNINJA=${LLBUILD:-`xcrun  --sdk $SDKROOT --find ninja  2> /dev/null`}\nINSTALL_TARGET=\"install\"\n\nif [ ! -z \"$LLBUILD\" ]; then\n  NINJA=\"$LLBUILD ninja build\"\nfi\n\nif [ ! -z \"$ONLY_BUILD_TEST\" ]; then\n    INSTALL_TARGET=\"install-$BUILD_ONLY\"\nfi\n\n${NINJA} -C ${DERIVED_FILES_DIR} ${INSTALL_TARGET}\n";
+                       shellScript = "$SRCROOT/build-scripts/dyld_tests-install.sh\n";
                        showEnvVarsInLog = 0;
                };
                C1225E3E21FA84BF0079CF9C /* 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\nARM_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\necho -n \"#define ARM_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nawk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\necho \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\necho -n \"#define ARM_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nawk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\necho \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\necho -n \"#define ARM64_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nawk '/define SHARED_REGION_BASE_ARM64[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\necho \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\necho -n \"#define ARM64_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nawk '/define SHARED_REGION_SIZE_ARM64[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\necho \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\ngrep SHARED_REGION_BASE_ARM64_32 \"${SHARED_REGION_FILE}\" > /dev/null 2>&1\nif [ \"$?\" -eq \"0\" ]; then\necho -n \"#define ARM64_32_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nawk '/define SHARED_REGION_BASE_ARM64_32/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\necho \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\necho -n \"#define ARM64_32_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nawk '/define SHARED_REGION_SIZE_ARM64_32/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\necho \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nfi\nelse\necho \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\nexit 1\nfi\n\nif [ -r \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" ]; then\nmkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\ncp \"${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\nmkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\ncp \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\"  \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\n\n";
+                       shellScript = "$SRCROOT/build-scripts/generate-cache-config-header.sh\n";
                        showEnvVarsInLog = 0;
                };
                F907E2490FA6469000BFEDBD /* install iPhone file */ = {
                        shellScript = "if [ \"${RC_PURPLE}\" = \"YES\" ]\nthen\n    mkdir -p ${DSTROOT}//System/Library/Caches/com.apple.dyld\n    echo \"existence of this file enables dyld to have dylibs override shared cache\" > ${DSTROOT}//System/Library/Caches/com.apple.dyld/enable-dylibs-to-override-cache\nfi\n";
                        showEnvVarsInLog = 0;
                };
-               F91083C91702592700831889 /* create dyld_cache_config.h */ = {
-                       isa = PBXShellScriptBuildPhase;
-                       buildActionMask = 2147483647;
-                       files = (
-                       );
-                       inputPaths = (
-                       );
-                       name = "create dyld_cache_config.h";
-                       outputPaths = (
-                               "$(DERIVED_FILE_DIR)/dyld_cache_config.h",
-                       );
-                       runOnlyForDeploymentPostprocessing = 0;
-                       shellPath = /bin/bash;
-                       shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n    # if iOS SDK not available, use MacOSX SDK\n    ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n    echo -n \"#define ARM_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n    echo -n \"#define ARM_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n    echo -n \"#define ARM64_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_BASE_ARM64[ \\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;
-               };
                F94182D61E60E74E00D8EF25 /* pre-platform builds */ = {
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 8;
                        outputPaths = (
                        );
                        runOnlyForDeploymentPostprocessing = 1;
-                       shellPath = /bin/sh;
-                       shellScript = "if [ \"${RC_PURPLE}\" = \"YES\" ]\nthen\n   OBJROOT_LOCAL=\"${TARGET_TEMP_DIR}/Objects_Local\"\n   xcodebuild install -target dyld_shared_cache_builder SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT_LOCAL}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\n    if [ \"${RC_BRIDGE}\" != \"YES\" ]\n    then\n        OBJROOT_SIM=\"${TARGET_TEMP_DIR}/Objects_Sim\"\n        xcodebuild install -target update_dyld_sim_shared_cache SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT_SIM}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\n    fi\nelse\n    OBJROOT_MAC=\"${TARGET_TEMP_DIR}/Objects_Mac\"\n    xcodebuild install -target update_dyld_shared_cache_tool SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT_MAC}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\n    OBJROOT_MAC=\"${TARGET_TEMP_DIR}/Objects2_Mac\"\n    xcodebuild install -target update_dyld_shared_cache_root_mode_tool SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT_MAC}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\nfi\n";
-                       showEnvVarsInLog = 0;
-               };
-               F951DA862228E5560057BA43 /* install headers */ = {
-                       isa = PBXShellScriptBuildPhase;
-                       buildActionMask = 8;
-                       files = (
-                       );
-                       inputFileListPaths = (
-                       );
-                       inputPaths = (
-                       );
-                       name = "install headers";
-                       outputFileListPaths = (
-                       );
-                       outputPaths = (
-                       );
-                       runOnlyForDeploymentPostprocessing = 1;
-                       shellPath = /bin/sh;
-                       shellScript = "# dyld.h and dyld_priv.h are not for use by actual drivers, so they are both in the Runtime directory\nmkdir -p ${DSTROOT}/${PUBLIC_HEADERS_FOLDER_PATH}\n${SRCROOT}/bin/expand.rb < ${SRCROOT}/include/mach-o/dyld_priv.h >  ${DSTROOT}/${PUBLIC_HEADERS_FOLDER_PATH}/dyld_priv.h\ncp ${SRCROOT}/include/mach-o/dyld.h ${DSTROOT}/${PUBLIC_HEADERS_FOLDER_PATH}/dyld.h\ncp ${SRCROOT}/include/dlfcn.h ${DSTROOT}/${PUBLIC_HEADERS_FOLDER_PATH}/dlfcn.h\n";
-                       showEnvVarsInLog = 0;
-               };
-               F959621018849DF20003E4D4 /* add dyld symlink */ = {
-                       isa = PBXShellScriptBuildPhase;
-                       buildActionMask = 8;
-                       files = (
-                       );
-                       inputPaths = (
-                       );
-                       name = "add dyld symlink";
-                       outputPaths = (
-                       );
-                       runOnlyForDeploymentPostprocessing = 1;
-                       shellPath = /bin/bash;
-                       shellScript = "if [[ \"${PLATFORM_NAME}\"  == *simulator* ]]\nthen\n\tcd ${DSTROOT}\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 */ = {
-                       isa = PBXShellScriptBuildPhase;
-                       buildActionMask = 8;
-                       files = (
-                       );
-                       inputPaths = (
-                               "${SRCROOT}/include/mach-o/dyld_priv.h",
-                       );
-                       name = "expand dyld_priv.h macros";
-                       outputPaths = (
-                               "${DSTROOT}/usr/local/include/mach-o/dyld_priv.h",
-                       );
-                       runOnlyForDeploymentPostprocessing = 1;
-                       shellPath = /bin/sh;
-                       shellScript = "${SRCROOT}/bin/expand.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 */ = {
-                       isa = PBXShellScriptBuildPhase;
-                       buildActionMask = 2147483647;
-                       files = (
-                       );
-                       inputPaths = (
-                       );
-                       name = "create dyld_cache_config.h";
-                       outputPaths = (
-                               "$(DERIVED_FILE_DIR)/dyld_cache_config.h",
-                       );
-                       runOnlyForDeploymentPostprocessing = 0;
-                       shellPath = /bin/bash;
-                       shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n    # if iOS SDK not available, use MacOSX SDK\n    ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n    echo -n \"#define ARM_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n    echo -n \"#define ARM_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n    echo -n \"#define ARM64_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_BASE_ARM64[ \\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;
-               };
-               F981C8C21F058F8200452F35 /* mkdir /var/db/dyld */ = {
-                       isa = PBXShellScriptBuildPhase;
-                       buildActionMask = 8;
-                       files = (
-                       );
-                       inputPaths = (
-                       );
-                       name = "mkdir /var/db/dyld";
-                       outputPaths = (
-                       );
-                       runOnlyForDeploymentPostprocessing = 1;
-                       shellPath = /bin/sh;
-                       shellScript = "mkdir -p ${DSTROOT}/private/var/db/dyld";
-                       showEnvVarsInLog = 0;
-               };
-               F98E37772332D048003706B4 /* create dyld_cache_config.h */ = {
-                       isa = PBXShellScriptBuildPhase;
-                       buildActionMask = 2147483647;
-                       files = (
-                       );
-                       inputPaths = (
-                       );
-                       name = "create dyld_cache_config.h";
-                       outputPaths = (
-                               "$(DERIVED_FILE_DIR)/dyld_cache_config.h",
-                       );
-                       runOnlyForDeploymentPostprocessing = 0;
-                       shellPath = /bin/bash;
-                       shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n    # if iOS SDK not available, use MacOSX SDK\n    ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n    echo -n \"#define ARM_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n    echo -n \"#define ARM_SHARED_REGION_SIZE \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    echo \"\"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n    echo -n \"#define ARM64_SHARED_REGION_START \"  >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n    awk '/define SHARED_REGION_BASE_ARM64[ \\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;
+                       shellPath = /bin/sh;
+                       shellScript = "$SRCROOT/build-scripts/update_dyld_shared_cache-build.sh\n";
                };
-               F98E37902332D048003706B4 /* do not install duplicates */ = {
+               F951DA862228E5560057BA43 /* install headers */ = {
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 8;
                        files = (
                        );
+                       inputFileListPaths = (
+                       );
                        inputPaths = (
                        );
-                       name = "do not install duplicates";
+                       name = "install headers";
+                       outputFileListPaths = (
+                       );
                        outputPaths = (
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
-                       shellScript = "if [ \"${INSTALL_LOCATION}\" = \"\" ] \nthen\n     # on iOS, libdyld builds arm libdsc.a and u_d_s_c builds intel libdsc.a\n     # on MacOSX, to avoid collision, u_d_s_c does not install libdsc.a\n    rm -rf ${DSTROOT}/usr/local/include\n    rm -rf ${DSTROOT}/usr/local/lib\n    rm -rf ${DSTROOT}/usr/lib\nfi\n";
+                       shellScript = "# dyld.h and dyld_priv.h are not for use by actual drivers, so they are both in the Runtime directory\n\n$SRCROOT/build-scripts/libdyld-generate-version-headers.sh\nmkdir -p ${DSTROOT}/${PUBLIC_HEADERS_FOLDER_PATH}\ncp ${SRCROOT}/include/mach-o/dyld.h ${DSTROOT}/${PUBLIC_HEADERS_FOLDER_PATH}/dyld.h\ncp ${SRCROOT}/include/dlfcn.h ${DSTROOT}/${PUBLIC_HEADERS_FOLDER_PATH}/dlfcn.h\n";
                        showEnvVarsInLog = 0;
                };
-               F98E37912332D048003706B4 /* mkdir /var/db/dyld */ = {
+               F960A78C1E405E2300840176 /* generate version headers */ = {
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 8;
                        files = (
                        );
                        inputPaths = (
+                               "${SRCROOT}/include/mach-o/dyld_priv.h",
                        );
-                       name = "mkdir /var/db/dyld";
+                       name = "generate version headers";
                        outputPaths = (
+                               "${DSTROOT}/usr/local/include/mach-o/dyld_priv.h",
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
-                       shellScript = "mkdir -p ${DSTROOT}/private/var/db/dyld";
+                       shellScript = "$SRCROOT/build-scripts/libdyld-generate-version-headers.sh\n";
                        showEnvVarsInLog = 0;
                };
-               F99006DF1E411C500013456D /* install dlfcn.h */ = {
+               F96354301DCD74A400895049 /* create dyld_cache_config.h */ = {
                        isa = PBXShellScriptBuildPhase;
-                       buildActionMask = 8;
+                       buildActionMask = 2147483647;
                        files = (
                        );
                        inputPaths = (
                        );
-                       name = "install dlfcn.h";
+                       name = "create dyld_cache_config.h";
                        outputPaths = (
+                               "$(DERIVED_FILE_DIR)/dyld_cache_config.h",
                        );
-                       runOnlyForDeploymentPostprocessing = 1;
-                       shellPath = /bin/sh;
-                       shellScript = "# xcode only lets you install public headers to one directory\ncp ${SRCROOT}/include/dlfcn.h  ${DSTROOT}/usr/include/\n";
+                       runOnlyForDeploymentPostprocessing = 0;
+                       shellPath = /bin/bash;
+                       shellScript = "$SRCROOT/build-scripts/generate-cache-config-header.sh\n";
                        showEnvVarsInLog = 0;
                };
-               F991E3030FF1A4EC0082CCC9 /* do not install duplicates */ = {
+               F99006DF1E411C500013456D /* install dlfcn.h */ = {
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 8;
                        files = (
                        );
                        inputPaths = (
                        );
-                       name = "do not install duplicates";
+                       name = "install dlfcn.h";
                        outputPaths = (
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
-                       shellScript = "if [ \"${INSTALL_LOCATION}\" = \"\" ] \nthen\n    # on iOS, libdyld builds arm libdsc.a and u_d_s_c builds intel libdsc.a\n    # on MacOSX, to avoid collision, u_d_s_c does not install libdsc.a\n    rm -rf ${DSTROOT}/usr/local/include/mach-o/dsc_iterator.h\n    rm -rf ${DSTROOT}/usr/local/include/mach-o/dsc_extractor.h\n    rm -rf ${DSTROOT}/usr/local/lib/slc_builder.dylib\n    rm -rf ${DSTROOT}/usr/local/lib/libdsc.a\n    rm -rf ${DSTROOT}/usr/lib/dsc_extractor.bundle\nfi\n";
+                       shellScript = "# xcode only lets you install public headers to one directory\ncp ${SRCROOT}/include/dlfcn.h  ${DSTROOT}/usr/include/\ncp ${SRCROOT}/include/dlfcn_private.h  ${DSTROOT}/usr/local/include/\n#  manual install of modulemap\ncp ${SRCROOT}/include/mach-o/dyld.modulemap  ${DSTROOT}/usr/include/mach-o\n";
                        showEnvVarsInLog = 0;
                };
-               F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */ = {
+               F991E3030FF1A4EC0082CCC9 /* do not install duplicates */ = {
                        isa = PBXShellScriptBuildPhase;
                        buildActionMask = 8;
                        files = (
                        );
                        inputPaths = (
                        );
-                       name = "suppress macosx dyld_shared_cache_util";
+                       name = "do not install duplicates";
                        outputPaths = (
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
-                       shellScript = "# iPhone wants a copy of dyld_shared_cache_util on the device\n# MacOSX does not need a copy because update_dyld_shared_cache target already installed a copy\nif [ \"${PLATFORM_NAME}\" = \"macosx\" ] \nthen\nrm -rf ${DSTROOT}/usr/local/bin/dyld_shared_cache_util\nrm -rf ${DSTROOT}/usr/local/bin/dyld_closure_util\nfi\n";
+                       shellScript = "if [ \"${INSTALL_LOCATION}\" = \"\" ] \nthen\n    # on iOS, libdyld builds arm libdsc.a and u_d_s_c builds intel libdsc.a\n    # on MacOSX, to avoid collision, u_d_s_c does not install libdsc.a\n    rm -rf ${DSTROOT}/usr/local/include/mach-o/dsc_iterator.h\n    rm -rf ${DSTROOT}/usr/local/include/mach-o/dsc_extractor.h\n    rm -rf ${DSTROOT}/usr/local/lib/*.a\n    rm -rf ${DSTROOT}/usr/lib/dsc_extractor.bundle\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# 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";
+                       shellScript = "${SRCROOT}/build-scripts/configure-dyld-archives.sh\n";
                        showEnvVarsInLog = 0;
                };
                F9F6F42B1C1FB0AE00BD8FED /* build */ = {
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        shellPath = /bin/sh;
-                       shellScript = "SYMROOT=${BUILD_DIR}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/dyld_tests\nOBJROOT=${PROJECT_TEMP_DIR}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}\nSDKROOT=${SDKROOT:-$(xcrun -sdk macosx.internal --show-sdk-path)}\nDEPLOYMENT_TARGET_CLANG_FLAG_NAME=${DEPLOYMENT_TARGET_CLANG_FLAG_NAME:-\"mmacosx-version-min\"}\nARCHS=${RC_ARCHS}\nDERIVED_FILES_DIR=${DERIVED_FILES_DIR}\nLDFLAGS=\"-L$BUILT_PRODUCTS_DIR\"\n#LLBUILD=$(xcrun --sdk $SDKROOT --find llbuild 2> /dev/null)\nNINJA=${LLBUILD:-`xcrun  --sdk $SDKROOT --find ninja  2> /dev/null`}\nBUILD_TARGET=${ONLY_BUILD_TEST:-all}\n\nif [ ! -z \"$LLBUILD\" ]; then\n  NINJA=\"$LLBUILD ninja build\"\nfi\n\nOSVERSION=\"10.14\"\nif [ ! -z \"$DEPLOYMENT_TARGET_CLANG_ENV_NAME\" ]; then\n    OSVERSION=${!DEPLOYMENT_TARGET_CLANG_ENV_NAME}\nfi\n\nif [ -z \"$SRCROOT\" ]; then\n    echo \"Error $$SRCROOT must be set\"\nfi\n\nif [ -z \"$ARCHS\" ]; then\n    PLATFORM_NAME=${PLATFORM_NAME:macosx}\n    case \"$PLATFORM_NAME\" in\n       \"watchos\")   ARCHS=\"armv7k arm64_32\"\n       ;;\n       \"appletvos\") ARCHS=\"arm64\"\n       ;;\n       \"macosx\")    ARCHS=${ARCHS_STANDARD_64_BIT}\n       ;;\n       *)           ARCHS=${ARCHS_STANDARD_32_64_BIT}\n       ;;\n    esac\nfi\n\nif [ -z \"$ARCHS\" ]; then\n    ARCHS=\"x86_64\"\nfi\n\n/bin/mkdir -p ${DERIVED_FILES_DIR}\nTMPFILE=$(mktemp ${DERIVED_FILES_DIR}/config.ninja.XXXXXX)\n\necho \"OBJROOT = $OBJROOT\" >> $TMPFILE\necho \"OSFLAG = $DEPLOYMENT_TARGET_CLANG_FLAG_NAME\" >> $TMPFILE\necho \"OSVERSION = $OSVERSION\" >> $TMPFILE\necho \"SDKROOT = $SDKROOT\" >> $TMPFILE\necho \"SRCROOT = $SRCROOT\" >> $TMPFILE\necho \"SYMROOT = $SYMROOT\" >> $TMPFILE\necho \"BUILT_PRODUCTS_DIR = $BUILT_PRODUCTS_DIR\" >> $TMPFILE\necho \"INSTALL_GROUP = $INSTALL_GROUP\" >> $TMPFILE\necho \"INSTALL_MODE_FLAG = $INSTALL_MODE_FLAG\" >> $TMPFILE\necho \"INSTALL_OWNER = $INSTALL_OWNER\" >> $TMPFILE\necho \"INSTALL_DIR = $INSTALL_DIR\" >> $TMPFILE\necho \"USER_HEADER_SEARCH_PATHS = $USER_HEADER_SEARCH_PATHS\" >> $TMPFILE\necho \"SYSTEM_HEADER_SEARCH_PATHS = $SYSTEM_HEADER_SEARCH_PATHS\" >> $TMPFILE\necho \"ARCHS = $ARCHS\" >> $TMPFILE\necho \"DERIVED_FILES_DIR = $DERIVED_FILES_DIR\" >> $TMPFILE\necho \"LDFLAGS = $LDFLAGS\" >> $TMPFILE\n\n/usr/bin/rsync -vc $TMPFILE ${DERIVED_FILES_DIR}/config.ninja\n/bin/rm -f $TMPFILE\n\n${SRCROOT}/testing/build_ninja.py ${DERIVED_FILES_DIR}/config.ninja\n${NINJA} -C ${DERIVED_FILES_DIR} ${BUILD_TARGET}\n";
+                       shellScript = "$SRCROOT/build-scripts/dyld_tests-build.sh\n";
                        showEnvVarsInLog = 0;
                };
 /* End PBXShellScriptBuildPhase section */
                                C17984D61FE9E9160057D002 /* mrm_shared_cache_builder.cpp in Sources */,
                                F93D73521F8FF7C2007D9413 /* MachOLoaded.cpp in Sources */,
                                F93D73531F8FF7C2007D9413 /* MachOAnalyzer.cpp in Sources */,
+                               F971EC7B2343CD9A000BCEAA /* MachOAnalyzerSet.cpp in Sources */,
                                37554F421E3F169600407388 /* CacheBuilder.cpp in Sources */,
                                C18A75F9209A1AF600DC01BB /* JSONReader.mm in Sources */,
                                C11ECA92233C307C0011726F /* SharedCacheBuilder.cpp in Sources */,
+                               C116F1A223F4D73A002D386B /* RootsChecker.cpp in Sources */,
                                37554F481E3F16BA00407388 /* OptimizerBranches.cpp in Sources */,
                                37554F441E3F16A900407388 /* OptimizerObjC.cpp in Sources */,
                                37554F581E3F7B6500407388 /* PathOverrides.cpp in Sources */,
                                37908A301E3ADD15009613FA /* Diagnostics.cpp in Sources */,
                                37554F491E3F76E400407388 /* DyldSharedCache.cpp in Sources */,
                                37908A2F1E3A864E009613FA /* dyld_shared_cache_builder.mm in Sources */,
+                               E9C2FAD823AA8B5C0077E966 /* IMPCaches.cpp in Sources */,
                                37554F461E3F16B600407388 /* OptimizerLinkedit.cpp in Sources */,
                                C1F003CE213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */,
                                37908A321E3ED667009613FA /* FileUtils.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               C14965DC22BDCF6800568D15 /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               C1BF4DAB2357B14700B0F1AE /* kernel_collection_builder.cpp in Sources */,
+                               C14965F822C3281A00568D15 /* ClosureFileSystemNull.cpp in Sources */,
+                               C14965E822BEBF2300568D15 /* Diagnostics.cpp in Sources */,
+                               C14965F622C327FB00568D15 /* PathOverrides.cpp in Sources */,
+                               C14965F722C3280B00568D15 /* AdjustDylibSegments.cpp in Sources */,
+                               C14965E722BDCF8300568D15 /* dyld_app_cache_util.cpp in Sources */,
+                               C14965EA22BEC04800568D15 /* MachOFile.cpp in Sources */,
+                               C14965ED22C09B6100568D15 /* FileUtils.cpp in Sources */,
+                               C14965F022C3203200568D15 /* OptimizerLinkedit.cpp in Sources */,
+                               C14965EE22C31F7C00568D15 /* CacheBuilder.cpp in Sources */,
+                               C14965EC22BEC05800568D15 /* ClosureFileSystemPhysical.cpp in Sources */,
+                               C14965F122C3203E00568D15 /* OptimizerBranches.cpp in Sources */,
+                               C1BDD446234EAF500095C7DC /* MachOAppCache.cpp in Sources */,
+                               C14965EB22BEC05000568D15 /* MachOLoaded.cpp in Sources */,
+                               C1BDD443234EA7DD0095C7DC /* AppCacheBuilder.cpp in Sources */,
+                               C14965E922BEBF2800568D15 /* MachOAnalyzer.cpp in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+               C14C355C230531820059E04C /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               C14C356B230539BE0059E04C /* MachOAnalyzer.cpp in Sources */,
+                               C14C356A2305376A0059E04C /* Diagnostics.cpp in Sources */,
+                               C14C3569230537630059E04C /* MachOFile.cpp in Sources */,
+                               C18839E523480866004E30FA /* ClosureFileSystemPhysical.cpp in Sources */,
+                               C14C356C230539C20059E04C /* MachOLoaded.cpp in Sources */,
+                               C14C3563230531830059E04C /* testing/run-static/run-static.cpp in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                C187B9001FE063A40042D3B7 /* Sources */ = {
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               E9EC319A240B0BA8001705D6 /* IMPCaches.cpp in Sources */,
                                C187B9181FE068260042D3B7 /* DyldSharedCache.cpp in Sources */,
                                C1F003D0213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */,
                                C1436B2C203BE67D00028AF1 /* FileUtils.cpp in Sources */,
                                C187B90F1FE067D30042D3B7 /* ClosureBuilder.cpp in Sources */,
                                C187B9131FE067F10042D3B7 /* CacheBuilder.cpp in Sources */,
                                C187B9121FE067E60042D3B7 /* MachOAnalyzer.cpp in Sources */,
+                               C116F1A423F4D742002D386B /* RootsChecker.cpp in Sources */,
                                C187B9161FE0680A0042D3B7 /* PathOverrides.cpp in Sources */,
                                C187B9171FE068180042D3B7 /* Diagnostics.cpp in Sources */,
                                C187B9151FE068000042D3B7 /* OptimizerObjC.cpp in Sources */,
+                               E9EC319E240B18E6001705D6 /* JSONReader.mm in Sources */,
                                C187B9101FE067D90042D3B7 /* MachOFile.cpp in Sources */,
                                C187B9111FE067E10042D3B7 /* MachOLoaded.cpp in Sources */,
+                               F971EC7D2343CDB9000BCEAA /* MachOAnalyzerSet.cpp in Sources */,
                                C187B90D1FE067C70042D3B7 /* Closure.cpp in Sources */,
                                C11ECA94233C307C0011726F /* SharedCacheBuilder.cpp in Sources */,
                                C1D268311FE0891C009F115B /* mrm_shared_cache_builder.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               C1BF4DAD2359254500B0F1AE /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               C1BF4DC0235938CB00B0F1AE /* MachOAppCache.cpp in Sources */,
+                               C1BF4DBF235938BD00B0F1AE /* MachOFile.cpp in Sources */,
+                               C1BF4DBD235938B600B0F1AE /* MachOAnalyzer.cpp in Sources */,
+                               C1BF4DB82359385700B0F1AE /* CacheBuilder.cpp in Sources */,
+                               C1BF4DBB235938AC00B0F1AE /* OptimizerLinkedit.cpp in Sources */,
+                               C1BF4DC1235938D400B0F1AE /* AdjustDylibSegments.cpp in Sources */,
+                               C1BF4DB7235925BA00B0F1AE /* Diagnostics.cpp in Sources */,
+                               C1BF4DB4235925A000B0F1AE /* kernel_collection_builder.cpp in Sources */,
+                               C1BF4DBC235938B000B0F1AE /* OptimizerBranches.cpp in Sources */,
+                               C1BF4DB92359386C00B0F1AE /* AppCacheBuilder.cpp in Sources */,
+                               C1BF4DBE235938BA00B0F1AE /* MachOLoaded.cpp in Sources */,
+                               C1BF4DB6235925B400B0F1AE /* ClosureFileSystemNull.cpp in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
                F92C7DE621E59840000D12B5 /* Sources */ = {
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               F93D73471F8C4E55007D9413 /* PathOverrides.cpp in Sources */,
-                               C1F003CC213F3CB4002D9DC9 /* ClosureFileSystemNull.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 */,
-                               F986921F1DC3F98700CBEDE6 /* CacheBuilder.cpp in Sources */,
-                               F98692231DC403F900CBEDE6 /* AdjustDylibSegments.cpp in Sources */,
-                               F98692191DC3EFDA00CBEDE6 /* FileUtils.cpp in Sources */,
-                               F98692201DC3F99300CBEDE6 /* Diagnostics.cpp in Sources */,
-                               C11ECA90233C307C0011726F /* SharedCacheBuilder.cpp in Sources */,
-                               F9D862401DC57A27000A199A /* OptimizerObjC.cpp in Sources */,
-                               F94182D51E60A2F100D8EF25 /* OptimizerBranches.cpp in Sources */,
-                               F9D8623F1DC41043000A199A /* OptimizerLinkedit.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                F9556D4520C21DD9004DF62A /* MachOFile.cpp in Sources */,
                                F9556D4620C21DD9004DF62A /* MachOLoaded.cpp in Sources */,
                                F9556D4720C21DD9004DF62A /* MachOAnalyzer.cpp in Sources */,
+                               F971EC752342D373000BCEAA /* MachOAnalyzerSet.cpp in Sources */,
                                F9556D4820C21DDF004DF62A /* ClosureFileSystemPhysical.cpp in Sources */,
+                               F9FA17F4235A71DB009B0907 /* DyldSharedCache.cpp in Sources */,
                                F9556D4920C21DF5004DF62A /* Diagnostics.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        buildActionMask = 2147483647;
                        files = (
                                F9653F941FAE51ED008B5D93 /* MachOAnalyzer.cpp in Sources */,
+                               E9EC3199240B0B95001705D6 /* IMPCaches.cpp in Sources */,
+                               E9EC319D240B18DC001705D6 /* JSONReader.mm in Sources */,
+                               F971EC7C2343CDA4000BCEAA /* MachOAnalyzerSet.cpp in Sources */,
                                C1F003CF213F3CB4002D9DC9 /* ClosureFileSystemNull.cpp in Sources */,
                                F9653F8E1FAE51C9008B5D93 /* Closure.cpp in Sources */,
                                F9653F8F1FAE51C9008B5D93 /* ClosureBuilder.cpp in Sources */,
                                F9653F921FAE51C9008B5D93 /* MachOLoaded.cpp in Sources */,
                                F96354461DCD74BC00895049 /* update_dyld_sim_shared_cache.cpp in Sources */,
                                F96354331DCD74A400895049 /* DyldSharedCache.cpp in Sources */,
+                               C116F1A323F4D73B002D386B /* RootsChecker.cpp in Sources */,
                                F96354341DCD74A400895049 /* CacheBuilder.cpp in Sources */,
                                F96354351DCD74A400895049 /* AdjustDylibSegments.cpp in Sources */,
                                F96354361DCD74A400895049 /* FileUtils.cpp in Sources */,
                                F9CC10D81F5F1D4E0021BFE2 /* MachOFile.cpp in Sources */,
                                F9A5E6171F5C967C0030C490 /* MachOLoaded.cpp in Sources */,
                                F9CC10D71F5F1D480021BFE2 /* MachOAnalyzer.cpp in Sources */,
+                               F971EC762342DF03000BCEAA /* MachOAnalyzerSet.cpp in Sources */,
                                F9DFEA7D1F588506003BF8A7 /* ClosurePrinter.cpp in Sources */,
+                               C116F19E23F4B11B002D386B /* RootsChecker.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               F98E37792332D048003706B4 /* PathOverrides.cpp in Sources */,
-                               F98E377A2332D048003706B4 /* ClosureFileSystemNull.cpp in Sources */,
-                               F98E377B2332D048003706B4 /* Closure.cpp in Sources */,
-                               F98E377C2332D048003706B4 /* ClosureWriter.cpp in Sources */,
-                               F98E377D2332D048003706B4 /* ClosureFileSystemPhysical.cpp in Sources */,
-                               F98E377E2332D048003706B4 /* ClosureBuilder.cpp in Sources */,
-                               F98E377F2332D048003706B4 /* MachOFile.cpp in Sources */,
-                               F98E37802332D048003706B4 /* MachOLoaded.cpp in Sources */,
-                               F98E37812332D048003706B4 /* MachOAnalyzer.cpp in Sources */,
                                F98E37822332D048003706B4 /* update_dyld_shared_cache.cpp in Sources */,
-                               F98E37832332D048003706B4 /* DyldSharedCache.cpp in Sources */,
-                               F98E37842332D048003706B4 /* CacheBuilder.cpp in Sources */,
-                               F98E37852332D048003706B4 /* AdjustDylibSegments.cpp in Sources */,
-                               F98E37862332D048003706B4 /* FileUtils.cpp in Sources */,
-                               F98E37872332D048003706B4 /* Diagnostics.cpp in Sources */,
-                               C11ECA91233C307C0011726F /* SharedCacheBuilder.cpp in Sources */,
-                               F98E37882332D048003706B4 /* OptimizerObjC.cpp in Sources */,
-                               F98E37892332D048003706B4 /* OptimizerBranches.cpp in Sources */,
-                               F98E378A2332D048003706B4 /* OptimizerLinkedit.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               F99B8E630FEC11B400701838 /* dyld_shared_cache_util.cpp in Sources */,
                                C176CB5C2321AB74009C1259 /* ClosureFileSystemPhysical.cpp in Sources */,
                                C1960ED02090D9F0007E3E6B /* Diagnostics.cpp in Sources */,
                                C1960ED42090DA09007E3E6B /* Closure.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 */,
+                               F97D395823A97B2C00BD3B5A /* dsc_extractor.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               DE49C499238EC59500CD7FFB /* MachOFile.cpp in Sources */,
+                               DE49C497238EC57B00CD7FFB /* Diagnostics.cpp in Sources */,
+                               DE49C4A3238EEE3400CD7FFB /* dsc_iterator.cpp in Sources */,
+                               DE49C49C238EC60D00CD7FFB /* Closure.cpp in Sources */,
+                               DE49C496238EC55300CD7FFB /* DyldSharedCache.cpp in Sources */,
+                               DE49C49A238EC5AD00CD7FFB /* MachOLoaded.cpp in Sources */,
                                F9D1001814D8D13D00099D91 /* dsc_extractor.cpp in Sources */,
-                               F9D1001D14D8D19500099D91 /* dsc_iterator.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                F93D73411F8404FA007D9413 /* MachOLoaded.cpp in Sources */,
                                F93D73401F8404A2007D9413 /* MachOFile.cpp in Sources */,
                                F93D73431F842CBF007D9413 /* MachOAnalyzer.cpp in Sources */,
+                               F971EC772343CD46000BCEAA /* MachOAnalyzerSet.cpp in Sources */,
                                F93D733D1F82F03F007D9413 /* Closure.cpp in Sources */,
                                F93D733E1F82F03F007D9413 /* ClosureWriter.cpp in Sources */,
                                373C58F1219CE478003442D5 /* BootArgs.cpp in Sources */,
                                F93D733F1F82F03F007D9413 /* ClosureBuilder.cpp in Sources */,
                                F93D73421F8421CC007D9413 /* PathOverrides.cpp in Sources */,
+                               C116F19C23F4B11B002D386B /* RootsChecker.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                F93D73441F8475C3007D9413 /* MachOFile.cpp in Sources */,
                                F93D73451F8475C3007D9413 /* MachOLoaded.cpp in Sources */,
                                F93D73461F8475C3007D9413 /* MachOAnalyzer.cpp in Sources */,
+                               F971EC782343CD63000BCEAA /* MachOAnalyzerSet.cpp in Sources */,
                                F90108611E2AD96000870568 /* PathOverrides.cpp in Sources */,
                                F9DFEA781F54FACF003BF8A7 /* ClosureBuilder.cpp in Sources */,
+                               C116F19D23F4B11B002D386B /* RootsChecker.cpp in Sources */,
                                F9DFEA741F54DB25003BF8A7 /* ClosureWriter.cpp in Sources */,
                                F97C619F1D9829AA00A84CD7 /* libdyldEntryVector.cpp in Sources */,
                        );
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               F9F2A5700F7AEEE300B7C9EB /* dsc_iterator.cpp in Sources */,
+                               F97D395723A1CBCA00BD3B5A /* Diagnostics.cpp in Sources */,
+                               F97D395523A1CBB600BD3B5A /* MachOFile.cpp in Sources */,
+                               F97D395623A1CBB600BD3B5A /* MachOLoaded.cpp in Sources */,
+                               DE9A811323982A3A00664840 /* dsc_iterator.cpp in Sources */,
                                F9CE307A1208F1B50098B590 /* dsc_extractor.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        target = 37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */;
                        targetProxy = 37A0AD0E1C16000F00731E50 /* PBXContainerItemProxy */;
                };
-               C1033EA822611306004407FB /* PBXTargetDependency */ = {
+               C14C3568230531EA0059E04C /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
-                       target = F9D1001114D8D0BA00099D91 /* dsc_extractor */;
-                       targetProxy = C1033EA722611306004407FB /* PBXContainerItemProxy */;
+                       target = C14C355F230531820059E04C /* run-static */;
+                       targetProxy = C14C3567230531EA0059E04C /* PBXContainerItemProxy */;
                };
                C187B90C1FE067590042D3B7 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        target = C187B8FF1FE063A40042D3B7 /* libslc_builder.dylib */;
                        targetProxy = C187B90B1FE067590042D3B7 /* PBXContainerItemProxy */;
                };
-               D8668AD01ECE335F005E7D31 /* PBXTargetDependency */ = {
+               C18F05362374D5B100DC6CCA /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = 3721A634230CABAF00594066 /* test_support */;
+                       targetProxy = C18F05352374D5B100DC6CCA /* PBXContainerItemProxy */;
+               };
+               C1B4759723E65F9600515793 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = 37F597CC2061EB4200F9B6F9 /* dyld_usage */;
+                       targetProxy = C1B4759623E65F9600515793 /* PBXContainerItemProxy */;
+               };
+               C1BF4DC32359390A00B0F1AE /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = C1BF4DAF2359254500B0F1AE /* libKernelCollectionBuilder */;
+                       targetProxy = C1BF4DC22359390A00B0F1AE /* PBXContainerItemProxy */;
+               };
+               C1C6403723E4EC1300ED4B46 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = F9D1001114D8D0BA00099D91 /* dsc_extractor */;
+                       targetProxy = C1C6403623E4EC1300ED4B46 /* PBXContainerItemProxy */;
+               };
+               C1C6403923E4EC1C00ED4B46 /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */;
+                       targetProxy = C1C6403823E4EC1C00ED4B46 /* PBXContainerItemProxy */;
+               };
+               C1C6403B23E4EC3000ED4B46 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        target = F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */;
-                       targetProxy = D8668ACF1ECE335F005E7D31 /* PBXContainerItemProxy */;
+                       targetProxy = C1C6403A23E4EC3000ED4B46 /* PBXContainerItemProxy */;
                };
                F908134811D3ED1A00626CC1 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        target = F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */;
                        targetProxy = F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */;
                };
-               F94182D81E60F0BE00D8EF25 /* PBXTargetDependency */ = {
-                       isa = PBXTargetDependency;
-                       target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */;
-                       targetProxy = F94182D71E60F0BE00D8EF25 /* PBXContainerItemProxy */;
-               };
                F94182DA1E60F0C000D8EF25 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        target = F9F2A5580F7AEE9800B7C9EB /* libdsc */;
                        target = F9D1001114D8D0BA00099D91 /* dsc_extractor */;
                        targetProxy = F94182DB1E60F16900D8EF25 /* PBXContainerItemProxy */;
                };
-               F96543A11E343601003C5540 /* PBXTargetDependency */ = {
+               F9A8E1B024120DD000CEB6BF /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
-                       target = F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */;
-                       targetProxy = F96543A01E343601003C5540 /* PBXContainerItemProxy */;
-               };
-               F99B8EB20FEC220C00701838 /* PBXTargetDependency */ = {
-                       isa = PBXTargetDependency;
-                       target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */;
-                       targetProxy = F99B8EB10FEC220C00701838 /* PBXContainerItemProxy */;
+                       target = F9556D3820C1F896004DF62A /* dyld_info */;
+                       targetProxy = F9A8E1AF24120DD000CEB6BF /* PBXContainerItemProxy */;
                };
                F9B4D78012AD9736000605A6 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                                SDKROOT = macosx.internal;
                                SUPPORTED_PLATFORMS = macosx;
                                TOOLCHAINS = default;
-                               USER_HEADER_SEARCH_PATHS = "../launch-cache/";
-                               VALID_ARCHS = "x86_64 x86_64h";
+                               USER_HEADER_SEARCH_PATHS = "";
                        };
                        name = Debug;
                };
                                SDKROOT = macosx.internal;
                                SUPPORTED_PLATFORMS = macosx;
                                TOOLCHAINS = default;
-                               USER_HEADER_SEARCH_PATHS = "../launch-cache/";
-                               VALID_ARCHS = "x86_64 x86_64h";
+                               USER_HEADER_SEARCH_PATHS = "";
                                VERSIONING_SYSTEM = "apple-generic";
                        };
                        name = Release;
                37A0AD0C1C15FFF500731E50 /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
+                               INSTALLHDRS_COPY_PHASE = YES;
+                               INSTALLHDRS_SCRIPT_PHASE = YES;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                        };
                        name = Debug;
                37A0AD0D1C15FFF500731E50 /* Release */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
+                               INSTALLHDRS_COPY_PHASE = YES;
+                               INSTALLHDRS_SCRIPT_PHASE = YES;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                        };
                        name = Release;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SDKROOT = macosx.internal;
                                SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
-                               VALID_ARCHS = "arm64 arm64e x86_64 arm64_32 armv7k";
                        };
                        name = Debug;
                };
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SDKROOT = macosx.internal;
                                SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
-                               VALID_ARCHS = "arm64 arm64e x86_64 arm64_32 armv7k";
+                       };
+                       name = Release;
+               };
+               C14965E522BDCF6900568D15 /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_ENABLE_OBJC_WEAK = YES;
+                               CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+                               CODE_SIGN_STYLE = Automatic;
+                               COPY_PHASE_STRIP = NO;
+                               DEBUG_INFORMATION_FORMAT = dwarf;
+                               ENABLE_TESTABILITY = YES;
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_ENABLE_CPP_EXCEPTIONS = YES;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "DEBUG=1",
+                                       "$(inherited)",
+                                       "BUILDING_CACHE_BUILDER=1",
+                                       "BUILDING_APP_CACHE_UTIL=1",
+                               );
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               MACOSX_DEPLOYMENT_TARGET = 10.14;
+                               MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+                               MTL_FAST_MATH = YES;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx.internal;
+                       };
+                       name = Debug;
+               };
+               C14965E622BDCF6900568D15 /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CLANG_ANALYZER_NONNULL = YES;
+                               CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+                               CLANG_ENABLE_MODULES = YES;
+                               CLANG_ENABLE_OBJC_ARC = YES;
+                               CLANG_ENABLE_OBJC_WEAK = YES;
+                               CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+                               CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+                               CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+                               CODE_SIGN_STYLE = Automatic;
+                               COPY_PHASE_STRIP = NO;
+                               DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+                               ENABLE_NS_ASSERTIONS = NO;
+                               GCC_ENABLE_CPP_EXCEPTIONS = YES;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "BUILDING_CACHE_BUILDER=1",
+                                       "BUILDING_APP_CACHE_UTIL=1",
+                               );
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+                               GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+                               MACOSX_DEPLOYMENT_TARGET = 10.14;
+                               MTL_ENABLE_DEBUG_INFO = NO;
+                               MTL_FAST_MATH = YES;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx.internal;
+                       };
+                       name = Release;
+               };
+               C14C3565230531830059E04C /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "-";
+                               CODE_SIGN_STYLE = Automatic;
+                               COPY_PHASE_STRIP = NO;
+                               DEBUG_INFORMATION_FORMAT = dwarf;
+                               DEVELOPMENT_TEAM = "";
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "DEBUG=1",
+                                       "BUILDING_RUN_STATIC=1",
+                                       "$(inherited)",
+                               );
+                               INSTALL_PATH = /AppleInternal/CoreOS/tests/dyld;
+                               IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+                               MACOSX_DEPLOYMENT_TARGET = 10.14;
+                               OTHER_CODE_SIGN_FLAGS = "--entitlements $SRCROOT/testing/run-static/jit_entitlement.plist";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               PROVISIONING_PROFILE_SPECIFIER = "";
+                               SDKROOT = macosx.internal;
+                               SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos watchsimulator iphonesimulator appletvsimulator";
+                       };
+                       name = Debug;
+               };
+               C14C3566230531830059E04C /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CODE_SIGN_IDENTITY = "-";
+                               CODE_SIGN_STYLE = Automatic;
+                               COPY_PHASE_STRIP = NO;
+                               DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+                               DEVELOPMENT_TEAM = "";
+                               GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_RUN_STATIC=1";
+                               INSTALL_PATH = /AppleInternal/CoreOS/tests/dyld;
+                               IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+                               MACOSX_DEPLOYMENT_TARGET = 10.14;
+                               OTHER_CODE_SIGN_FLAGS = "--entitlements $SRCROOT/testing/run-static/jit_entitlement.plist";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               PROVISIONING_PROFILE_SPECIFIER = "";
+                               SDKROOT = macosx.internal;
+                               SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos watchsimulator iphonesimulator appletvsimulator";
                        };
                        name = Release;
                };
                                SDKROOT = macosx.internal;
                                STRIP_INSTALLED_PRODUCT = NO;
                                STRIP_STYLE = "non-global";
-                               USER_HEADER_SEARCH_PATHS = "../launch-cache/";
-                               VALID_ARCHS = "x86_64 x86_64h";
+                               USER_HEADER_SEARCH_PATHS = "";
                        };
                        name = Debug;
                };
                                PRODUCT_NAME = slc_builder;
                                SDKROOT = macosx.internal;
                                STRIP_STYLE = "non-global";
-                               USER_HEADER_SEARCH_PATHS = "../launch-cache/";
-                               VALID_ARCHS = "x86_64 x86_64h";
+                               USER_HEADER_SEARCH_PATHS = "";
                                ZERO_LINK = NO;
                        };
                        name = Release;
                };
+               C1BDD43D234E8FA00095C7DC /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CODE_SIGN_STYLE = Automatic;
+                               INSTALLHDRS_SCRIPT_PHASE = YES;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                       };
+                       name = Debug;
+               };
+               C1BDD43E234E8FA00095C7DC /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CODE_SIGN_STYLE = Automatic;
+                               INSTALLHDRS_SCRIPT_PHASE = YES;
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                       };
+                       name = Release;
+               };
+               C1BF4DB22359254500B0F1AE /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CODE_SIGN_STYLE = Automatic;
+                               COPY_PHASE_STRIP = NO;
+                               DEBUG_INFORMATION_FORMAT = dwarf;
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               GCC_ENABLE_CPP_EXCEPTIONS = YES;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "DEBUG=1",
+                                       "BUILDING_CACHE_BUILDER=1",
+                                       "BUILDING_APP_CACHE_UTIL=1",
+                                       "$(inherited)",
+                               );
+                               GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+                               INSTALLHDRS_COPY_PHASE = YES;
+                               INSTALL_PATH = "$(DT_TOOLCHAIN_DIR)/usr/lib";
+                               LD_DYLIB_INSTALL_NAME = "@rpath/$(PRODUCT_NAME).dylib";
+                               MACOSX_DEPLOYMENT_TARGET = 10.10;
+                               OTHER_LDFLAGS = "-Wl,-no_warn_inits";
+                               OTHER_TAPI_FLAGS = "-extra-private-header $(SRCROOT)/dyld3/shared-cache/kernel_collection_builder.h";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx.internal;
+                               SUPPORTS_TEXT_BASED_API = YES;
+                               TAPI_VERIFY_MODE = Pedantic;
+                       };
+                       name = Debug;
+               };
+               C1BF4DB32359254500B0F1AE /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               CODE_SIGN_STYLE = Automatic;
+                               COPY_PHASE_STRIP = NO;
+                               DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+                               DYLIB_COMPATIBILITY_VERSION = 1;
+                               DYLIB_CURRENT_VERSION = 1;
+                               GCC_ENABLE_CPP_EXCEPTIONS = YES;
+                               GCC_PREPROCESSOR_DEFINITIONS = (
+                                       "BUILDING_CACHE_BUILDER=1",
+                                       "BUILDING_APP_CACHE_UTIL=1",
+                               );
+                               GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+                               INSTALLHDRS_COPY_PHASE = YES;
+                               INSTALL_PATH = "$(DT_TOOLCHAIN_DIR)/usr/lib";
+                               LD_DYLIB_INSTALL_NAME = "@rpath/$(PRODUCT_NAME).dylib";
+                               MACOSX_DEPLOYMENT_TARGET = 10.10;
+                               OTHER_TAPI_FLAGS = "-extra-private-header $(SRCROOT)/dyld3/shared-cache/kernel_collection_builder.h";
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                               SDKROOT = macosx.internal;
+                               SUPPORTS_TEXT_BASED_API = YES;
+                               TAPI_VERIFY_MODE = Pedantic;
+                       };
+                       name = Release;
+               };
                F908134311D3ED0C00626CC1 /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO;
                                GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
                                GCC_WARN_MISSING_PARENTHESES = YES;
-                               GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
                                GCC_WARN_SHADOW = YES;
                                GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
                                GCC_WARN_UNUSED_FUNCTION = YES;
                                PRODUCT_NAME = update_dyld_shared_cache;
                                SDKROOT = macosx.internal;
                                USE_HEADERMAP = NO;
-                               VALID_ARCHS = x86_64;
                        };
                        name = Debug;
                };
                                GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
                                GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
                                GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
-                               GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
                                GCC_WARN_SHADOW = YES;
                                GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
                                INSTALL_PATH = /usr/bin;
                                STRIP_INSTALLED_PRODUCT = YES;
                                STRIP_STYLE = debugging;
                                USE_HEADERMAP = NO;
-                               VALID_ARCHS = x86_64;
                                VERSIONING_SYSTEM = "apple-generic";
                        };
                        name = Release;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CODE_SIGN_IDENTITY = "-";
                                COPY_PHASE_STRIP = NO;
+                               DEAD_CODE_STRIPPING = YES;
                                DEBUG_INFORMATION_FORMAT = dwarf;
                                ENABLE_TESTABILITY = YES;
                                GCC_C_LANGUAGE_STANDARD = gnu11;
                                MACOSX_DEPLOYMENT_TARGET = 10.14;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                PRODUCT_NAME = "$(TARGET_NAME)";
+                               STRIP_STYLE = debugging;
                                SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
                        };
                        name = Debug;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CODE_SIGN_IDENTITY = "-";
                                COPY_PHASE_STRIP = NO;
+                               DEAD_CODE_STRIPPING = YES;
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                ENABLE_NS_ASSERTIONS = NO;
                                GCC_C_LANGUAGE_STANDARD = gnu11;
                                MACOSX_DEPLOYMENT_TARGET = 10.14;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                PRODUCT_NAME = "$(TARGET_NAME)";
+                               STRIP_STYLE = debugging;
                                SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
                        };
                        name = Release;
                                GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO;
                                GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
                                GCC_WARN_MISSING_PARENTHESES = YES;
-                               GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
                                GCC_WARN_SHADOW = YES;
                                GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
                                GCC_WARN_UNUSED_FUNCTION = YES;
                                GCC_WARN_UNUSED_VARIABLE = YES;
-                               INSTALL_PATH = "$(DEVICE_PLATFORM_INSTALL_DIR)/Developer/Library/CoreSimulator/Profiles/Runtimes/$(SIMULATOR_DIR_NAME).simruntime/Contents/Resources";
                                MACOSX_DEPLOYMENT_TARGET = 10.14;
                                OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
                                OTHER_LDFLAGS = "-stdlib=libc++";
                                SDKROOT = macosx.internal;
                                SIMULATOR_DIR_NAME = "$(PLATFORM_FAMILY_NAME_$(DEVICE_PLATFORM_NAME)) $(IPHONE_SDK_MARKETING_VERSION)";
                                USE_HEADERMAP = NO;
-                               VALID_ARCHS = "x86_64 x86_64h";
                        };
                        name = Debug;
                };
                                GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
                                GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
                                GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
-                               GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
                                GCC_WARN_SHADOW = YES;
                                GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
-                               INSTALL_PATH = "$(DEVICE_PLATFORM_INSTALL_DIR)/Developer/Library/CoreSimulator/Profiles/Runtimes/$(SIMULATOR_DIR_NAME).simruntime/Contents/Resources";
                                MACOSX_DEPLOYMENT_TARGET = 10.14;
                                OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
                                OTHER_LDFLAGS = "-stdlib=libc++";
                                STRIP_INSTALLED_PRODUCT = YES;
                                STRIP_STYLE = debugging;
                                USE_HEADERMAP = NO;
-                               VALID_ARCHS = "x86_64 x86_64h";
                                VERSIONING_SYSTEM = "apple-generic";
                        };
                        name = Release;
                                MACOSX_DEPLOYMENT_TARGET = 10.13;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                ONLY_ACTIVE_ARCH = YES;
+                               OTHER_CFLAGS = "-DBUILDING_CLOSURE_UTIL=1";
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SDKROOT = macosx.internal;
                                SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
                                MACOSX_DEPLOYMENT_TARGET = 10.13;
                                MTL_ENABLE_DEBUG_INFO = NO;
+                               OTHER_CFLAGS = "-DBUILDING_CLOSURE_UTIL=1";
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SDKROOT = macosx.internal;
                                SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos";
                                GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO;
                                GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
                                GCC_WARN_MISSING_PARENTHESES = YES;
-                               GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
                                GCC_WARN_SHADOW = YES;
                                GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
                                GCC_WARN_UNUSED_FUNCTION = YES;
                                GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
                                GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
                                GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
-                               GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
                                GCC_WARN_SHADOW = YES;
                                GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
                                INSTALL_PATH = /usr/bin;
                                GCC_OPTIMIZATION_LEVEL = 0;
                                GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+                               MACOSX_DEPLOYMENT_TARGET = 10.14;
                                OTHER_CFLAGS = "-DBUILDING_SHARED_CACHE_UTIL=1";
                                PRODUCT_NAME = dyld_shared_cache_util;
                                SDKROOT = macosx.internal;
                                GCC_DYNAMIC_NO_PIC = NO;
                                GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+                               MACOSX_DEPLOYMENT_TARGET = 10.14;
                                OTHER_CFLAGS = "-DBUILDING_SHARED_CACHE_UTIL=1";
                                PRODUCT_NAME = dyld_shared_cache_util;
                                SDKROOT = macosx.internal;
                                        "$(ENTRY)",
                                        "-Wl,-fixup_chains",
                                        "-Wl,-data_const",
+                                       "-fapple-link-rtlib",
                                );
                                STRIPFLAGS = "-S";
                                SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos watchsimulator iphonesimulator appletvsimulator";
                                        "-Wl,-data_const",
                                        "-Wl,-section_order,__DATA,__all_image_info:__nl_symbol_ptr:__got:__auth_ptr:__const:__crash_info:__data:__bss:__common",
                                        "-Wl,-fixup_chains",
+                                       "-fapple-link-rtlib",
                                );
                                STRIPFLAGS = "-S";
                                SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos watchsimulator iphonesimulator appletvsimulator";
                                GCC_WARN_SHADOW = YES;
                                GCC_WARN_UNINITIALIZED_AUTOS = YES;
                                GENERATE_TEXT_BASED_STUBS = YES;
+                               HEADER_SEARCH_PATHS = ./include;
                                INSTALLHDRS_COPY_PHASE = YES;
                                INSTALLHDRS_SCRIPT_PHASE = YES;
                                IS_ZIPPERED = YES;
                                        "-umbrella",
                                        System,
                                        "-L$(SDKROOT)/usr/lib/system",
+                                       "$(EXTRA_SECTIONS)",
+                                       "-Wl,-unexported_symbol,__ZNSt3__18in_placeE",
                                );
-                               OTHER_TAPI_FLAGS = "-extra-public-header ./include/dlfcn.h -extra-private-header ./dyld3/libdyldEntryVector.h -extra-private-header ${SRCROOT}/include/mach-o/dyld_priv.h -ObjC++ -std=c++11 -umbrella System";
+                               OTHER_TAPI_FLAGS = "-extra-public-header ./include/dlfcn.h -extra-private-header ./include/dlfcn_private.h -extra-private-header ./dyld3/libdyldEntryVector.h -extra-private-header ${SRCROOT}/include/mach-o/dyld_priv.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";
                                SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos watchsimulator iphonesimulator appletvsimulator";
                                SUPPORTS_TEXT_BASED_API = YES;
                                TAPI_VERIFY_MODE = Pedantic;
+                               USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache $(DERIVED_FILE_DIR)";
                                VERSIONING_SYSTEM = "apple-generic";
                                VERSION_INFO_EXPORT_DECL = "__attribute__((visibility(\"default\")))";
                                WARNING_CFLAGS = (
                                GCC_WARN_SHADOW = YES;
                                GCC_WARN_UNINITIALIZED_AUTOS = YES;
                                GENERATE_TEXT_BASED_STUBS = YES;
+                               HEADER_SEARCH_PATHS = ./include;
                                INSTALLHDRS_COPY_PHASE = YES;
                                INSTALLHDRS_SCRIPT_PHASE = YES;
                                IS_ZIPPERED = YES;
                                        "-umbrella",
                                        System,
                                        "-L$(SDKROOT)/usr/lib/system",
+                                       "$(EXTRA_SECTIONS)",
+                                       "-Wl,-unexported_symbol,__ZNSt3__18in_placeE",
                                );
-                               OTHER_TAPI_FLAGS = "-extra-public-header ./include/dlfcn.h -extra-private-header ./dyld3/libdyldEntryVector.h -extra-private-header ${SRCROOT}/include/mach-o/dyld_priv.h -ObjC++ -std=c++11 -umbrella System";
+                               OTHER_TAPI_FLAGS = "-extra-public-header ./include/dlfcn.h -extra-private-header ./include/dlfcn_private.h -extra-private-header ./dyld3/libdyldEntryVector.h -extra-private-header ${SRCROOT}/include/mach-o/dyld_priv.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";
                                SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos watchsimulator iphonesimulator appletvsimulator";
                                SUPPORTS_TEXT_BASED_API = YES;
                                TAPI_VERIFY_MODE = Pedantic;
+                               USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache $(DERIVED_FILE_DIR)";
                                VERSIONING_SYSTEM = "apple-generic";
                                VERSION_INFO_EXPORT_DECL = "__attribute__((visibility(\"default\")))";
                                WARNING_CFLAGS = (
                                GCC_WARN_UNUSED_VARIABLE = YES;
                                HEADER_SEARCH_PATHS = ./include;
                                SDKROOT = macosx.internal;
+                               STRIP_INSTALLED_PRODUCT = YES;
                                USER_HEADER_SEARCH_PATHS = "./dyld3 ./dyld3/shared-cache";
                                WARNING_CFLAGS = "-Wimplicit-fallthrough";
                        };
                                GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
                                GCC_MODEL_TUNING = G5;
                                GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_LIBDSC=1";
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/lib";
                                OTHER_CPLUSPLUSFLAGS = (
                                GCC_ENABLE_OBJC_EXCEPTIONS = NO;
                                GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
                                GCC_MODEL_TUNING = G5;
+                               GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_LIBDSC=1";
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
                                GCC_WARN_ABOUT_RETURN_TYPE = YES;
                        buildSettings = {
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos watchsimulator iphonesimulator appletvsimulator";
-                               SYSTEM_HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders";
+                               SYSTEM_HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders ${SDKROOT)/usr/local/include";
                                USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/dyld3 $(SRCROOT)/shared-cache $(SRCROOT)/include $(SRCROOT)/testing/include";
                        };
                        name = Debug;
                        buildSettings = {
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                SUPPORTED_PLATFORMS = "macosx iphoneos watchos appletvos bridgeos watchsimulator iphonesimulator appletvsimulator";
-                               SYSTEM_HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders";
+                               SYSTEM_HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders ${SDKROOT)/usr/local/include";
                                USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/dyld3 $(SRCROOT)/shared-cache $(SRCROOT)/include $(SRCROOT)/testing/include";
                        };
                        name = Release;
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
+               C14965E422BDCF6900568D15 /* Build configuration list for PBXNativeTarget "dyld_app_cache_util" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               C14965E522BDCF6900568D15 /* Debug */,
+                               C14965E622BDCF6900568D15 /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               C14C3564230531830059E04C /* Build configuration list for PBXNativeTarget "run-static" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               C14C3565230531830059E04C /* Debug */,
+                               C14C3566230531830059E04C /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
                C187B9071FE063A40042D3B7 /* Build configuration list for PBXNativeTarget "libslc_builder.dylib" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
+               C1BDD43F234E8FA00095C7DC /* Build configuration list for PBXAggregateTarget "dyld_executables" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               C1BDD43D234E8FA00095C7DC /* Debug */,
+                               C1BDD43E234E8FA00095C7DC /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               C1BF4DB12359254500B0F1AE /* Build configuration list for PBXNativeTarget "libKernelCollectionBuilder" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               C1BF4DB22359254500B0F1AE /* Debug */,
+                               C1BF4DB32359254500B0F1AE /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
                F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
-               F9556D3F20C1F896004DF62A /* Build configuration list for PBXNativeTarget "dyldinfo" */ = {
+               F9556D3F20C1F896004DF62A /* Build configuration list for PBXNativeTarget "dyld_info" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
                                F9556D3D20C1F896004DF62A /* Debug */,
index 8ed4a6dfb7771bc65e020ca7630630a55f8808c6..cfec4dc5532c515550a0d8a181e0a42f7bdfa183 100644 (file)
@@ -54,6 +54,8 @@
 #include "ClosureBuilder.h"
 #include "ClosureFileSystemPhysical.h"
 
+#include <dyld/VersionMap.h>
+
 #if __has_feature(ptrauth_calls)
 #include <ptrauth.h>
 #endif
@@ -78,10 +80,6 @@ static const void *stripPointer(const void *ptr) {
 
 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");
@@ -122,6 +120,11 @@ const char* _dyld_get_image_name(uint32_t imageIndex)
     return gAllImages.imagePathByIndex(imageIndex);
 }
 
+const struct mach_header * _dyld_get_prog_image_header()
+{
+    log_apis("_dyld_get_prog_image_header()\n");
+    return gAllImages.mainExecutable();
+}
 
 static bool nameMatch(const char* installName, const char* libraryName)
 {
@@ -305,7 +308,18 @@ uint32_t dyld_get_sdk_version(const mach_header* mh)
 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 result = dyld3::dyld_get_sdk_version(gAllImages.mainExecutable());
+#if TARGET_OS_OSX
+    // HACK: We didn't have time to fix all the zippered clients in the spring releases, so keep the mapping. We have resolved it for all new clients using the platform aware SPIs. Since we are doing to deprecate this SPI we will leave the hack in.
+    if (dyld_get_active_platform() == (dyld_platform_t)dyld3::Platform::iOSMac) {
+        if (result >= 0x000D0400) {
+            result = 0x000A0F04;
+        } else {
+            result = 0x000A0F00;
+        }
+    }
+#endif
+    return result;
 }
 
 uint32_t dyld_get_min_os_version(const mach_header* mh)
@@ -335,7 +349,7 @@ dyld_platform_t dyld_get_active_platform(void) {
 
 dyld_platform_t dyld_get_base_platform(dyld_platform_t platform) {
     switch (platform) {
-        case PLATFORM_IOSMAC:               return PLATFORM_IOS;
+        case PLATFORM_MACCATALYST:               return PLATFORM_IOS;
         case PLATFORM_IOSSIMULATOR:         return PLATFORM_IOS;
         case PLATFORM_WATCHOSSIMULATOR:     return PLATFORM_WATCHOS;
         case PLATFORM_TVOSSIMULATOR:        return PLATFORM_TVOS;
@@ -354,8 +368,24 @@ bool dyld_is_simulator_platform(dyld_platform_t platform) {
     }
 }
 
+static
+dyld_build_version_t mapFromVersionSet(dyld_build_version_t version) {
+    if (version.platform != 0xffffffff) return version;
+    auto i = std::lower_bound(sVersionMap.begin(), sVersionMap.end(), version.version);
+    assert(i != sVersionMap.end());
+    switch(dyld3::dyld_get_base_platform(::dyld_get_active_platform())) {
+        case PLATFORM_MACOS: return { .platform = PLATFORM_MACOS, .version = i->macos };
+        case PLATFORM_IOS: return { .platform = PLATFORM_IOS, .version = i->ios };
+        case PLATFORM_WATCHOS: return { .platform = PLATFORM_WATCHOS, .version = i->watchos };
+        case PLATFORM_TVOS: return { .platform = PLATFORM_TVOS, .version = i->tvos };
+        case PLATFORM_BRIDGEOS: return { .platform = PLATFORM_BRIDGEOS, .version = i->bridgeos };
+        default: return { .platform = 0, .version = 0 };
+    }
+}
+
 bool dyld_sdk_at_least(const struct mach_header* mh, dyld_build_version_t version) {
     __block bool retval = false;
+    version = mapFromVersionSet(version);
     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;
@@ -366,6 +396,7 @@ bool dyld_sdk_at_least(const struct mach_header* mh, dyld_build_version_t versio
 
 bool dyld_minos_at_least(const struct mach_header* mh, dyld_build_version_t version) {
     __block bool retval = false;
+    version = mapFromVersionSet(version);
     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;
@@ -374,15 +405,7 @@ bool dyld_minos_at_least(const struct mach_header* mh, dyld_build_version_t vers
     return retval;
 }
 
-bool dyld_program_sdk_at_least(dyld_build_version_t version) {
-    return dyld3::dyld_sdk_at_least(gAllImages.mainExecutable(), version);
-}
-
-bool dyld_program_minos_at_least(dyld_build_version_t version) {
-    return dyld3::dyld_minos_at_least(gAllImages.mainExecutable(), version);
-}
-
-#if TARGET_OS_OSX || TARGET_OS_IOS
+#if TARGET_OS_OSX
 static
 uint32_t linkedDylibVersion(const mach_header* mh, const char *installname) {
     __block uint32_t retval = 0;
@@ -400,17 +423,18 @@ uint32_t linkedDylibVersion(const mach_header* mh, const char *installname) {
 #define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff))
 
 static uint32_t deriveVersionFromDylibs(const struct mach_header* mh) {
+#if TARGET_OS_IOS
+    // 7.0 is the last version that was in iOSes mapping table, and it is the earliest version that support 64 bit binarie.
+    // Since we dropped 32 bit support, we know any binary with a version must be from 7.0
+    return 0x00070000;
+#elif TARGET_OS_OSX
     // This is a binary without a version load command, we need to infer things
     struct DylibToOSMapping {
         uint32_t dylibVersion;
         uint32_t osVersion;
     };
-    uint32_t linkedVersion = 0;
-#if TARGET_OS_OSX
-    linkedVersion = linkedDylibVersion(mh, "/usr/lib/libSystem.B.dylib");
+    uint32_t 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 },
         { PACKED_VERSION(159,0,0),  0x000A0700 },
         { PACKED_VERSION(169,3,0),  0x000A0800 },
@@ -419,31 +443,6 @@ static uint32_t deriveVersionFromDylibs(const struct mach_header* mh) {
         // We don't need to expand this table because all recent
         // binaries have LC_VERSION_MIN_ load command.
     };
-#elif TARGET_OS_IOS
-        linkedVersion = linkedDylibVersion(mh, "/System/Library/Frameworks/Foundation.framework/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=versionMapping; ; ++p) {
@@ -456,6 +455,7 @@ static uint32_t deriveVersionFromDylibs(const struct mach_header* mh) {
             lastOsVersion = p->osVersion;
         }
     }
+#endif
     return 0;
 }
 
@@ -470,14 +470,6 @@ static void dyld_get_image_versions_internal(const struct mach_header* mh, void
         if (sdk == 0) {
             sdk = deriveVersionFromDylibs(mh);
         }
-        // HACK: We don't have time to fix all the zippered clients in the spring releases, so keep the mapping
-        if (platform == dyld3::Platform::iOSMac) {
-            if (sdk >= 0x000D0400) {
-                sdk = 0x000A0F04;
-            } else {
-                sdk = 0x000A0F00;
-            }
-        }
         callback((const dyld_platform_t)platform, sdk, minOS);
     });
 
@@ -553,6 +545,120 @@ void dyld_get_image_versions(const struct mach_header* mh, void (^callback)(dyld
         dyld_get_image_versions_internal(mh, callback);
 }
 
+struct VIS_HIDDEN VersionSPIDispatcher {
+    static bool dyld_program_minos_at_least(dyld_build_version_t version) {
+        return dyld_program_minos_at_least_active(version);
+    }
+    static bool dyld_program_sdk_at_least(dyld_build_version_t version) {
+        return dyld_program_sdk_at_least_active(version);
+    }
+private:
+    // We put these into a struct to guarantee so we can control the placement to guarantee a version and the set equivalent
+    // Can be loaded via a single load pair instruction.
+    struct FastPathData {
+        uint32_t version;
+        uint32_t versionSetEquivalent;
+        dyld_platform_t platform;
+    };
+    static uint32_t findVersionSetEquuivalent(uint32_t version) {
+        uint32_t candidateVersion = 0;
+        uint32_t candidateVersionEquivalent = 0;
+        uint32_t newVersionSetVersion = 0;
+        for (const auto& i : sVersionMap) {
+            switch (dyld_get_base_platform(::dyld_get_active_platform())) {
+                case PLATFORM_MACOS:    newVersionSetVersion = i.macos; break;
+                case PLATFORM_IOS:      newVersionSetVersion = i.ios; break;
+                case PLATFORM_WATCHOS:  newVersionSetVersion = i.watchos; break;
+                case PLATFORM_TVOS:     newVersionSetVersion = i.tvos; break;
+                case PLATFORM_BRIDGEOS: newVersionSetVersion = i.bridgeos; break;
+                default: newVersionSetVersion = 0xffffffff; // If we do not know about the platform it is newer than everything
+            }
+            if (newVersionSetVersion > version) { break; }
+            candidateVersion = newVersionSetVersion;
+            candidateVersionEquivalent = i.set;
+        }
+        return candidateVersionEquivalent;
+    };
+
+    static void setVersionMappingFastPathData(const struct mach_header* mh) {
+        dyld3::dyld_get_image_versions(mh, ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
+            minosFastPathData.platform = dyld_get_base_platform(::dyld_get_active_platform());
+            sdkFastPathData.platform = dyld_get_base_platform(::dyld_get_active_platform());
+            minosFastPathData.version = min_version;
+            minosFastPathData.versionSetEquivalent = findVersionSetEquuivalent(min_version);
+            sdkFastPathData.version = sdk_version;
+            sdkFastPathData.versionSetEquivalent = findVersionSetEquuivalent(sdk_version);
+        });
+    }
+
+    static void setupFastPath(void) {
+        setVersionMappingFastPathData((const struct mach_header*)_NSGetMachExecuteHeader());
+        dyld_program_minos_at_least_active = &dyld_program_minos_at_least_fast;
+        dyld_program_sdk_at_least_active = &dyld_program_sdk_at_least_fast;
+    }
+
+    static bool dyld_program_minos_at_least_slow (dyld_build_version_t version) {
+        setupFastPath();
+        return dyld_program_minos_at_least_fast(version);
+    }
+
+    static bool dyld_program_sdk_at_least_slow (dyld_build_version_t version) {
+        setupFastPath();
+        return dyld_program_sdk_at_least_fast(version);
+    }
+
+    // Fast path implementation of version checks for main executables
+    // This works by using the fact that are essentially 3 cases we care about:
+    // 1. Comparing the exctuable against any other platform (which should always return false)
+    // 2. Comparing the exctuable against a version set (platform 0xfffffff)
+    // 3. Comparing the exctuable againstt our base platform
+    //
+    // We achieve this by setting up a single compare (currentVersion >= version.version) and a couple of
+    // of simple tests that will all compile to conditional moves to setup that compare:
+    // 1. We setup the comapreVersion as 0. It will only keep that value if it is not a version set and it
+    //    it is not the platform we are testing against. 0 will be less than the value encoded in any well
+    //    formed binary, so the test will end up returning false
+    // 2. If the platform is 0xffffffff it is a version set. In the fast path setup we we calculated a value
+    //    that allows a direct comparison, so we set comapreVersion to that (versionSetEquivalent)
+    // 3. If it is a concrete platform and it matches the current platform running then we can set comapreVersion
+    //    to the actual version number that the was embedded in the binary, which is we stashed in the fast
+    //    path data
+
+    static bool dyld_program_minos_at_least_fast (dyld_build_version_t version) {
+        uint32_t currentVersion = 0;
+        if (version.platform == 0xffffffff) { currentVersion = minosFastPathData.versionSetEquivalent; }
+        if (version.platform == minosFastPathData.platform) { currentVersion = minosFastPathData.version; }
+        return (currentVersion >= version.version);
+    }
+
+    static bool dyld_program_sdk_at_least_fast (dyld_build_version_t version) {
+        uint32_t currentVersion = 0;
+        if (version.platform == 0xffffffff) { currentVersion = sdkFastPathData.versionSetEquivalent ; }
+        if (version.platform == sdkFastPathData.platform) { currentVersion = sdkFastPathData.version; }
+        return (currentVersion >= version.version);
+    }
+
+    static bool (*dyld_program_minos_at_least_active)(dyld_build_version_t version);
+    static bool (*dyld_program_sdk_at_least_active)(dyld_build_version_t version);
+    static FastPathData minosFastPathData;
+    static FastPathData sdkFastPathData;
+};
+
+bool (*VersionSPIDispatcher::dyld_program_minos_at_least_active)(dyld_build_version_t version) = &VersionSPIDispatcher::dyld_program_minos_at_least_slow;
+bool (*VersionSPIDispatcher::dyld_program_sdk_at_least_active)(dyld_build_version_t version) = &VersionSPIDispatcher::dyld_program_sdk_at_least_slow;
+VersionSPIDispatcher::FastPathData VersionSPIDispatcher::minosFastPathData = {0, 0, 0};
+VersionSPIDispatcher::FastPathData VersionSPIDispatcher::sdkFastPathData = {0, 0, 0};
+
+
+// We handle this directly instead of dispatching through dyld3::dyld_program_sdk_at_least because they are very perf sensitive
+bool dyld_program_minos_at_least(dyld_build_version_t version) {
+    return VersionSPIDispatcher::dyld_program_minos_at_least(version);
+}
+
+bool dyld_program_sdk_at_least(dyld_build_version_t version) {
+    return VersionSPIDispatcher::dyld_program_sdk_at_least(version);
+}
+
 uint32_t dyld_get_program_min_os_version()
 {
     log_apis("dyld_get_program_min_os_version()\n");
@@ -852,7 +958,8 @@ void* dlopen_internal(const char* path, int mode, void* callerAddress)
     else
         leafName = path;
 
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
+
+#if TARGET_OS_IPHONE
     // <rdar://problem/40235395> dyld3: dlopen() not working with non-canonical paths
     char canonicalPath[PATH_MAX];
     if ( leafName != path ) {
@@ -913,8 +1020,8 @@ bool dlopen_preflight_internal(const char* path)
     DYLD_LOAD_LOCK_THIS_BLOCK
     log_apis("dlopen_preflight(%s)\n", path);
 
-    // check if path is in dyld shared cache
-    if ( gAllImages.dyldCacheHasPath(path) )
+    // check if path is in dyld shared cache, or is a symlink to the cache
+    if ( _dyld_shared_cache_contains_path(path) )
         return true;
 
     // check if file is loadable
@@ -927,8 +1034,6 @@ bool dlopen_preflight_internal(const char* path)
         return true;
     }
 
-    // FIXME: may be symlink to something in dyld cache
-
     return false;
 }
 
@@ -1136,6 +1241,12 @@ bool _dyld_shared_cache_is_locally_built()
     return false;
 }
 
+uint32_t _dyld_launch_mode()
+{
+    return gAllImages.launchMode();
+}
+
+
 void _dyld_images_for_addresses(unsigned count, const void* addresses[], dyld_image_uuid_offset infos[])
 {
     log_apis("_dyld_images_for_addresses(%u, %p, %p)\n", count, addresses, infos);
@@ -1237,13 +1348,13 @@ void dyld_dynamic_interpose(const mach_header* mh, const dyld_interpose_tuple ar
 static void* mapStartOfCache(const char* path, size_t length)
 {
     struct stat statbuf;
-    if ( ::stat(path, &statbuf) == -1 )
+    if ( dyld3::stat(path, &statbuf) == -1 )
         return NULL;
 
     if ( statbuf.st_size < length )
         return NULL;
 
-    int cache_fd = ::open(path, O_RDONLY);
+    int cache_fd = dyld3::open(path, O_RDONLY, 0);
     if ( cache_fd < 0 )
         return NULL;
 
@@ -1277,7 +1388,7 @@ static const DyldSharedCache* findCacheInDirAndMap(const uuid_t cacheUuid, const
             if ( const DyldSharedCache* cache = (DyldSharedCache*)mapStartOfCache(cachePath, 0x00100000) ) {
                 uuid_t foundUuid;
                 cache->getUUID(foundUuid);
-                if ( ::memcmp(foundUuid, cacheUuid, 16) != 0 ) {
+                if ( (::memcmp(cache, "dyld_", 5) != 0) || (::memcmp(foundUuid, cacheUuid, 16) != 0) ) {
                     // wrong uuid, unmap and keep looking
                     ::munmap((void*)cache, 0x00100000);
                 }
@@ -1309,12 +1420,11 @@ int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extr
     }
     if ( sharedCache == nullptr ) {
          // if not, look in default location for cache files
-    #if    __IPHONE_OS_VERSION_MIN_REQUIRED
-        const char* defaultSearchDir = IPHONE_DYLD_SHARED_CACHE_DIR;
+    #if TARGET_OS_IPHONE
+        sharedCache = findCacheInDirAndMap(cacheUuid, IPHONE_DYLD_SHARED_CACHE_DIR, sizeMapped);
     #else
-        const char* defaultSearchDir = MACOSX_DYLD_SHARED_CACHE_DIR;
-    #endif
-        sharedCache = findCacheInDirAndMap(cacheUuid, defaultSearchDir, sizeMapped);
+        sharedCache = findCacheInDirAndMap(cacheUuid, MACOSX_MRM_DYLD_SHARED_CACHE_DIR, sizeMapped);
+   #endif
         // if not there, look in extra search locations
         if ( sharedCache == nullptr ) {
             for (const char** p = extraSearchDirs; *p != nullptr; ++p) {
@@ -1329,7 +1439,8 @@ int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extr
 
     // get base address of cache
     __block uint64_t cacheUnslidBaseAddress = 0;
-    sharedCache->forEachRegion(^(const void *content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+    sharedCache->forEachRegion(^(const void *content, uint64_t vmAddr, uint64_t size, uint32_t permissions,
+                                 uint64_t flags) {
         if ( cacheUnslidBaseAddress == 0 )
             cacheUnslidBaseAddress = vmAddr;
     });
@@ -1360,9 +1471,9 @@ int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(cons
     return dyld3::dyld_shared_cache_find_iterate_text(cacheUuid, extraSearchDirs, callback);
 }
     
-bool dyld_need_closure(const char* execPath, const char* tempDir)
+bool dyld_need_closure(const char* execPath, const char* dataContainerRootDir)
 {
-    log_apis("dyld_need_closure()\n");
+    log_apis("dyld_need_closure(%s)\n", execPath);
 
     // We don't need to build a closure if the shared cache has it already
     const DyldSharedCache* sharedCache = (DyldSharedCache*)gAllImages.cacheLoadAddress();
@@ -1371,11 +1482,29 @@ bool dyld_need_closure(const char* execPath, const char* tempDir)
             return false;
     }
 
+    // this SPI changed. Originally the second path was to $TMPDIR, now it is $HOME
+    // if called old way, adjust
+    size_t rootDirLen = strlen(dataContainerRootDir);
+    char homeFromTmp[PATH_MAX];
+    if ( (rootDirLen > 5) && (strcmp(&dataContainerRootDir[rootDirLen-4], "/tmp") == 0) && (rootDirLen < PATH_MAX) ) {
+        strlcpy(homeFromTmp, dataContainerRootDir, PATH_MAX);
+        homeFromTmp[rootDirLen-4] = '\0';
+        dataContainerRootDir = homeFromTmp;
+    }
+
+    // dummy up envp needed by buildClosureCachePath()
+    char strBuf[PATH_MAX+8]; // room for HOME= and max path
+    strcpy(strBuf, "HOME=");
+    strlcat(strBuf, dataContainerRootDir, sizeof(strBuf));
+    const char* envp[2];
+    envp[0] = strBuf;
+    envp[1] = nullptr;
 
     char closurePath[PATH_MAX];
-    if ( dyld3::closure::LaunchClosure::buildClosureCachePath(execPath, closurePath, tempDir, false) ) {
+    if ( dyld3::closure::LaunchClosure::buildClosureCachePath(execPath, envp, false, closurePath) ) {
         struct stat statbuf;
-        return (::stat(closurePath, &statbuf) != 0);
+        // if no file at location where closure would be stored, then need to build a closure
+        return (dyld3::stat(closurePath, &statbuf) != 0);
     }
 
     // Not containerized so no point in building a closure.
@@ -1416,6 +1545,12 @@ void _dyld_for_each_objc_protocol(const char* protocolName,
     gAllImages.forEachObjCProtocol(protocolName, callback);
 }
 
+void _dyld_register_driverkit_main(void (*mainFunc)())
+{
+    log_apis("_dyld_register_driverkit_main()\n");
+    gAllImages.setDriverkitMain(mainFunc);
+}
+
 #if !TARGET_OS_DRIVERKIT
 struct dyld_func {
     const char*  name;
@@ -1430,6 +1565,10 @@ static const struct dyld_func dyld_funcs[] = {
     {"__dyld_get_image_name",           (void*)dyld3::_dyld_get_image_name },
     {"__dyld_get_image_header",         (void*)dyld3::_dyld_get_image_header },
     {"__dyld_get_image_vmaddr_slide",   (void*)dyld3::_dyld_get_image_vmaddr_slide },
+#if TARGET_OS_OSX
+    // <rdar://problem/59265987> support old licenseware plug ins on macOS
+    {"__dyld_lookup_and_bind",          (void*)dyld3::_dyld_lookup_and_bind },
+#endif
 };
 #endif
 
index 59b7f2822308bbe3650026abed2c545309882db5..c6ccca8d97befd6a767b15a3dc156db8f4d671fe 100644 (file)
@@ -82,6 +82,8 @@ intptr_t _dyld_get_image_vmaddr_slide(uint32_t imageIndex) TEMP_HIDDEN;
 
 const char* _dyld_get_image_name(uint32_t imageIndex) TEMP_HIDDEN;
 
+const struct mach_header * _dyld_get_prog_image_header() TEMP_HIDDEN;
+
 int32_t NSVersionOfLinkTimeLibrary(const char* libraryName) TEMP_HIDDEN;
 
 int32_t NSVersionOfRunTimeLibrary(const char* libraryName) TEMP_HIDDEN;
@@ -157,7 +159,9 @@ bool _dyld_shared_cache_optimized() TEMP_HIDDEN;
 
 bool _dyld_shared_cache_is_locally_built() TEMP_HIDDEN;
     
-bool dyld_need_closure(const char* execPath, const char* tempDir) TEMP_HIDDEN;
+uint32_t _dyld_launch_mode() TEMP_HIDDEN;
+
+bool dyld_need_closure(const char* execPath, const char* dataContainerRootDir) TEMP_HIDDEN;
 
 void _dyld_images_for_addresses(unsigned count, const void* addresses[], struct dyld_image_uuid_offset infos[]) TEMP_HIDDEN;
 
@@ -193,8 +197,10 @@ void _dyld_for_each_objc_class(const char* className,
 void _dyld_for_each_objc_protocol(const char* protocolName,
                                   void (^callback)(void* protocolPtr, bool isLoaded, bool* stop)) TEMP_HIDDEN;
 
+void _dyld_register_driverkit_main(void (*mainFunc)())TEMP_HIDDEN;
+
 // only in macOS and deprecated 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
 NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage) TEMP_HIDDEN;
 NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void *address, size_t size, NSObjectFileImage *objectFileImage) TEMP_HIDDEN;
 bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) TEMP_HIDDEN;
index ad87d47485d861652aacee8a4bc21e3f20412856..6a24ca32881774f146b031031ad4c7c671280de8 100644 (file)
@@ -55,7 +55,7 @@ void                    parseDlHandle(void* h, const MachOLoaded** mh, bool* don
 
 
 // only in macOS and deprecated 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
 
 // macOS needs to support an old API that only works with fileype==MH_BUNDLE.
 // In this deprecated API (unlike dlopen), loading and linking are separate steps.
@@ -70,7 +70,7 @@ NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* path, NS
 
     // verify path exists
      struct stat statbuf;
-    if ( ::stat(path, &statbuf) == -1 )
+    if ( dyld3::stat(path, &statbuf) == -1 )
         return NSObjectFileImageFailure;
 
     // create ofi that just contains path. NSLinkModule does all the work
@@ -98,13 +98,13 @@ NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void* memIma
     bool usable = false;
     const MachOFile* mf = (MachOFile*)memImage;
     if ( mf->hasMachOMagic() && mf->isMachO(diag, memImageSize) ) {
-        usable = (gAllImages.archs().grade(mf->cputype, mf->cpusubtype) != 0);
+        usable = (gAllImages.archs().grade(mf->cputype, mf->cpusubtype, false) != 0);
     }
     else if ( const FatFile* ff = FatFile::isFatFile(memImage) ) {
         uint64_t sliceOffset;
         uint64_t sliceLen;
         bool     missingSlice;
-        if ( ff->isFatFileWithSlice(diag, memImageSize, gAllImages.archs(), sliceOffset, sliceLen, missingSlice) ) {
+        if ( ff->isFatFileWithSlice(diag, memImageSize, gAllImages.archs(), false, sliceOffset, sliceLen, missingSlice) ) {
             mf = (MachOFile*)((long)memImage+sliceOffset);
             if ( mf->isMachO(diag, sliceLen) ) {
                 usable = true;
@@ -112,7 +112,7 @@ NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void* memIma
         }
     }
     if ( usable ) {
-        if ( !mf->supportsPlatform(Platform::macOS) )
+        if ( !mf->builtForPlatform(Platform::macOS) )
             usable = false;
     }
     if ( !usable ) {
@@ -343,10 +343,15 @@ const char* NSLibraryNameForModule(NSModule m)
 
 static bool flatFindSymbol(const char* symbolName, void** symbolAddress, const mach_header** foundInImageAtLoadAddress)
 {
+    // <rdar://problem/59265987> allow flat lookup to find "_memcpy" even though it is not implemented as that name in any dylib
+    MachOLoaded::DependentToMachOLoaded finder = ^(const MachOLoaded* mh, uint32_t depIndex) {
+        return gAllImages.findDependent(mh, depIndex);
+    };
+
     __block bool result = false;
     gAllImages.forEachImage(^(const LoadedImage& loadedImage, bool& stop) {
         bool resultPointsToInstructions = false;
-        if ( loadedImage.loadedAddress()->hasExportedSymbol(symbolName, nullptr, symbolAddress, &resultPointsToInstructions) ) {
+        if ( loadedImage.loadedAddress()->hasExportedSymbol(symbolName, finder, symbolAddress, &resultPointsToInstructions) ) {
             *foundInImageAtLoadAddress = loadedImage.loadedAddress();
             stop = true;
             result = true;
index 1126fbb64b848015b23e98a812aa548435cf6eeb..8eca05962f1de19a0867c0706eb1f422b7e80aaa 100644 (file)
@@ -31,6 +31,7 @@
 #include <libkern/OSAtomic.h>
 #include <uuid/uuid.h>
 #include <mach-o/dyld_images.h>
+#include <libc_private.h>
 
 #include <vector>
 #include <algorithm>
@@ -45,6 +46,7 @@
 #include "Closure.h"
 #include "ClosureBuilder.h"
 #include "ClosureFileSystemPhysical.h"
+#include "RootsChecker.h"
 
 #include "objc-shared-cache.h"
 
@@ -84,8 +86,7 @@ void AllImages::init(const closure::LaunchClosure* closure, const DyldSharedCach
     _dyldCachePath      = dyldCachePath;
 
     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;
+        _dyldCacheSlide = (uint64_t)dyldCacheLoadAddress - dyldCacheLoadAddress->unslidLoadAddress();
         _imagesArrays.push_back(dyldCacheLoadAddress->cachedDylibsImageArray());
         if ( auto others = dyldCacheLoadAddress->otherOSImageArray() )
             _imagesArrays.push_back(others);
@@ -113,11 +114,25 @@ void AllImages::init(const closure::LaunchClosure* closure, const DyldSharedCach
     _processDOFs = Loader::dtraceUserProbesEnabled();
 }
 
-void AllImages::setProgramVars(ProgramVars* vars)
+void AllImages::setProgramVars(ProgramVars* vars, bool keysOff, bool osBinariesOnly)
 {
     _programVars = vars;
-    const dyld3::MachOFile* mf = (dyld3::MachOFile*)_programVars->mh;
-    _archs = &GradedArchs::forCurrentOS(mf);
+    _archs = &GradedArchs::forCurrentOS(keysOff, osBinariesOnly);
+}
+
+void AllImages::setLaunchMode(uint32_t flags)
+{
+    _launchMode = flags;
+}
+
+AllImages::MainFunc AllImages::getDriverkitMain()
+{
+    return _driverkitMain;
+}
+
+void AllImages::setDriverkitMain(MainFunc mainFunc)
+{
+    _driverkitMain = mainFunc;
 }
 
 void AllImages::setRestrictions(bool allowAtPaths, bool allowEnvPaths)
@@ -218,7 +233,7 @@ void AllImages::mirrorToOldAllImageInfos()
         // <radr://problem/42668846> update UUID array if needed
         uint32_t nonCachedCount = 1; // always add dyld
         for (const LoadedImage& li : _loadedImages) {
-            if ( !li.loadedAddress()->inDyldCache() )
+            if ( _oldAllImageInfos->processDetachedFromSharedRegion || !li.loadedAddress()->inDyldCache())
                 ++nonCachedCount;
         }
         if ( nonCachedCount != _oldAllImageInfos->uuidArrayCount ) {
@@ -239,7 +254,7 @@ void AllImages::mirrorToOldAllImageInfos()
             dyldMF->getUuid(_oldUUIDArray[0].imageUUID);
             index = 1;
             for (const LoadedImage& li : _loadedImages) {
-                if ( !li.loadedAddress()->inDyldCache() ) {
+                if ( _oldAllImageInfos->processDetachedFromSharedRegion || !li.loadedAddress()->inDyldCache() ) {
                     _oldUUIDArray[index].imageLoadAddress = li.loadedAddress();
                     li.loadedAddress()->getUuid(_oldUUIDArray[index].imageUUID);
                     ++index;
@@ -257,13 +272,6 @@ void AllImages::addImages(const Array<LoadedImage>& newImages)
     // 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;
-            }
-        }
     });
 }
 
@@ -312,6 +320,13 @@ void AllImages::runImageNotifiers(const Array<LoadedImage>& newImages)
         _oldAllImageInfos->notification(dyld_image_adding, count, oldDyldInfo);
     }
 
+    // if any image not in the shared cache added, recompute bounds
+    for (const LoadedImage& li : newImages) {
+        if ( !((MachOAnalyzer*)li.loadedAddress())->inDyldCache() ) {
+            recomputeBounds();
+            break;
+        }
+    }
 
     // update immutable ranges
     for (const LoadedImage& li : newImages) {
@@ -319,7 +334,7 @@ void AllImages::runImageNotifiers(const Array<LoadedImage>& newImages)
             uintptr_t baseAddr = (uintptr_t)li.loadedAddress();
             li.image()->forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool laterReadOnly, bool &stop) {
                 if ( (permissions & (VM_PROT_READ|VM_PROT_WRITE)) == VM_PROT_READ ) {
-                    addImmutableRange(baseAddr + vmOffset, baseAddr + vmOffset + vmSize);
+                    addImmutableRange(baseAddr + (uintptr_t)vmOffset, (uintptr_t)(baseAddr + vmOffset + vmSize));
                 }
             });
         }
@@ -349,7 +364,7 @@ void AllImages::runImageNotifiers(const Array<LoadedImage>& newImages)
             image->getUuid(uuid);
             fsid_t       fsid = {{ 0, 0 }};
             fsobj_id_t   fsobjid = { 0, 0 };
-            if ( !li.loadedAddress()->inDyldCache() && (stat(path, &stat_buf) == 0) ) {
+            if ( !li.loadedAddress()->inDyldCache() && (dyld3::stat(path, &stat_buf) == 0) ) {
                 fsobjid = *(fsobj_id_t*)&stat_buf.st_ino;
                 fsid    = {{ stat_buf.st_dev, 0 }};
             }
@@ -466,7 +481,7 @@ void AllImages::removeImages(const Array<LoadedImage>& unloadImages)
             image->getUuid(uuid);
             fsid_t       fsid = {{ 0, 0 }};
             fsobj_id_t   fsobjid = { 0, 0 };
-            if ( stat(path, &stat_buf) == 0 ) {
+            if ( dyld3::stat(path, &stat_buf) == 0 ) {
                 fsobjid = *(fsobj_id_t*)&stat_buf.st_ino;
                 fsid    = {{ stat_buf.st_dev, 0 }};
             }
@@ -919,6 +934,12 @@ void AllImages::breadthFirstRecurseDependents(Array<closure::ImageNum>& visited,
         if ( !findImageNum(depImageNum, depLi) )
             return;
         handler(depLi, depStop);
+        // <rdar://58466613> if there is an override of some dyld cache dylib, we need to store the override ImageNum in the visited set
+        if ( depImageNum != depLi.image()->imageNum() ) {
+            depImageNum = depLi.image()->imageNum();
+            if ( visited.contains(depImageNum) )
+                return;
+        }
         visited.push_back(depImageNum);
         if ( depStop ) {
             stopped = true;
@@ -956,7 +977,7 @@ const MachOLoaded* AllImages::mainExecutable() const
 const closure::Image* AllImages::mainExecutableImage() const
 {
     assert(_mainClosure != nullptr);
-    return _mainClosure->images()->imageForNum(_mainClosure->topImage());
+    return _mainClosure->images()->imageForNum(_mainClosure->topImageNum());
 }
 
 void AllImages::setMainPath(const char* path )
@@ -966,7 +987,7 @@ void AllImages::setMainPath(const char* path )
 
 const char* AllImages::imagePath(const closure::Image* image) const
 {
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
+#if TARGET_OS_IPHONE
     // on iOS and watchOS, apps may be moved on device after closure built
        if ( _mainExeOverridePath != nullptr ) {
         if ( image == mainExecutableImage() )
@@ -977,14 +998,7 @@ const char* AllImages::imagePath(const closure::Image* image) const
 }
 
 dyld_platform_t AllImages::platform() const {
-    if (oldAllImageInfo()->version >= 16) { return (dyld_platform_t)oldAllImageInfo()->platform; }
-
-    __block dyld_platform_t result;
-    // FIXME: Remove this once we only care about version 16 or greater all image infos
-    dyld_get_image_versions(mainExecutable(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
-        result = platform;
-    });
-    return result;
+    return (dyld_platform_t)oldAllImageInfo()->platform;
 }
 
 const GradedArchs& AllImages::archs() const
@@ -1026,7 +1040,7 @@ void AllImages::decRefCount(const mach_header* loadAddress)
 }
 
 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
 NSObjectFileImage AllImages::addNSObjectFileImage(const OFIInfo& image)
 {
     __block uint64_t imageNum = 0;
@@ -1488,7 +1502,9 @@ void AllImages::applyInterposingToDyldCache(const closure::Closure* closure)
         LoadedImage foundImage;
         switch ( entry.replacement.image.kind ) {
             case closure::Image::ResolvedSymbolTarget::kindImage:
-                assert(findImageNum(entry.replacement.image.imageNum, foundImage));
+                if ( !findImageNum(entry.replacement.image.imageNum, foundImage) ) {
+                    abort_report_np("cannot find replacement imageNum=0x%04X when patching cache to override imageNum=0x%04X\n", entry.replacement.image.imageNum, entry.overriddenDylibInCache);
+                }
                 newValue = (uintptr_t)(foundImage.loadedAddress()) + (uintptr_t)entry.replacement.image.offset;
                 break;
             case closure::Image::ResolvedSymbolTarget::kindSharedCache:
@@ -1661,9 +1677,14 @@ void AllImages::runAllInitializersInImage(const closure::Image* image, const Mac
     });
 }
 
-const MachOLoaded* AllImages::dlopen(Diagnostics& diag, const char* path, bool rtldNoLoad, bool rtldLocal, bool rtldNoDelete, bool rtldNow, bool fromOFI, const void* callerAddress)
+// Note this is noinline to avoid having too much stack used if loadImage has to call due to an invalid closure
+__attribute__((noinline))
+const MachOLoaded* AllImages::dlopen(Diagnostics& diag, const char* path, bool rtldNoLoad, bool rtldLocal,
+                                     bool rtldNoDelete, bool rtldNow, bool fromOFI, const void* callerAddress,
+                                     bool canUsePrebuiltSharedCacheClosure)
 {
     bool sharedCacheFormatCompatible = (_dyldCacheAddress != nullptr) && (_dyldCacheAddress->header.formatVersion == dyld3::closure::kFormatVersion);
+    canUsePrebuiltSharedCacheClosure &= sharedCacheFormatCompatible;
 
     // quick check if path is in shared cache and already loaded
     if ( _dyldCacheAddress != nullptr ) {
@@ -1687,7 +1708,7 @@ const MachOLoaded* AllImages::dlopen(Diagnostics& diag, const char* path, bool r
                  sharedCacheFormatCompatible ) {
                 const dyld3::closure::ImageArray* images = _dyldCacheAddress->cachedDylibsImageArray();
                 const dyld3::closure::Image* image = images->imageForNum(dyldCacheImageIndex+1);
-                return loadImage(diag, image->imageNum(), nullptr, rtldLocal, rtldNoDelete, rtldNow, fromOFI);
+                return loadImage(diag, path, image->imageNum(), nullptr, rtldLocal, rtldNoDelete, rtldNow, fromOFI, callerAddress);
             }
         }
     }
@@ -1709,10 +1730,11 @@ const MachOLoaded* AllImages::dlopen(Diagnostics& diag, const char* path, bool r
     // Then try again with forcing a new closure
     for (bool canUseSharedCacheClosure : { true, false }) {
         // We can only use a shared cache closure if the shared cache format is the same as libdyld.
-        canUseSharedCacheClosure &= sharedCacheFormatCompatible;
+        canUseSharedCacheClosure &= canUsePrebuiltSharedCacheClosure;
         closure::FileSystemPhysical fileSystem(nullptr, nullptr, _allowEnvPaths);
+        RootsChecker rootsChecker;
         closure::ClosureBuilder::AtPath atPathHanding = (_allowAtPaths ? closure::ClosureBuilder::AtPath::all : closure::ClosureBuilder::AtPath::onlyInRPaths);
-        closure::ClosureBuilder cb(_nextImageNum, fileSystem, _dyldCacheAddress, true, *_archs, closure::gPathOverrides, atPathHanding);
+        closure::ClosureBuilder cb(_nextImageNum, fileSystem, rootsChecker, _dyldCacheAddress, true, *_archs, closure::gPathOverrides, atPathHanding, true, nullptr, (dyld3::Platform)platform());
         newClosure = cb.makeDlopenClosure(path, _mainClosure, _loadedImages.array(), callerImageNum, rtldNoLoad, rtldNow, canUseSharedCacheClosure, &topImageNum);
         if ( newClosure == closure::ClosureBuilder::sRetryDlopenClosure ) {
             log_apis("   dlopen: closure builder needs to retry: %s\n", path);
@@ -1736,7 +1758,7 @@ const MachOLoaded* AllImages::dlopen(Diagnostics& diag, const char* path, bool r
         if ( const closure::ImageArray* newArray = newClosure->images() ) {
             appendToImagesArray(newArray);
         }
-        log_apis("   dlopen: made closure: %p\n", newClosure);
+        log_apis("   dlopen: made %s closure: %p\n", newClosure->topImage()->variantString(), newClosure);
     }
 
     // if already loaded, just bump refCount and return
@@ -1772,14 +1794,16 @@ const MachOLoaded* AllImages::dlopen(Diagnostics& diag, const char* path, bool r
         }
     }
 
-    return loadImage(diag, topImageNum, newClosure, rtldLocal, rtldNoDelete, rtldNow, fromOFI);
+    return loadImage(diag, path, topImageNum, newClosure, rtldLocal, rtldNoDelete, rtldNow, fromOFI, callerAddress);
 }
 
 // Note this is noinline to avoid having too much stack used in the parent
 // dlopen method
 __attribute__((noinline))
-const MachOLoaded* AllImages::loadImage(Diagnostics& diag, closure::ImageNum topImageNum, const closure::DlopenClosure* newClosure,
-                                        bool rtldLocal, bool rtldNoDelete, bool rtldNow, bool fromOFI) {
+const MachOLoaded* AllImages::loadImage(Diagnostics& diag, const char* path,
+                                        closure::ImageNum topImageNum, const closure::DlopenClosure* newClosure,
+                                        bool rtldLocal, bool rtldNoDelete, bool rtldNow, bool fromOFI,
+                                        const void* callerAddress) {
     // Note this array is used as the storage to Loader so needs to be at least
     // large enough to handle whatever total number of images we need to do the dlopen
     STACK_ALLOC_OVERFLOW_SAFE_ARRAY(LoadedImage, newImages, 1024);
@@ -1790,9 +1814,10 @@ const MachOLoaded* AllImages::loadImage(Diagnostics& diag, closure::ImageNum top
     dyld3::Array<dyld3::closure::Image::ObjCSelectorImage>  selectorImages;
 
     // run loader to load all new images
+    RootsChecker rootsChecker;
     Loader loader(_loadedImages.array(), newImages, _dyldCacheAddress, imagesArrays(),
-                  selectorOpt, selectorImages,
-                  &dyld3::log_loads, &dyld3::log_segments, &dyld3::log_fixups, &dyld3::log_dofs);
+                  selectorOpt, selectorImages, rootsChecker, (dyld3::Platform)platform(),
+                  &dyld3::log_loads, &dyld3::log_segments, &dyld3::log_fixups, &dyld3::log_dofs, !rtldNow);
 
     // find Image* for top image, look in new closure first
     const closure::Image* topImage = nullptr;
@@ -1802,9 +1827,9 @@ const MachOLoaded* AllImages::loadImage(Diagnostics& diag, closure::ImageNum top
         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);
+            log_apis("   dlopen: using pre-built %s dlopen closure from dyld shared cache %p\n", topImage->variantString(), topImage);
         else
-            log_apis("   dlopen: using pre-built dlopen closure %p\n", topImage);
+            log_apis("   dlopen: using pre-built %s dlopen closure %p\n", topImage->variantString(), topImage);
     }
     LoadedImage topLoadedImage = LoadedImage::make(topImage);
     if ( rtldLocal && !topImage->inDyldCache() )
@@ -1819,9 +1844,19 @@ const MachOLoaded* AllImages::loadImage(Diagnostics& diag, closure::ImageNum top
     loader.completeAllDependents(diag, someCacheImageOverridden);
     if ( diag.hasError() )
         return nullptr;
-    loader.mapAndFixupAllImages(diag, _processDOFs, fromOFI);
-    if ( diag.hasError() )
+    bool closureOutOfDate;
+    bool recoverable;
+    loader.mapAndFixupAllImages(diag, _processDOFs, fromOFI, &closureOutOfDate, &recoverable);
+    if ( diag.hasError() ) {
+        // If we used a pre-built shared cache closure, and now found that it was out of date,
+        // try again and rebuild a new closure
+        // Note, newClosure is null in the case where we used a prebuilt closure
+        if ( closureOutOfDate && recoverable && (newClosure == nullptr) ) {
+            diag.clearError();
+            return dlopen(diag, path, false /* rtldNoLoad */, rtldLocal, rtldNoDelete, rtldNow, fromOFI, callerAddress, false);
+        }
         return nullptr;
+    }
 
     // Record if we had a root
     _someImageOverridden |= someCacheImageOverridden;
index dc277eaa677d2f66bbde13bca0eb6061aee509d8..50f579c5e55958a3d9112fe8713b51b6755ac591 100644 (file)
@@ -36,7 +36,7 @@
 #include "DyldSharedCache.h"
 
 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
 // only in macOS and deprecated 
 struct VIS_HIDDEN OFIInfo
 {
@@ -56,6 +56,7 @@ public:
     typedef void                (*NotifyFunc)(const mach_header* mh, intptr_t slide);
     typedef void                (*LoadNotifyFunc)(const mach_header* mh, const char* path, bool unloadable);
     typedef void                (*BulkLoadNotifier)(unsigned count, const mach_header* mhs[], const char* paths[]);
+    typedef void                (*MainFunc)(void);
 
     void                        init(const closure::LaunchClosure* closure, const DyldSharedCache* dyldCacheLoadAddress, const char* dyldCachePath,
                                      const Array<LoadedImage>& initialImages);
@@ -63,6 +64,7 @@ public:
     void                        setHasCacheOverrides(bool someCacheImageOverriden);
     bool                        hasCacheOverrides() const;
     void                        setMainPath(const char* path);
+    void                        setLaunchMode(uint32_t flags);
     void                        applyInitialImages();
 
     void                        addImages(const Array<LoadedImage>& newImages);
@@ -99,7 +101,8 @@ public:
     const char*                 imagePath(const closure::Image*) const;
     dyld_platform_t             platform() const;
     const GradedArchs&          archs() const;
-
+    uint32_t                    launchMode() const { return _launchMode; }
+    
     const Array<const closure::ImageArray*>& imagesArrays();
 
     void                        incRefCount(const mach_header* loadAddress);
@@ -119,7 +122,7 @@ public:
     void                        notifyMonitorLoads(const Array<LoadedImage>& newImages);
     void                        notifyMonitorUnloads(const Array<LoadedImage>& unloadingImages);
 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
     NSObjectFileImage           addNSObjectFileImage(const OFIInfo&);
     void                        removeNSObjectFileImage(NSObjectFileImage);
     bool                        forNSObjectFileImage(NSObjectFileImage imageHandle,
@@ -131,8 +134,13 @@ public:
                                                  void (^callback)(void* classPtr, bool isLoaded, bool* stop)) const;
     void                        forEachObjCProtocol(const char* protocolName,
                                                     void (^callback)(void* protocolPtr, bool isLoaded, bool* stop)) const;
+    
+    MainFunc                    getDriverkitMain();
+    void                        setDriverkitMain(MainFunc mainFunc);
 
-    const MachOLoaded*          dlopen(Diagnostics& diag, const char* path, bool rtldNoLoad, bool rtldLocal, bool rtldNoDelete, bool forceBindLazies, bool fromOFI, const void* callerAddress);
+    const MachOLoaded*          dlopen(Diagnostics& diag, const char* path, bool rtldNoLoad, bool rtldLocal, bool rtldNoDelete,
+                                       bool forceBindLazies, bool fromOFI, const void* callerAddress,
+                                       bool canUsePrebuiltSharedCacheClosure = true);
 
     struct ProgramVars
     {
@@ -142,7 +150,7 @@ public:
         const char***      environPtr;
         const char**       __prognamePtr;
     };
-    void                    setProgramVars(ProgramVars* vars);
+    void                    setProgramVars(ProgramVars* vars, bool keysOff, bool platformBinariesOnly);
 
     // Note these are to be used exclusively by forking
     void takeLockBeforeFork();
@@ -178,8 +186,10 @@ private:
         }                               array[2];  // programs with only main-exe and dyld cache fit in here
     };
 
-    const MachOLoaded*          loadImage(Diagnostics& diag, closure::ImageNum topImageNum, const closure::DlopenClosure* newClosure,
-                                          bool rtldLocal, bool rtldNoDelete, bool rtldNow, bool fromOFI);
+    const MachOLoaded*          loadImage(Diagnostics& diag, const char* path,
+                                          closure::ImageNum topImageNum, const closure::DlopenClosure* newClosure,
+                                          bool rtldLocal, bool rtldNoDelete, bool rtldNow, bool fromOFI,
+                                          const void* callerAddress);
 
 
     typedef void (*Initializer)(int argc, const char* argv[], char* envp[], const char* apple[], const ProgramVars* vars);
@@ -236,8 +246,11 @@ private:
     bool                                    _allowAtPaths        = false;
     bool                                    _allowEnvPaths       = false;
     bool                                    _someImageOverridden = false;
+    uint32_t                                _launchMode          = 0;
     uintptr_t                               _lowestNonCached     = 0;
     uintptr_t                               _highestNonCached    = UINTPTR_MAX;
+    
+    MainFunc                               _driverkitMain       = nullptr;
 #ifdef OS_UNFAIR_RECURSIVE_LOCK_INIT
     mutable os_unfair_recursive_lock        _globalLock      = OS_UNFAIR_RECURSIVE_LOCK_INIT;
 #else
@@ -250,9 +263,9 @@ private:
     GrowableArray<BulkLoadNotifier, 2, 2>   _loadBulkNotifiers;
     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;
+#if TARGET_OS_OSX
+    uint64_t                                _nextObjectFileImageNum = 0;
+    GrowableArray<OFIInfo, 4, 1>            _objectFileImages;
 #endif
 
     // ObjC selectors
index f35295a761cd2a162eadfa1ed302424d6dd1a027..3a6a88e19d00bb85cac0e76bf4e4f5da8538baa4 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <algorithm>
 #include <stdint.h>
+#include <assert.h>
 #include <stddef.h>
 #include <mach/mach.h>
 
@@ -159,13 +160,13 @@ inline void OverflowSafeArray<T,MAXCOUNT>::growTo(uintptr_t n)
 #if BUILDING_LIBDYLD
         //FIXME We should figure out a way to do this in dyld
         char crashString[256];
-        snprintf(crashString, 256, "OverflowSafeArray failed to allocate %lu bytes, vm_allocate returned: %d\n",
-                _overflowBufferSize, kr);
+        snprintf(crashString, 256, "OverflowSafeArray failed to allocate %llu bytes, vm_allocate returned: %d\n",
+                (uint64_t)_overflowBufferSize, kr);
         CRSetCrashLogMessage(crashString);
 #endif
         assert(0);
     }
-    ::memcpy((void*)_overflowBuffer, this->_elements, this->_usedCount*sizeof(T));
+    ::memcpy((void*)_overflowBuffer, (void*)this->_elements, this->_usedCount*sizeof(T));
     this->_elements = (T*)_overflowBuffer;
     this->_allocCount = _overflowBufferSize / sizeof(T);
 
index 35fff965de8a47e94c0a814e9b5d619c8dadd4dc..b518e67a675424d2155fa65a741404c0b6b9ba08 100644 (file)
@@ -1,9 +1,25 @@
-//
-//  BootArgs.cpp
-//  dyld
-//
-//  Created by Louis Gerbarg on 11/14/18.
-//
+/*
+* 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/sysctl.h>
 #include "Loading.h" // For internalInstall()
 #include "BootArgs.h"
 
-namespace dyld3 {
-/*
-* Checks to see if there are any args that impact dyld. These args
-* can be set sevaral ways. These will only be honored on development
-* and Apple Internal builds.
-*/
-bool BootArgs::contains(const char* arg)
-{
-    //FIXME: Use strnstr(). Unfortunately we are missing an imp in libc.a
-#if TARGET_OS_SIMULATOR
-    return false;
-#else
-    // don't check for boot-args on customer installs
-    if ( !internalInstall() )
-        return false;
-
-    // get length of full boot-args string
-    size_t len;
-    if ( sysctlbyname("kern.bootargs", NULL, &len, NULL, 0) != 0 )
-        return false;
-
-    // get copy of boot-args string
-    char bootArgsBuffer[len];
-    if ( sysctlbyname("kern.bootargs", bootArgsBuffer, &len, NULL, 0) != 0 )
-        return false;
-
-    // return true if 'arg' is a sub-string of boot-args
-    return (strstr(bootArgsBuffer, arg) != nullptr);
+namespace dyld {
+#if defined(__x86_64__) && !TARGET_OS_SIMULATOR
+    bool isTranslated();
 #endif
 }
 
+namespace dyld3 {
+
 uint64_t BootArgs::_flags = 0;
 
 bool BootArgs::forceCustomerCache() {
     return (_flags & kForceCustomerCacheMask);
 }
 
+bool BootArgs::forceDevelopmentCache() {
+    return (_flags & kForceDevelopmentCacheMask);
+}
+
 bool BootArgs::forceDyld2() {
+#if defined(__x86_64__) && !TARGET_OS_SIMULATOR
+    if (dyld::isTranslated()) { return true; }
+#endif
     // If both force dyld2 and dyld3 are set then use dyld3
     if (_flags & kForceDyld3CacheMask) { return false; }
     if (_flags & kForceDyld2CacheMask) { return true; }
-    if (contains("force_dyld2=1")) { return true; }
     return false;
 }
 
 bool BootArgs::forceDyld3() {
-    return ((_flags & kForceDyld3CacheMask) || contains("force_dyld3=1"));
+    return (_flags & kForceDyld3CacheMask);
 }
 
 bool BootArgs::enableDyldTestMode() {
index 19278de2a87e4370b3352880597baa3b8c0db2f1..b12917a594ba5e8ee8b0742378cebe054ece59b9 100644 (file)
@@ -1,9 +1,26 @@
-//
-//  BootArgs.hpp
-//  dyld
-//
-//  Created by Louis Gerbarg on 11/14/18.
-//
+/*
+* 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_BOOTARGS_H__
 #define __DYLD_BOOTARGS_H__
@@ -15,8 +32,8 @@
 namespace dyld3 {
 #if BUILDING_DYLD
     struct VIS_HIDDEN BootArgs {
-        static bool contains(const char* arg);
         static bool forceCustomerCache();
+        static bool forceDevelopmentCache();
         static bool forceDyld2();
         static bool forceDyld3();
         static bool enableDyldTestMode();
@@ -25,6 +42,7 @@ namespace dyld3 {
     private:
         static const uint64_t kForceCustomerCacheMask = 1<<0;
         static const uint64_t kDyldTestModeMask = 1<<1;
+        static const uint64_t kForceDevelopmentCacheMask = 1<<2;
         static const uint64_t kForceDyld2CacheMask = 1<<15;
         static const uint64_t kForceDyld3CacheMask = 1<<16;
         static const uint64_t kEnableCompactImageInfoMask = 1<<17;
index ef1fffe8e9cbbb3b607dec207d59d2ececcf3b6b..5b95550b439d4b9536b0e2cc7c8e39bd8cd4fb0c 100644 (file)
@@ -31,6 +31,7 @@
 #include <sys/stat.h>
 #include <sys/sysctl.h>
 #include <System/machine/cpu_capabilities.h>
+#include <_simple.h>
 
 extern "C" {
   #include <corecrypto/ccdigest.h>
@@ -142,7 +143,7 @@ bool Image::representsImageNum(ImageNum num) const
         return true;
     if ( !flags.isDylib )
         return false;
-    if ( flags.inDyldCache )
+    if ( !flags.hasOverrideImageNum )
         return false;
     ImageNum cacheImageNum;
     if ( isOverrideOfDyldCacheImage(cacheImageNum) )
@@ -573,6 +574,16 @@ bool Image::hasPrecomputedObjC() const
     return getFlags().hasPrecomputedObjC;
 }
 
+bool Image::fixupsNotEncoded() const
+{
+    return getFlags().fixupsNotEncoded;
+}
+
+bool Image::rebasesNotEncoded() const
+{
+    return getFlags().rebasesNotEncoded;
+}
+
 void Image::forEachTerminator(const void* imageLoadAddress, void (^handler)(const void* terminator)) const
 {
     uint32_t size;
@@ -643,17 +654,10 @@ void Image::forEachFixup(void (^rebase)(uint64_t imageOffsetToRebase, bool& stop
     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;
-    }
+    stop = this->forEachBind(bind);
+    if ( stop )
+        return;
+
 
     if (hasChainedFixups())
         chainedFixups(chainedStartsOffset(), chainedTargets(), stop);
@@ -729,6 +733,24 @@ void Image::forEachFixup(void (^rebase)(uint64_t imageOffsetToRebase, bool& stop
     }
 }
 
+bool Image::forEachBind(void (^bind)(uint64_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop)) const
+{
+    const uint32_t pointerSize = is64() ? 8 : 4;
+    bool stop = false;
+    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;
+    }
+    return stop;
+}
+
 void Image::forEachTextReloc(void (^rebase)(uint32_t imageOffsetToRebase, bool& stop),
                              void (^bind)(uint32_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop)) const
 {
@@ -888,6 +910,11 @@ void Image::forEachImageToInitBefore(void (^handler)(ImageNum imageToInit, bool&
     }
 }
 
+const char* Image::variantString() const
+{
+    return (this->fixupsNotEncoded() ? "minimal" : "full");
+}
+
 ////////////////////////////  ImageArray ////////////////////////////////////////
 
 size_t ImageArray::size() const
@@ -954,7 +981,11 @@ const Image* ImageArray::imageForNum(ImageNum num) const
 
 const Image* ImageArray::findImage(const Array<const ImageArray*> imagesArrays, ImageNum imageNum)
 {
-   for (const ImageArray* ia : imagesArrays) {
+    // Search image arrays backwards as the main closure, or dlopen closures, may rebuild closures
+    // for shared cache images which are not roots, but whose initialisers must rebuild as they depend
+    // on a root
+    for (uintptr_t index = imagesArrays.count(); index > 0; --index) {
+        const ImageArray* ia = imagesArrays[index - 1];
         if ( const Image* result = ia->imageForNum(imageNum) )
             return result;
     }
@@ -986,7 +1017,7 @@ const ImageArray* Closure::images() const
     return (ImageArray*)result;
 }
 
-ImageNum Closure::topImage() const
+ImageNum Closure::topImageNum() const
 {
     uint32_t size;
     const ImageNum* top = (ImageNum*)findAttributePayload(Type::topImage, &size);
@@ -995,6 +1026,13 @@ ImageNum Closure::topImage() const
     return *top;
 }
 
+const Image* Closure::topImage() const
+{
+    ImageNum                            imageNum      = this->topImageNum();
+    const dyld3::closure::ImageArray*   closureImages = this->images();
+    return closureImages->imageForNum(imageNum);
+}
+
 void Closure::forEachPatchEntry(void (^handler)(const PatchEntry& entry)) const
 {
        forEachAttributePayload(Type::cacheOverrides, ^(const void* payload, uint32_t size, bool& stop) {
@@ -1021,6 +1059,7 @@ void Closure::deallocate() const
     ::vm_deallocate(mach_task_self(), (long)this, size());
 }
 
+
 ////////////////////////////  LaunchClosure ////////////////////////////////////////
 
 void LaunchClosure::forEachMustBeMissingFile(void (^handler)(const char* path, bool& stop)) const
@@ -1074,12 +1113,6 @@ bool LaunchClosure::builtAgainstDyldCache(uuid_t cacheUUID) const
     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) {
@@ -1269,86 +1302,69 @@ void LaunchClosure::duplicateClassesHashTable(const ObjCClassDuplicatesOpt*& dup
     duplicateClassesHashTable = (const ObjCClassDuplicatesOpt*)buffer;
 }
 
-static void putHexNibble(uint8_t value, char*& p)
-{
-    if ( value < 10 )
-        *p++ = '0' + value;
-    else
-        *p++ = 'A' + value - 10;
-}
 
-static void putHexByte(uint8_t value, char*& p)
+static bool getContainerLibraryCachesDir(const char* envp[], char libCacheDir[])
 {
-    value &= 0xFF;
-    putHexNibble(value >> 4,   p);
-    putHexNibble(value & 0x0F, p);
-}
+    // $HOME is root of writable data container
+    const char* homeDir = _simple_getenv(envp, "HOME");
+    if ( homeDir == nullptr )
+        return false;
 
-static bool hashBootAndFileInfo(const char* mainExecutablePath, char hashString[32])
-{
-    struct stat statbuf;
-    if ( ::stat(mainExecutablePath, &statbuf) != 0)
+    // Use realpath to block malicious values like HOME=/tmp/../usr/bin
+    char realHomePath[PATH_MAX];
+    if ( realpath(homeDir, realHomePath) != nullptr )
+        homeDir = realHomePath;
+#if TARGET_OS_OSX
+    // <rdar://problem/66593232> iOS apps on Apple Silicon macOS have a different data container location
+    if ( strstr(homeDir, "/Library/Containers/") == nullptr )
+        return false;
+#else
+    // <rdar://problem/47688842> dyld3 should only save closures to disk for containerized apps
+    if ( strncmp(homeDir, "/private/var/mobile/Containers/Data/", 36) != 0 )
         return false;
-#if !TARGET_OS_DRIVERKIT // Temp until core crypto is available
-    const struct ccdigest_info* di = ccsha256_di();
-    ccdigest_di_decl(di, hashTemp); // defines hashTemp array in stack
-    ccdigest_init(di, hashTemp);
-
-    // put boot time into hash
-    const uint64_t* bootTime = ((uint64_t*)_COMM_PAGE_BOOTTIME_USEC);
-    ccdigest_update(di, hashTemp, sizeof(uint64_t), bootTime);
-
-    // put inode of executable into hash
-    ccdigest_update(di, hashTemp, sizeof(statbuf.st_ino), &statbuf.st_ino);
-
-    // put mod-time of executable into hash
-    ccdigest_update(di, hashTemp, sizeof(statbuf.st_mtime), &statbuf.st_mtime);
-
-    // complete hash computation and append as hex string
-    uint8_t hashBits[32];
-    ccdigest_final(di, hashTemp, hashBits);
-    char* s = hashString;
-    for (size_t i=0; i < sizeof(hashBits); ++i)
-        putHexByte(hashBits[i], s);
-    *s = '\0';
 #endif
+
+    // return $HOME/Library/Caches/
+    strlcpy(libCacheDir, homeDir, PATH_MAX);
+    strlcat(libCacheDir, "/Library/Caches", PATH_MAX);
     return true;
 }
 
-bool LaunchClosure::buildClosureCachePath(const char* mainExecutablePath, char closurePath[], const char* tempDir,
-                                          bool makeDirsIfMissing)
+bool LaunchClosure::buildClosureCachePath(const char* mainExecutablePath, const char* envp[],
+                                          bool makeDirsIfMissing, char closurePath[])
 {
-    // <rdar://problem/47688842> dyld3 should only save closures to disk for containerized apps
-    if ( strstr(tempDir, "/Containers/Data/") == nullptr )
+    // get path to data container's Library/Caches/ dir
+    if ( !getContainerLibraryCachesDir(envp, closurePath) )
         return false;
 
-    strlcpy(closurePath, tempDir, PATH_MAX);
-    strlcat(closurePath, "/com.apple.dyld/", PATH_MAX);
-    
-    // make sure dyld sub-dir exists
+    // make sure XXX/Library/Caches/ exists
+    struct stat statbuf;
+    if ( dyld3::stat(closurePath, &statbuf) != 0 )
+        return false;
+
+    // add dyld sub-dir
+    strlcat(closurePath, "/com.apple.dyld", PATH_MAX);
     if ( makeDirsIfMissing ) {
-        struct stat statbuf;
-        if ( ::stat(closurePath, &statbuf) != 0 ) {
+        if ( dyld3::stat(closurePath, &statbuf) != 0 ) {
             if ( ::mkdir(closurePath, S_IRWXU) != 0 )
                 return false;
         }
     }
-    
+
+    // add <prog-name> + ".closure"
     const char* leafName = strrchr(mainExecutablePath, '/');
     if ( leafName == nullptr )
         leafName = mainExecutablePath;
     else
         ++leafName;
+    strlcat(closurePath, "/", PATH_MAX);
     strlcat(closurePath, leafName, PATH_MAX);
-    strlcat(closurePath, "-", PATH_MAX);
-
-    if ( !hashBootAndFileInfo(mainExecutablePath, &closurePath[strlen(closurePath)]) )
-        return false;
 
     strlcat(closurePath, ".closure", PATH_MAX);
     return true;
 }
 
+
 ////////////////////////////  ObjCStringTable ////////////////////////////////////////
     
 uint32_t ObjCStringTable::hash(const char *key, size_t keylen) const
index ace36a542d4ba08affb925d3a5a4ea867994737a..628208bdb60c53ca537fed592429cdd47e2201a7 100644 (file)
@@ -110,7 +110,6 @@ struct VIS_HIDDEN TypedBytes
         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)
@@ -166,6 +165,8 @@ struct VIS_HIDDEN Image : ContainerTypedBytes
     bool                hasObjC() const;
     bool                hasInitializers() const;
     bool                hasPrecomputedObjC() const;
+    bool                fixupsNotEncoded() const;   // minimal closure, dyld must parse and apply fixups
+    bool                rebasesNotEncoded() const;
     bool                hasTerminators() const;
     bool                hasReadOnlyData() const;
     bool                hasChainedFixups() const;
@@ -177,7 +178,6 @@ struct VIS_HIDDEN Image : ContainerTypedBytes
     bool                is64() const;
     bool                neverUnload() const;
     bool                cwdMustBeThisDir() const;
-    bool                isPlatformBinary() const;
     bool                overridableDylib() const;
     bool                hasFileModTimeAndInode(uint64_t& inode, uint64_t& mTime) const;
     void                forEachCDHash(void (^handler)(const uint8_t cdHash[20], bool& stop)) const;
@@ -193,6 +193,7 @@ struct VIS_HIDDEN Image : ContainerTypedBytes
     bool                hasPathWithHash(const char* path, uint32_t hash) const;
     bool                isOverrideOfDyldCacheImage(ImageNum& cacheImageNum) const;
     uint64_t            textSize() const;
+    const char*         variantString() const; // "minimal" or "full" closure
 
        union ResolvedSymbolTarget
     {
@@ -360,6 +361,8 @@ struct VIS_HIDDEN Image : ContainerTypedBytes
     void                forEachTextReloc(void (^rebase)(uint32_t imageOffsetToRebase, bool& stop),
                                          void (^bind)(uint32_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop)) const;
 
+    bool                forEachBind(void (^bind)(uint64_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop)) const;
+
        static_assert(sizeof(ResolvedSymbolTarget) == 8, "Overflow in size of SymbolTargetLocation");
 
     static uint32_t     hashFunction(const char*);
@@ -370,7 +373,9 @@ private:
     friend class ClosureBuilder;
     friend class ClosureWriter;
     friend class LaunchClosureWriter;
-
+    friend class RebasePatternBuilder;
+    friend class BindPatternBuilder;
+    
     uint32_t             pageSize() const;
 
     struct Flags
@@ -396,7 +401,10 @@ private:
                         hasReadOnlyData              : 1,
                         hasChainedFixups             : 1,
                         hasPrecomputedObjC           : 1,
-                        padding                      : 17;
+                        fixupsNotEncoded             : 1,
+                        rebasesNotEncoded            : 1,
+                        hasOverrideImageNum          : 1,
+                        padding                      : 14;
     };
 
     static_assert(sizeof(Flags) == sizeof(uint64_t), "Flags overflow");
@@ -657,7 +665,8 @@ struct VIS_HIDDEN Closure : public ContainerTypedBytes
 {
     size_t              size() const;
     const ImageArray*   images() const;
-    ImageNum            topImage() const;
+    ImageNum            topImageNum() const;
+    const Image*        topImage() const;
     void                deallocate() const;
 
     friend class ClosureWriter;
@@ -698,7 +707,6 @@ 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                forEachSkipIfExistsFile(void (^handler)(const SkippedFile& file, bool& stop)) const;
     void                forEachEnvVar(void (^handler)(const char* keyEqualValue, bool& stop)) const;
@@ -721,8 +729,8 @@ struct VIS_HIDDEN LaunchClosure : public Closure
     bool                hasInterposings() const;
     bool                hasProgramVars(uint32_t& runtimeOffset) const;
     
-    static bool         buildClosureCachePath(const char* mainExecutablePath, char closurePath[], const char* tempDir,
-                                              bool makeDirsIfMissing);
+    static bool         buildClosureCachePath(const char* mainExecutablePath, const char* envp[],
+                                              bool makeDirsIfMissing, char closurePath[]);
 
 private:
     friend class LaunchClosureWriter;
index 2d83fc593c200d4850000daf192ba9a635c26789..c21e632f523010ed17a6bdb916ee6f4752bf7539 100644 (file)
 #include "ClosureWriter.h"
 #include "ClosureBuilder.h"
 #include "MachOAnalyzer.h"
+#include "MachOAnalyzerSet.h"
 #include "libdyldEntryVector.h"
+#include "RootsChecker.h"
 #include "Tracing.h"
 
 #define CLOSURE_SELOPT_WRITE
 #include "objc-shared-cache.h"
 
+#if BUILDING_DYLD
+namespace dyld { void log(const char*, ...); }
+#endif
+
 namespace dyld3 {
 namespace closure {
 
 
 const DlopenClosure* ClosureBuilder::sRetryDlopenClosure = (const DlopenClosure*)(-1);
 
-ClosureBuilder::ClosureBuilder(uint32_t startImageNum, const FileSystem& fileSystem, const DyldSharedCache* dyldCache, bool dyldCacheIsLive,
+ClosureBuilder::ClosureBuilder(uint32_t startImageNum, const FileSystem& fileSystem, const RootsChecker& rootsChecker,
+                               const DyldSharedCache* dyldCache, bool dyldCacheIsLive,
                                const GradedArchs& archs, const PathOverrides& pathOverrides, AtPath atPathHandling, bool allowRelativePaths,
-                               LaunchErrorInfo* errorInfo, Platform platform, const CacheDylibsBindingHandlers* handlers)
-    : _fileSystem(fileSystem), _dyldCache(dyldCache), _pathOverrides(pathOverrides), _archs(archs), _platform(platform), _startImageNum(startImageNum),
-      _handlers(handlers), _atPathHandling(atPathHandling), _launchErrorInfo(errorInfo), _dyldCacheIsLive(dyldCacheIsLive), _allowRelativePaths(allowRelativePaths)
+                               LaunchErrorInfo* errorInfo, Platform platform, DylibFixupHandler handler)
+: _fileSystem(fileSystem), _rootsChecker(rootsChecker), _dyldCache(dyldCache), _pathOverrides(pathOverrides), _archs(archs), _platform(platform), _startImageNum(startImageNum),
+      _dylibFixupHandler(handler), _atPathHandling(atPathHandling), _launchErrorInfo(errorInfo), _dyldCacheIsLive(dyldCacheIsLive), _allowRelativePaths(allowRelativePaths)
 {
     if ( dyldCache != nullptr ) {
         _dyldImageArray = dyldCache->cachedDylibsImageArray();
-        if ( (dyldCache->header.otherImageArrayAddr != 0) && (dyldCache->header.progClosuresSize == 0) )
-            _makingClosuresInCache = true;
     }
 }
 
@@ -74,6 +79,11 @@ ClosureBuilder::~ClosureBuilder() {
         PathPool::deallocate(_objcDuplicateClassWarnings);
 }
 
+static bool iOSSupport(const char* path)
+{
+    return ( strncmp(path, "/System/iOSSupport/", 19) == 0 );
+}
+
 bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& forImageChain, BuilderLoadedImage*& foundImage, LinkageType linkageType,
                                 uint32_t compatVersion, bool canUseSharedCacheClosure)
 {
@@ -90,6 +100,13 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for
         dylibsExpectedOnDisk = _dyldCache->header.dylibsExpectedOnDisk;
     }
 
+    // when building dyld cache for macOS, if requesting dylib is iOSMac unzippered twin, tell pathOverrides object to look in /System/iOSSupport first
+    dyld3::Platform targetPlatform = _platform;
+    if ( _makingDyldCacheImages && (_platform == dyld3::Platform::macOS) ) {
+        if ( forImageChain.image.loadAddress()->builtForPlatform(Platform::iOSMac, true) )
+            targetPlatform = Platform::iOSMac;
+    }
+
     _pathOverrides.forEachPathVariant(loadPath, pathIsInDyldCacheWhichCannotBeOverridden, ^(const char* possibleVariantPath, bool isFallbackPath, bool& stopPathVariant) {
 
         // This check is within forEachPathVariant() to let DYLD_LIBRARY_PATH override LC_RPATH
@@ -143,6 +160,12 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for
                 if ( _fileSystem.fileExists(possiblePath, &fileFoundINode, &fileFoundMTime, nullptr, &inodesMatchRuntime) ) {
                     fileFound = true;
                     for (BuilderLoadedImage& li: _loadedImages) {
+                        if ( (li.loadedFileInfo.inode == 0) && (li.loadedFileInfo.mtime == 0) ) {
+                            // Some already loaded image does not have an inode/mtime recorded, fix that if we can
+                            if ( dylibsExpectedOnDisk || !li.loadAddress()->inDyldCache() ) {
+                                _fileSystem.fileExists(li.path(), &li.loadedFileInfo.inode, &li.loadedFileInfo.mtime , nullptr, nullptr);
+                            }
+                        }
                         if ( (li.loadedFileInfo.inode == fileFoundINode) && (li.loadedFileInfo.mtime == fileFoundMTime) )  {
                             foundImage = &li;
                             result = true;
@@ -153,6 +176,23 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for
                 }
             }
 
+            // We record the realpath of the file in the loaded images, but we might be loading via a symlink path.
+            // We need to search using the realpath just in case the dylib the symlink points to was overwritten while
+            // the process is running
+            if ( fileFound ) {
+                char realPath[MAXPATHLEN];
+                if ( _fileSystem.getRealPath(possiblePath, realPath) ) {
+                    for (BuilderLoadedImage& li: _loadedImages) {
+                        if ( strcmp(li.path(), realPath) == 0 ) {
+                            foundImage = &li;
+                            result = true;
+                            stop = true;
+                            return;
+                        }
+                    }
+                }
+            }
+
             bool                  unmapWhenDone     = false;
             bool                  contentRebased    = false;
             bool                  hasInits          = false;
@@ -198,26 +238,44 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for
                             }
                         }
                     }
-                    // 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 found in the cache, but not on-disk, this may be an already loaded image, but we are opening the alias.
+                // For example, we are trying to open .../AppKit but we already have a loaded root of .../Versions/C/AppKit
+                // This doesn't work with the calls to realpath when the symlinks themselves were removed from disk.
+                if ( foundInCache && !fileFound ) {
+                    ImageNum dyldCacheImageNum = dyldCacheImageIndex + 1;
+                    for (BuilderLoadedImage& li: _loadedImages) {
+                        if ( li.overrideImageNum == dyldCacheImageNum ) {
+                            foundImage = &li;
+                            result = true;
+                            stop = true;
+                            return;
+                        }
+                    }
+                }
+
+                // if not found in cache, may be a symlink to something in cache
+                // We have to do this check even if the symlink target is not on disk as we may
+                // have symlinks in iOSMac dlopen paths which are resolved to a dylib removed from disk
+                if ( !foundInCache && (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;
-                                        }
+                            // 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
                             }
+#endif
                         }
                     }
                 }
@@ -227,25 +285,32 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for
                     ImageNum dyldCacheImageNum = dyldCacheImageIndex + 1;
                     bool useCache = true;
                     markNeverUnload = true; // dylibs in cache, or dylibs that override cache should not be unloaded at runtime
+                    bool ignoreCacheDylib = false;
                     const Image* image = _dyldImageArray->imageForNum(dyldCacheImageNum);
                     if ( image->overridableDylib() ) {
                         if ( fileFound ) {
-                            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 = ( (fileFoundINode == expectedInode) && (fileFoundMTime == expectedModTime) );
-                            }
-                            else if ( _makingClosuresInCache ) {
+                            if ( _makingClosuresInCache ) {
                                 // during iOS cache build, don't look at files on disk, use ones in cache
                                 useCache = true;
-                            }
-                            else {
+                            } else if ( !_rootsChecker.onDiskFileIsRoot(filePath, _dyldCache, image,
+                                                                        &_fileSystem, fileFoundINode, fileFoundMTime) ) {
+                                // file exists, but is not a root
+                                useCache = true;
+                            } else {
                                 // iOS internal build. Any disk on cache overrides cache
                                 useCache = false;
                             }
                         }
-                        if ( !useCache ) {
+                        if ( useCache && ((targetPlatform == Platform::iOSMac) || (targetPlatform == Platform::macOS)) ) {
+                            // check this cached dylib is suitable for catalyst or mac program
+                            mh = (MachOAnalyzer*)_dyldCache->getIndexedImageEntry(dyldCacheImageNum-1, loadedFileInfo.mtime, loadedFileInfo.inode);
+                            if ( !mh->loadableIntoProcess(targetPlatform, possiblePath) ) {
+                                useCache = false;
+                                mh = nullptr;
+                                ignoreCacheDylib = true;
+                            }
+                        }
+                        if ( !useCache && !ignoreCacheDylib ) {
                             overrideImageNum = dyldCacheImageNum;
                             _foundDyldCacheRoots = true;
                         }
@@ -272,7 +337,7 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for
                 return;
             }
 
-             // if not found yet, mmap file
+            // if not found yet, mmap file
             if ( mh == nullptr ) {
                 loadedFileInfo = MachOAnalyzer::load(_diag, _fileSystem, filePath, _archs, _platform, realPath);
                 mh = (const MachOAnalyzer*)loadedFileInfo.fileContent;
@@ -331,10 +396,17 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for
                 loadedFileInfo.fileContent = mh;
             }
 
+            if ( mh->inDyldCache() ) {
+                // We may be loading from a symlink, so use the path in the cache which is the realpath
+                filePath = _dyldImageArray->imageForNum(foundImageNum)->path();
+            }
+
             // if path is not original path, or its an inserted path (as forEachInColonList uses a stack temporary)
             if ( (filePath != loadPath) || (linkageType == LinkageType::kInserted) ) {
-                // possiblePath may be a temporary (stack) string, since we found file at that path, make it permanent
-                filePath = strdup_temp(filePath);
+                if ( !mh->inDyldCache() ) {
+                    // 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
                 // This is the case where we didn't find the image with the path in the shared cache, perhaps as it used library paths
                 // but the path we requested had pointed in to the cache
@@ -351,6 +423,17 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for
                 }
             }
 
+            // check if this is an iOSMac dylib that is overriding a macOS dylib in the dyld cache
+            if ( mh->inDyldCache() && iOSSupport(filePath) ) {
+                const char* twinPath = &filePath[18];
+                uint32_t dyldCacheImageIndex;
+                if ( (_dyldCache != nullptr) && _dyldCache->hasImagePath(twinPath, dyldCacheImageIndex) ) {
+                    ImageNum possibleOverrideNum = dyldCacheImageIndex+1;
+                    if ( possibleOverrideNum != foundImageNum )
+                        overrideImageNum = possibleOverrideNum;
+                }
+            }
+
             if ( !markNeverUnload ) {
                 switch (linkageType) {
                     case LinkageType::kStatic:
@@ -387,7 +470,7 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for
             // Set the path again just in case it was strdup'ed.
             loadedFileInfo.path = filePath;
 
-            // add new entry
+             // add new entry
             BuilderLoadedImage entry;
             entry.loadedFileInfo        = loadedFileInfo;
             entry.imageNum              = foundImageNum;
@@ -399,7 +482,10 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for
             entry.isBadImage            = false;
             entry.mustBuildClosure      = mustBuildClosure;
             entry.hasMissingWeakImports = false;
+            entry.hasInterposingTuples  = !mh->inDyldCache() && mh->hasInterposingTuples();
             entry.overrideImageNum      = overrideImageNum;
+            entry.exportsTrieOffset     = 0;
+            entry.exportsTrieSize       = 0;
             _loadedImages.push_back(entry);
             foundImage = &_loadedImages.back();
             if ( isFallbackPath )
@@ -409,7 +495,7 @@ bool ClosureBuilder::findImage(const char* loadPath, const LoadedImageChain& for
         });
         if (result)
             stopPathVariant = true;
-    }, _platform);
+    }, targetPlatform);
 
     // If we found a file, but also had an error, then we must have logged a diagnostic for a file we couldn't use.
     // Clear that for now.
@@ -456,7 +542,7 @@ bool ClosureBuilder::expandAtLoaderPath(const char* loadPath, bool fromLCRPATH,
     return false;
 }
 
-bool ClosureBuilder::expandAtExecutablePath(const char* loadPath, bool fromLCRPATH, char fixedPath[])
+bool ClosureBuilder::expandAtExecutablePath(const char* loadPath, bool fromLCRPATH, bool fromLCRPATHinMain, char fixedPath[])
 {
     switch ( _atPathHandling ) {
         case AtPath::none:
@@ -464,6 +550,9 @@ bool ClosureBuilder::expandAtExecutablePath(const char* loadPath, bool fromLCRPA
         case AtPath::onlyInRPaths:
             if ( !fromLCRPATH )
                 return false;
+            // main executables can always have an LC_RPATH that uses @executable_path, other images cannot if restricted
+            if  ( !fromLCRPATHinMain )
+                return false;
             break;
         case AtPath::all:
             break;
@@ -519,7 +608,7 @@ void ClosureBuilder::forEachResolvedPathVar(const char* loadPath, const LoadedIm
 
     // expand @executable_path
     // Note this is supported for DYLD_INSERT_LIBRARIES
-    if ( expandAtExecutablePath(loadPath, false, tempPath) ) {
+    if ( expandAtExecutablePath(loadPath, false, false, tempPath) ) {
         bool stop = false;
         handler(tempPath, stop);
         return;
@@ -546,14 +635,28 @@ void ClosureBuilder::forEachResolvedPathVar(const char* loadPath, const LoadedIm
         // LC_RPATHS from each dylib as they are recursively loaded.  Our imageChain represents that stack.
         __block bool done = false;
         for (const LoadedImageChain* link = &forImageChain; (link != nullptr) && !done; link = link->previous) {
+            bool mainExecutable = link->image.loadAddress()->isMainExecutable();
             link->image.loadAddress()->forEachRPath(^(const char* rPath, bool& stop) {
                 // fprintf(stderr, "LC_RPATH %s from %s\n", rPath, link->image.loadedFileInfo.path);
-                if ( expandAtLoaderPath(rPath, true, link->image, tempPath) || expandAtExecutablePath(rPath, true, tempPath) ) {
+                if ( expandAtLoaderPath(rPath, true, link->image, tempPath) || expandAtExecutablePath(rPath, true, mainExecutable, tempPath) ) {
                     // @loader_path allowed and expended
                     strlcat(tempPath, rpathTail, PATH_MAX);
                     handler(tempPath, stop);
                 }
                 else if ( rPath[0] == '/' ) {
+#if (TARGET_OS_OSX && TARGET_CPU_ARM64)
+                    if ( (_platform == Platform::iOS) && (strncmp(rPath, "/usr/lib/swift", 14) == 0) ) {
+                        // LC_RPATH is to /usr/lib/swift, but running on macOS that is /System/iOSSupport/usr/lib/swift
+                        strlcpy(tempPath, "/System/iOSSupport", PATH_MAX);
+                        strlcat(tempPath, rPath, PATH_MAX);
+                        strlcat(tempPath, rpathTail, PATH_MAX);
+                        handler(tempPath, stop);
+                        if (stop) {
+                            done = true;
+                            return;
+                        }
+                    }
+#endif
                     // LC_RPATH is an absolute path, not blocked by AtPath::none
                     strlcpy(tempPath, rPath, PATH_MAX);
                     strlcat(tempPath, rpathTail, PATH_MAX);
@@ -583,7 +686,7 @@ void ClosureBuilder::forEachResolvedPathVar(const char* loadPath, const LoadedIm
     handler(loadPath, stop);
 }
 
-const char* ClosureBuilder::strdup_temp(const char* path)
+const char* ClosureBuilder::strdup_temp(const char* path) const
 {
     if ( _tempPaths == nullptr )
         _tempPaths = PathPool::allocate();
@@ -595,7 +698,9 @@ void ClosureBuilder::addMustBeMissingPath(const char* path)
     //fprintf(stderr, "must be missing: %s\n", path);
     if ( _mustBeMissingPaths == nullptr )
         _mustBeMissingPaths = PathPool::allocate();
-    _mustBeMissingPaths->add(path);
+    // don't add path if already in list
+    if ( !_mustBeMissingPaths->contains(path) )
+        _mustBeMissingPaths->add(path);
 }
 
 void ClosureBuilder::addSkippedFile(const char* path, uint64_t inode, uint64_t mtime)
@@ -615,6 +720,21 @@ ClosureBuilder::BuilderLoadedImage& ClosureBuilder::findLoadedImage(ImageNum ima
             return li;
         }
     }
+    assert(0 && "LoadedImage not found by num");
+}
+
+const ClosureBuilder::BuilderLoadedImage& ClosureBuilder::findLoadedImage(ImageNum imageNum) const
+{
+    for (const BuilderLoadedImage& li : _loadedImages) {
+        if ( li.imageNum == imageNum ) {
+            return li;
+        }
+    }
+    for (const BuilderLoadedImage& li : _loadedImages) {
+        if ( li.overrideImageNum == imageNum ) {
+            return li;
+        }
+    }
     assert(0 && "LoadedImage not found");
 }
 
@@ -625,7 +745,7 @@ ClosureBuilder::BuilderLoadedImage& ClosureBuilder::findLoadedImage(const MachOA
              return li;
         }
     }
-    assert(0 && "LoadedImage not found");
+    assert(0 && "LoadedImage not found by mh");
 }
 
 const MachOAnalyzer* ClosureBuilder::machOForImageNum(ImageNum imageNum)
@@ -790,20 +910,10 @@ void ClosureBuilder::loadDanglingUpwardLinks(bool canUseSharedCacheClosure)
 
 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 macOS, the cache can be customer/development in the basesystem/main OS
     // 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
+    // on customer caches, only allow libdispatch.dylib to be overridden
+    return _dyldCache->isOverridablePath(forImage.path());
 }
 
 void ClosureBuilder::buildImage(ImageWriter& writer, BuilderLoadedImage& forImage)
@@ -819,7 +929,10 @@ void ClosureBuilder::buildImage(ImageWriter& writer, BuilderLoadedImage& forImag
     writer.setIs64(macho->is64());
     writer.setIsExecutable(macho->isMainExecutable());
     writer.setUses16KPages(macho->uses16KPages());
-    writer.setOverridableDylib(overridableDylib(forImage));
+    if ( macho->inDyldCache() ) {
+        // only set on dylibs in the dyld shared cache
+        writer.setOverridableDylib(overridableDylib(forImage));
+    }
     writer.setInDyldCache(macho->inDyldCache());
     if ( macho->hasObjC() ) {
         writer.setHasObjC(true);
@@ -853,17 +966,15 @@ void ClosureBuilder::buildImage(ImageWriter& writer, BuilderLoadedImage& forImag
         writer.setFileInfo(forImage.loadedFileInfo.inode, forImage.loadedFileInfo.mtime);
     }
 #else
-    if ( _platform == Platform::macOS || MachOFile::isSimulatorPlatform(_platform) ) {
-        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);
-        }
+    // in cache builder code
+    if ( !_dyldCache->header.dylibsExpectedOnDisk ) {
+        // don't add file info for shared cache files mastered out of final file system
+        // This also covers executable and dlopen closures as we are not running on a live
+        // file system. no we don't have access to accurate inode/mtime
     }
     else {
-        // all other platforms, cache is built off-device, so inodes are not known
+        // file is either not in cache or is in cache but not mastered out
+        writer.setFileInfo(forImage.loadedFileInfo.inode, forImage.loadedFileInfo.mtime);
     }
 #endif
 
@@ -907,6 +1018,16 @@ void ClosureBuilder::buildImage(ImageWriter& writer, BuilderLoadedImage& forImag
     // set segments
     addSegments(writer, macho);
 
+    // if shared cache contains two variants of same framework (macOS and iOS), mark iOS one as override of macOS one
+    if ( _makingDyldCacheImages && iOSSupport(forImage.path()) ) {
+        const char* truncName = forImage.path()+18;
+        for (const BuilderLoadedImage& li : _loadedImages) {
+            if ( strcmp(li.path(), truncName) == 0 ) {
+                writer.setAsOverrideOf(li.imageNum);
+            }
+        }
+    }
+
     // record if this dylib overrides something in the cache
     if ( forImage.overrideImageNum != 0 ) {
         writer.setAsOverrideOf(forImage.overrideImageNum);
@@ -918,46 +1039,65 @@ void ClosureBuilder::buildImage(ImageWriter& writer, BuilderLoadedImage& forImag
             _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 {
-                // Note we have to do binds before rebases so that we know if we have missing lazy binds
-                addBindInfo(writer, forImage);
-                if ( _diag.noError() )
-                    addRebaseInfo(writer, macho);
-            }
-        }
+    // record fix up info
+    if ( macho->inDyldCache() && !_makingDyldCacheImages ) {
+        // when building app closures, don't record fix up info about dylibs in the cache
+    }
+    else if ( _makeMinimalClosure ) {
+        // don't record fix up info in dyld3s mode
+        writer.setFixupsNotEncoded();
+    }
+    else if ( !_makingDyldCacheImages && macho->hasChainedFixups() ) {
+        // when building app closures, just evaluate target of chain binds and record that table
+        addChainedFixupInfo(writer, forImage);
+    }
+    else {
+        // run rebase/bind opcodes or chained fixups
+        addFixupInfo(writer, forImage);
     }
     if ( _diag.hasError() ) {
         writer.setInvalid();
         return;
     }
     
-    // Don't build iOSMac for now.  Just add an invalid placeholder
-    if ( _makingDyldCacheImages && strncmp(forImage.path(), "/System/iOSSupport/", 19) == 0 ) {
-        writer.setInvalid();
-        return;
-    }
 
     // add initializers
-    bool contentRebased = forImage.contentRebased;
+#if BUILDING_CACHE_BUILDER
+
+    // In the shared cache builder, we'll only ever see 'inDyldCache' images here for the shared
+    // cache dylibs themselves.  These are in an intermediate state where the cache is not live, the pointers
+    // are unslid, but the pointers also don't contain fixup chains
+    dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = macho->makeVMAddrConverter(forImage.contentRebased);
+    if ( macho->inDyldCache() ) {
+        vmAddrConverter.preferredLoadAddress = 0;
+        vmAddrConverter.slide = 0;
+        vmAddrConverter.chainedPointerFormat = 0;
+        vmAddrConverter.contentRebased = false;
+        vmAddrConverter.sharedCacheChainedPointerFormat  = MachOAnalyzer::VMAddrConverter::SharedCacheFormat::none;
+    }
+
+#else
+
+    dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = macho->makeVMAddrConverter(forImage.contentRebased);
+#if !(BUILDING_LIBDYLD || BUILDING_DYLD)
+    // The shared cache is always live in dyld/libdyld, but if we get here then we are an offline tool
+    // In that case, use the shared cache vmAddrConverter if we need it
+    if ( macho->inDyldCache() )
+        vmAddrConverter = _dyldCache->makeVMAddrConverter(forImage.contentRebased);
+#endif
+
+#endif // BUILDING_CACHE_BUILDER
+
     __block unsigned initCount = 0;
     Diagnostics initializerDiag;
-    macho->forEachInitializer(initializerDiag, contentRebased, ^(uint32_t offset) {
+    macho->forEachInitializer(initializerDiag, vmAddrConverter, ^(uint32_t offset) {
         ++initCount;
     }, _dyldCache);
     if ( initializerDiag.noError() ) {
         if ( initCount != 0 ) {
             BLOCK_ACCCESSIBLE_ARRAY(uint32_t, initOffsets, initCount);
             __block unsigned index = 0;
-           macho->forEachInitializer(_diag, contentRebased, ^(uint32_t offset) {
+           macho->forEachInitializer(_diag, vmAddrConverter, ^(uint32_t offset) {
                 initOffsets[index++] = offset;
             }, _dyldCache);
             writer.setInitOffsets(initOffsets, initCount);
@@ -976,13 +1116,13 @@ void ClosureBuilder::buildImage(ImageWriter& writer, BuilderLoadedImage& forImag
     // add terminators (except for dylibs in the cache because they are never unloaded)
     if ( !macho->inDyldCache() ) {
         __block unsigned termCount = 0;
-        macho->forEachTerminator(_diag, contentRebased, ^(uint32_t offset) {
+        macho->forEachTerminator(_diag, vmAddrConverter, ^(uint32_t offset) {
             ++termCount;
         });
         if ( termCount != 0 ) {
             BLOCK_ACCCESSIBLE_ARRAY(uint32_t, termOffsets, termCount);
             __block unsigned index = 0;
-            macho->forEachTerminator(_diag, contentRebased, ^(uint32_t offset) {
+            macho->forEachTerminator(_diag, vmAddrConverter, ^(uint32_t offset) {
                 termOffsets[index++] = offset;
             });
             writer.setTermOffsets(termOffsets, termCount);
@@ -1107,8 +1247,9 @@ void ClosureBuilder::addInterposingTuples(LaunchClosureWriter& writer, const Ima
                     if ( !isTupleFixup(sectVmOffset, sectVmEndOffset, fixupOffset, entrySize, tupleIndex) )
                         return;
                     uint32_t bindOrdinal;
+                    int64_t  addend;
                     uint64_t rebaseTargetOffset;
-                    if ( fixupLoc->isBind(segInfo->pointer_format, bindOrdinal) ) {
+                    if ( fixupLoc->isBind(segInfo->pointer_format, bindOrdinal, addend) ) {
                         if ( bindOrdinal < targets.count() ) {
                             resolvedTuples[tupleIndex].stockImplementation = targets[bindOrdinal];
                         }
@@ -1169,72 +1310,376 @@ void ClosureBuilder::addInterposingTuples(LaunchClosureWriter& writer, const Ima
     });
 }
 
-void ClosureBuilder::addRebaseInfo(ImageWriter& writer, const MachOAnalyzer* mh)
+const Image::RebasePattern RebasePatternBuilder::_s_maxLeapPattern = { 0xFFFFF, 0, 0xF};
+const uint64_t             RebasePatternBuilder::_s_maxLeapCount   = _s_maxLeapPattern.repeatCount * _s_maxLeapPattern.skipCount;
+
+
+
+RebasePatternBuilder::RebasePatternBuilder(OverflowSafeArray<closure::Image::RebasePattern>& entriesStorage, uint64_t ptrSize)
+    : _rebaseEntries(entriesStorage), _lastLocation(-ptrSize), _ptrSize(ptrSize)
 {
-       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, !_foundMissingLazyBinds, ^(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++;
+}
+
+void RebasePatternBuilder::add(uint64_t runtimeOffset)
+{
+    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;
             }
-            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 > _s_maxLeapCount ) {
+            _rebaseEntries.push_back(_s_maxLeapPattern);
+            advanceCount -= _s_maxLeapCount;
+        }
+        // if next rebase is not reachable with skipCount==1 or skipCount==15, add intermediate
+        while ( advanceCount > _s_maxLeapPattern.repeatCount ) {
+            uint64_t count = advanceCount / _s_maxLeapPattern.skipCount;
+            _rebaseEntries.push_back({ (uint32_t)count, 0, _s_maxLeapPattern.skipCount });
+            advanceCount -= (count*_s_maxLeapPattern.skipCount);
+        }
+        if ( advanceCount != 0 )
+            _rebaseEntries.push_back({ (uint32_t)advanceCount, 0, 1 });
+        _rebaseEntries.push_back({ 1, 1, 0 });
+    }
+    _lastLocation = runtimeOffset;
+
+}
+
+
+BindPatternBuilder::BindPatternBuilder(OverflowSafeArray<closure::Image::BindPattern>& entriesStorage, uint64_t ptrSize)
+   : _bindEntries(entriesStorage), _ptrSize(ptrSize), _lastOffset(-ptrSize), _lastTarget({ {0, 0} })
+{
+}
+
+void BindPatternBuilder::add(uint64_t runtimeOffset, Image::ResolvedSymbolTarget target, bool weakBindCoalese)
+{
+    if ( weakBindCoalese )  {
+        // may be previous bind to this location
+        // if so, update that rather create new BindPattern
+        for (Image::BindPattern& aBind : _bindEntries) {
+            if ( (aBind.startVmOffset == runtimeOffset) && (aBind.repeatCount == 1)  && (aBind.skipCount == 0) ) {
+                aBind.target = target;
+                return;
             }
-            rebaseEntries.push_back({ 1, 1, 0 });
+        }
+    }
+    bool mergedIntoPrevious = false;
+    if ( !mergedIntoPrevious && (target == _lastTarget) && (runtimeOffset > _lastOffset) && !_bindEntries.empty() ) {
+        uint64_t skipAmount = (runtimeOffset - _lastOffset - _ptrSize)/_ptrSize;
+        if ( skipAmount*_ptrSize != (runtimeOffset - _lastOffset - _ptrSize) ) {
+            // misaligned pointer means we cannot optimize
         }
         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);
+            if ( (_bindEntries.back().repeatCount == 1) && (_bindEntries.back().skipCount == 0) && (skipAmount <= 255) ) {
+                _bindEntries.back().repeatCount = 2;
+                _bindEntries.back().skipCount   = skipAmount;
+                assert(_bindEntries.back().skipCount == skipAmount); // check overflow
+                mergedIntoPrevious       = true;
+            }
+            else if ( (_bindEntries.back().skipCount == skipAmount) && (_bindEntries.back().repeatCount < 0xfff) ) {
+                uint32_t prevRepeatCount = _bindEntries.back().repeatCount;
+                _bindEntries.back().repeatCount += 1;
+                assert(_bindEntries.back().repeatCount > prevRepeatCount); // check overflow
+                mergedIntoPrevious       = true;
+            }
+        }
+    }
+    if ( (target == _lastTarget) && (runtimeOffset == _lastOffset) && !_bindEntries.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);
+        _bindEntries.push_back(pattern);
+    }
+    _lastTarget = target;
+    _lastOffset = runtimeOffset;
+}
+
+
+bool ClosureBuilder::mas_fromImageWeakDefLookup(const WrappedMachO& fromWmo, const char* symbolName, uint64_t addend, CachePatchHandler patcher, FixupTarget& target) const
+{
+    // when building dylibs into the dyld cache, there is no load-order, so we cannot use the standard algorithm
+    // otherwise call through to standard weak-def coalescing algorithm
+    if ( !_makingDyldCacheImages )
+        return MachOAnalyzerSet::mas_fromImageWeakDefLookup(fromWmo, symbolName, addend, patcher, target);
+
+
+    // look first in /usr/lib/libc++, most will be here
+    Diagnostics diag;
+    for (const BuilderLoadedImage& li : _loadedImages) {
+        if ( li.loadAddress()->hasWeakDefs() && (strncmp(li.path(), "/usr/lib/libc++", 15) == 0) ) {
+            WrappedMachO libcxxWmo(li.loadAddress(), this, (void*)&li);
+            if ( libcxxWmo.findSymbolIn(diag, symbolName, addend, target) )
+                return true;
+        }
+    }
+
+    // if not found, try looking in the images itself, most custom weak-def symbols have a copy in the image itself
+    if ( fromWmo.findSymbolIn(diag, symbolName, addend, target) )
+        return true;
+
+    // if we link with something that also defines this weak-def, use it
+    ClosureBuilder::BuilderLoadedImage* fromImage = (ClosureBuilder::BuilderLoadedImage*)(fromWmo._other);
+    for (Image::LinkedImage child : fromImage->dependents) {
+        if (child.imageNum() == kMissingWeakLinkedImage)
+            continue;
+        if (child.kind() == Image::LinkKind::upward)
+            continue;
+        const BuilderLoadedImage& childLi = findLoadedImage(child.imageNum());
+        if ( childLi.loadAddress()->hasWeakDefs() ) {
+            WrappedMachO childWmo(childLi.loadAddress(), this, (void*)&childLi);
+            if ( childWmo.findSymbolIn(diag, symbolName, addend, target) )
+                return true;
+        }
+    }
+    return false;
+}
+
+void ClosureBuilder::mas_forEachImage(void (^handler)(const WrappedMachO& wmo, bool hidden, bool& stop)) const
+{
+    bool stop = false;
+    for (const ClosureBuilder::BuilderLoadedImage& li : _loadedImages) {
+        WrappedMachO wmo(li.loadAddress(), this, (void*)&li);
+        handler(wmo, li.rtldLocal, stop);
+        if ( stop )
+            break;
+    }
+}
+
+bool ClosureBuilder::wmo_missingSymbolResolver(const WrappedMachO* fromWmo, bool weakImport, bool lazyBind, const char* symbolName, const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const
+{
+    // if weakImport and missing, bind to NULL
+    if ( weakImport ) {
+        // construct NULL target
+        target.offsetInImage        = 0;
+        target.kind                 = FixupTarget::Kind::bindAbsolute;
+        target.requestedSymbolName  = symbolName;
+        target.foundSymbolName      = nullptr;
+        // Record that we found a missing weak import so that the objc optimizer doens't have to check
+        ClosureBuilder::BuilderLoadedImage* fromBLI = (ClosureBuilder::BuilderLoadedImage*)(fromWmo->_other);
+        fromBLI->hasMissingWeakImports = true;
+        return true;
+    }
+    // dyld3 binds everything ahead of time, to simulator lazy failure
+    // if non-weakImport and lazy, then bind to __dyld_missing_symbol_abort()
+    if ( lazyBind && _allowMissingLazies ) {
+        for (const BuilderLoadedImage& li : _loadedImages) {
+            if ( li.loadAddress()->isDylib() && (strcmp(li.loadAddress()->installName(), "/usr/lib/system/libdyld.dylib") == 0) ) {
+                WrappedMachO libdyldWmo(li.loadAddress(), this, (void*)&li);
+                Diagnostics  diag;
+                if ( libdyldWmo.findSymbolIn(diag, "__dyld_missing_symbol_abort", 0, target) ) {
+                     // <rdar://problem/44315944> closures should bind missing lazy-bind symbols to a missing symbol handler in libdyld in flat namespace
+                     return true;
+                }
+                break;
+            }
+        }
+    }
+    // support abort payload
+    if ( _launchErrorInfo != nullptr ) {
+         _launchErrorInfo->kind              = DYLD_EXIT_REASON_SYMBOL_MISSING;
+         _launchErrorInfo->clientOfDylibPath = strdup_temp(clientPath);
+         _launchErrorInfo->targetDylibPath   = strdup_temp(expectedInDylibPath);
+         _launchErrorInfo->symbol            = symbolName;
+    }
+    return false;
+}
+
+void ClosureBuilder::mas_mainExecutable(WrappedMachO& wmo) const
+{
+    const ClosureBuilder::BuilderLoadedImage& mainLi = _loadedImages[_mainProgLoadIndex];
+    WrappedMachO mainWmo(mainLi.loadAddress(), this, (void*)&mainLi);
+    wmo = mainWmo;
+}
+
+void* ClosureBuilder::mas_dyldCache() const
+{
+    return (void*)_dyldCache;
+}
+
+bool ClosureBuilder::wmo_dependent(const WrappedMachO* wmo, uint32_t depIndex, WrappedMachO& childWmo, bool& missingWeakDylib) const
+{
+    ClosureBuilder::BuilderLoadedImage* forImage = (ClosureBuilder::BuilderLoadedImage*)(wmo->_other);
+
+    if ( depIndex >= forImage->dependents.count() )
+        return false;
+
+    ImageNum childNum = forImage->dependents[depIndex].imageNum();
+    if ( childNum == kMissingWeakLinkedImage ) {
+        missingWeakDylib = true;
+        return true;
+    }
+    const BuilderLoadedImage& depLoadedImage = this->findLoadedImage(childNum);
+    childWmo = WrappedMachO(depLoadedImage.loadAddress(), this, (void*)&depLoadedImage);
+    missingWeakDylib = false;
+    return true;
+}
+
+const char* ClosureBuilder::wmo_path(const WrappedMachO* wmo) const
+{
+    ClosureBuilder::BuilderLoadedImage* forImage = (ClosureBuilder::BuilderLoadedImage*)(wmo->_other);
+    return forImage->loadedFileInfo.path;
+}
+
+MachOAnalyzerSet::ExportsTrie ClosureBuilder::wmo_getExportsTrie(const WrappedMachO* wmo) const
+{
+    ClosureBuilder::BuilderLoadedImage* forImage = (ClosureBuilder::BuilderLoadedImage*)(wmo->_other);
+    if ( forImage->exportsTrieOffset == 0 ) {
+        // if trie location not already cached, look it up
+        wmo->_mh->hasExportTrie(forImage->exportsTrieOffset, forImage->exportsTrieSize);
+    }
+    const uint8_t* start = nullptr;
+    const uint8_t* end   = nullptr;
+    if ( forImage->exportsTrieOffset != 0 ) {
+        start = (uint8_t*)wmo->_mh + forImage->exportsTrieOffset;
+        end   = start + forImage->exportsTrieSize;
+    }
+    return { start, end };
+}
+
+
+Image::ResolvedSymbolTarget ClosureBuilder::makeResolvedTarget(const FixupTarget& target) const
+{
+     Image::ResolvedSymbolTarget resolvedTarget;
+     switch ( target.kind ) {
+        case MachOAnalyzerSet::FixupTarget::Kind::rebase:
+            assert(0 && "target is a rebase");
+            break;
+        case MachOAnalyzerSet::FixupTarget::Kind::bindToImage:
+            if ( target.foundInImage._mh->inDyldCache() ) {
+                resolvedTarget.sharedCache.kind   = Image::ResolvedSymbolTarget::kindSharedCache;
+                resolvedTarget.sharedCache.offset = (uint8_t*)target.foundInImage._mh - (uint8_t*)_dyldCache + target.offsetInImage;
+            }
+            else {
+                ClosureBuilder::BuilderLoadedImage* targetBuildLoaderImage = (ClosureBuilder::BuilderLoadedImage*)(target.foundInImage._other);
+                resolvedTarget.image.kind     = Image::ResolvedSymbolTarget::kindImage;
+                resolvedTarget.image.imageNum = targetBuildLoaderImage->imageNum;
+                resolvedTarget.image.offset   = target.offsetInImage;
+            }
+            return resolvedTarget;
+        case MachOAnalyzerSet::FixupTarget::Kind::bindAbsolute:
+          resolvedTarget.absolute.kind   = Image::ResolvedSymbolTarget::kindAbsolute;
+          resolvedTarget.absolute.value  = target.offsetInImage;
+          return resolvedTarget;
+        case MachOAnalyzerSet::FixupTarget::Kind::bindMissingSymbol:
+            assert(0 && "unknown FixupTarget::Kind::bindMissingSymbol found in closure");
+            break;
+    }
+    assert(0 && "unknown FixupTarget kind");
+}
+
+void ClosureBuilder::addFixupInfo(ImageWriter& writer, BuilderLoadedImage& forImage)
+{
+    STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::RebasePattern, rebaseEntries, 1024);
+    STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::BindPattern, binds, 512);
+    __block RebasePatternBuilder rebaseBuilder(rebaseEntries, forImage.loadAddress()->pointerSize());
+    __block BindPatternBuilder bindBuilder(binds, forImage.loadAddress()->pointerSize());
+
+    const bool stompedLazyOpcodes = forImage.loadAddress()->hasStompedLazyOpcodes();
+    WrappedMachO forImage_wmo(forImage.loadAddress(), this, (void*)&forImage);
+    forImage_wmo.forEachFixup(_diag,
+        ^(uint64_t fixupLocRuntimeOffset, PointerMetaData pmd, const MachOAnalyzerSet::FixupTarget& target, bool& stop) {
+            if ( target.kind == MachOAnalyzerSet::FixupTarget::Kind::rebase ) {
+                // normally ignore rebase on lazy pointer because dyld3 will immediately bind that same pointer
+                // but if app is licensewared and stomps lazy bind opcodes, keep the rebases
+                if ( target.isLazyBindRebase && !stompedLazyOpcodes )
+                    return;
+            }
+            if ( _dylibFixupHandler ) {
+                // applying fixups to dylibs in dyld cache as the cache is being built
+                _dylibFixupHandler(forImage.loadAddress(), fixupLocRuntimeOffset, pmd, target);
+                return;
+            }
+            switch ( target.kind ) {
+                case MachOAnalyzerSet::FixupTarget::Kind::rebase:
+                    if ( !_leaveRebasesAsOpcodes )
+                        rebaseBuilder.add(fixupLocRuntimeOffset);
+                    break;
+                case MachOAnalyzerSet::FixupTarget::Kind::bindToImage:
+                case MachOAnalyzerSet::FixupTarget::Kind::bindAbsolute:
+                    bindBuilder.add(fixupLocRuntimeOffset, makeResolvedTarget(target), target.weakCoalesced);
+                    break;
+                case MachOAnalyzerSet::FixupTarget::Kind::bindMissingSymbol:
+                    // this is last call from forEachFixup() because a symbol could not be resolved
+                    break;
+            }
+        },
+        ^(uint32_t cachedDylibIndex, uint32_t exportCacheOffset, const FixupTarget& target) {
+            addWeakDefCachePatch(cachedDylibIndex, exportCacheOffset, target);
+        }
+    );
+
+    // check for __dyld section in main executable to support licenseware
+    if ( forImage.loadAddress()->filetype == MH_EXECUTE ) {
+        forImage.loadAddress()->forEachSection(^(const MachOAnalyzer::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+            if ( (strcmp(sectInfo.sectName, "__dyld") == 0) && (strcmp(sectInfo.segInfo.segName, "__DATA") == 0) ) {
+                // find dyld3::compatFuncLookup in libdyld.dylib
+                assert(_libDyldImageNum != 0);
+                const BuilderLoadedImage& libdyldImage = findLoadedImage(_libDyldImageNum);
+                WrappedMachO libdyldWmo(libdyldImage.loadAddress(), this, (void*)&libdyldImage);
+                FixupTarget libdyldCompatTarget;
+                if ( libdyldWmo.findSymbolIn(_diag, "__ZN5dyld316compatFuncLookupEPKcPPv", 0, libdyldCompatTarget) ) {
+                    // dyld_func_lookup is second pointer in __dyld section
+                    uint64_t fixupLocRuntimeOffset = sectInfo.sectAddr - forImage.loadAddress()->preferredLoadAddress() + forImage.loadAddress()->pointerSize();
+                    bindBuilder.add(fixupLocRuntimeOffset, makeResolvedTarget(libdyldCompatTarget), false);
+                }
+                else {
+                    _diag.error("libdyld.dylib is missing dyld3::compatFuncLookup");
+                }
+            }
+        });
+    }
+
+    // add all rebase and bind info into closure, unless building dyld cache
+    if ( !_makingDyldCacheImages ) {
+        if ( _leaveRebasesAsOpcodes )
+            writer.setRebasesNotEncoded();
+        else
+            writer.setRebaseInfo(rebaseEntries);
+       writer.setBindInfo(binds);
+    }
 
     // i386 programs also use text relocs to rebase stubs
-    if ( mh->cputype == CPU_TYPE_I386 ) {
+    if ( (forImage.loadAddress()->cputype == CPU_TYPE_I386) && !_makingDyldCacheImages ) {
         STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::TextFixupPattern, textRebases, 512);
         __block uint64_t lastOffset = -4;
-        mh->forEachTextRebase(_diag, ^(uint64_t runtimeOffset, bool& stop) {
+        forImage.loadAddress()->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;
@@ -1264,486 +1709,64 @@ void ClosureBuilder::addRebaseInfo(ImageWriter& writer, const MachOAnalyzer* mh)
         });
         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),
-                                                               void (^missingLazyBindHandler)())
-{
-    __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, bool lazyBind, 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, lazyBind, addend, target, targetInfo) ) {
-            if ( !targetInfo.skippableWeakDef ) {
-                handler(runtimeOffset, target, targetInfo, stop);
-                lastSymbolName = symbolName;
-                lastLibOrdinal = libOrdinal;
-                lastAddend     = addend;
-            }
-        }
-        else {
-            stop = true;
-        }
-    }, ^(const char* symbolName) {
-        strongHandler(symbolName);
-    }, ^() {
-        missingLazyBindHandler();
-    });
 }
 
-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, 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, 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);
-                        }
-                    }
-                }
-            }
-        }
-    }, ^() {
-        _foundMissingLazyBinds = true;
-    });
 
-    // check for __dyld section in main executable to support licenseware
-    if ( forImage.loadAddress()->filetype == MH_EXECUTE ) {
-        forImage.loadAddress()->forEachSection(^(const MachOAnalyzer::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
-            if ( (strcmp(sectInfo.sectName, "__dyld") == 0) && (strcmp(sectInfo.segInfo.segName, "__DATA") == 0) ) {
-                // find dyld3::compatFuncLookup in libdyld.dylib
-                assert(_libDyldImageNum != 0);
-                Image::ResolvedSymbolTarget lookupFuncTarget;
-                ResolvedTargetInfo          lookupFuncInfo;
-                if ( findSymbolInImage(findLoadedImage(_libDyldImageNum).loadAddress(), "__ZN5dyld316compatFuncLookupEPKcPPv", 0, false, false, lookupFuncTarget, lookupFuncInfo) ) {
-                    // add bind to set second pointer in __dyld section to be dyld3::compatFuncLookup
-                    uint64_t runtimeOffset = sectInfo.sectAddr - forImage.loadAddress()->preferredLoadAddress() + forImage.loadAddress()->pointerSize();
-                    Image::BindPattern compatFuncPattern;
-                    compatFuncPattern.target        = lookupFuncTarget;
-                    compatFuncPattern.startVmOffset = runtimeOffset;
-                    compatFuncPattern.repeatCount   = 1;
-                    compatFuncPattern.skipCount     = 0;
-                    assert(compatFuncPattern.startVmOffset == runtimeOffset);
-                    binds.push_back(compatFuncPattern);
-                }
-                else {
-                    _diag.error("libdyld.dylib is dyld3::compatFuncLookup");
-                }
-            }
-        });
-    }
 
-    writer.setBindInfo(binds);
-}
 
-void ClosureBuilder::reportRebasesAndBinds(ImageWriter& writer, BuilderLoadedImage& forImage)
+void ClosureBuilder::addWeakDefCachePatch(uint32_t cachedDylibIndex, uint32_t exportCacheOffset, const FixupTarget& patchTarget)
 {
-    // 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) {},
-    ^() { });
+    // minimal closures don't need weak def patches, they are regenerated at launch
+    if ( _makeMinimalClosure )
+        return;
 
-    // i386 programs also use text relocs to rebase stubs
-    if ( forImage.loadAddress()->cputype == CPU_TYPE_I386 ) {
-        // FIX ME
+    // don't add duplicates
+    for (const Closure::PatchEntry& aPatch : _weakDefCacheOverrides) {
+        if ( aPatch.exportCacheOffset == exportCacheOffset )
+            return;
     }
+    // add new patch entry
+    ClosureBuilder::BuilderLoadedImage* targetImage = (ClosureBuilder::BuilderLoadedImage*)(patchTarget.foundInImage._other);
+    Closure::PatchEntry patch;
+    patch.overriddenDylibInCache     = cachedDylibIndex+1; // convert image index to ImageNum
+    patch.exportCacheOffset          = exportCacheOffset;
+    patch.replacement.image.kind     = Image::ResolvedSymbolTarget::kindImage;
+    patch.replacement.image.imageNum = targetImage->imageNum;
+    patch.replacement.image.offset   = patchTarget.offsetInImage;
+    _weakDefCacheOverrides.push_back(patch);
 }
 
-// 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* const 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, BuilderLoadedImage& forImage)
 {
+    // as a side effect of building targets array, we discover if anything in dyld cache uses weak-defs that need
+    // to be redirected to an impl in some other dylib (cache patched)
+    auto patchAddr = ^(uint32_t cachedDylibIndex, uint32_t exportCacheOffset, const FixupTarget& patchTarget) {
+                        addWeakDefCachePatch(cachedDylibIndex, exportCacheOffset, patchTarget);
+                    };
+
     // 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, false, addend, target, targetInfo) ) {
+        FixupTarget target;
+        WrappedMachO forImageWmo(forImage.loadAddress(), this, (void*)&forImage);
+        if ( wmo_findSymbolFrom(&forImageWmo, _diag, libOrdinal, symbolName, weakImport, false, addend, patchAddr, target) )
+            targets.push_back(makeResolvedTarget(target));
+        else
             stop = true;
-            return;
-        }
-        if ( libOrdinal == BIND_SPECIAL_DYLIB_WEAK_LOOKUP ) {
-            // 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;
 
-    uint64_t chainStartsOffset = forImage.loadAddress()->chainStartsOffset();
-    if ( _handlers != nullptr ) {
-        forImage.loadAddress()->withChainStarts(_diag, chainStartsOffset, ^(const dyld_chained_starts_in_image* starts) {
-            _handlers->chainedBind(forImage.imageNum, forImage.loadAddress(), starts, targets, targetInfos);
-        });
-    }
-    else {
-        writer.setChainedFixups(chainStartsOffset, targets);
-    }
-
-    // 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, false, foundInfo, nullptr) ) {
-                _weakDefsFromChainedBinds.push_back(weakSymbolName);
-            }
-        }
-    }
-}
-
-
-bool ClosureBuilder::findSymbolInImage(const MachOAnalyzer* macho, const char* symbolName, uint64_t addend, bool followReExports,
-                                       bool weakImport, Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo)
-{
-    targetInfo.foundInDylib        = nullptr;
-    targetInfo.requestedSymbolName = symbolName;
-    targetInfo.addend              = addend;
-    targetInfo.weakBindCoalese     = false;
-    targetInfo.weakBindSameImage   = false;
-    targetInfo.isWeakDef           = false;
-    targetInfo.skippableWeakDef    = 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, weakImport, 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() ) {
-            uint64_t offsetValue = (uint8_t*)impDylib - (uint8_t*)_dyldCache + foundInfo.value + addend;
-            target.sharedCache.kind   = Image::ResolvedSymbolTarget::kindSharedCache;
-            target.sharedCache.offset = offsetValue;
-            assert(target.sharedCache.offset == offsetValue);
-        }
-        else {
-            uint64_t offsetValue = foundInfo.value + addend;
-            target.image.kind     = Image::ResolvedSymbolTarget::kindImage;
-            target.image.imageNum = findLoadedImage(impDylib).imageNum;
-            target.image.offset   = offsetValue;
-            assert(target.image.offset == offsetValue);
-        }
-        return true;
-    }
-    return false;
-}
-
-bool ClosureBuilder::findSymbol(BuilderLoadedImage& fromImage, int libOrdinal, const char* symbolName, bool weakImport, bool lazyBind,
-                                uint64_t addend, Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo)
-{
-    target.raw                      = 0;
-    targetInfo.weakBindCoalese      = false;
-    targetInfo.weakBindSameImage    = false;
-    targetInfo.isWeakDef            = false;
-    targetInfo.skippableWeakDef     = false;
-    targetInfo.requestedSymbolName  = symbolName;
-    targetInfo.foundSymbolName      = nullptr;
-    targetInfo.libOrdinal           = libOrdinal;
-    if ( libOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) {
-        for (const BuilderLoadedImage& li : _loadedImages) {
-            if ( !li.rtldLocal && findSymbolInImage(li.loadAddress(), symbolName, addend, true, weakImport, target, targetInfo) )
-                return true;
-        }
-        if ( weakImport ) {
-            target.absolute.kind  = Image::ResolvedSymbolTarget::kindAbsolute;
-            target.absolute.value = 0;
-            // Record that we found a missing weak import so that the objc optimizer doens't have to check
-            fromImage.hasMissingWeakImports = true;
-            return true;
-        }
-        // <rdar://problem/44315944> closures should bind missing lazy-bind symbols to a missing symbol handler in libdyld in flat namespace
-        if ( lazyBind && _allowMissingLazies ) {
-            if ( findMissingSymbolHandler(target, targetInfo) )
-                return true;
-        }
-        _diag.error("symbol '%s' not found, expected in flat namespace by '%s'", symbolName, fromImage.path());
-    }
-    else if ( libOrdinal == BIND_SPECIAL_DYLIB_WEAK_LOOKUP ) {
-        // 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;
-
-        if ( _makingDyldCacheImages ) {
-            // _loadedImages is all dylibs in the dyld cache, it is not load-order, so need alterate weak-def binding algorithm
-            // look first in /usr/lib/libc++, most will be here
-            for (const BuilderLoadedImage& li : _loadedImages) {
-                if ( li.loadAddress()->hasWeakDefs() && (strncmp(li.path(), "/usr/lib/libc++", 15) == 0) ) {
-                   if ( findSymbolInImage(li.loadAddress(), symbolName, addend, false, weakImport, target, targetInfo) ) {
-                        foundImpl = true;
-                        break;
-                    }
-                }
-            }
-            // if not found, try looking in the images itself, most custom weak-def symbols have a copy in the image itself
-            if ( !foundImpl ) {
-                if ( findSymbolInImage(fromImage.loadAddress(), symbolName, addend, false, weakImport, target, targetInfo) ) {
-                    foundImpl = true;
-                }
-            }
-            // if still not found, then this is the rare case of a simple use of a weak-def symbol
-            if ( !foundImpl ) {
-                // look in all direct dependents
-                for (Image::LinkedImage child : fromImage.dependents) {
-                    if (child.imageNum() == kMissingWeakLinkedImage)
-                        continue;
-                    BuilderLoadedImage& childLi = findLoadedImage(child.imageNum());
-                    if ( childLi.loadAddress()->hasWeakDefs() && findSymbolInImage(childLi.loadAddress(), symbolName, addend, false, weakImport, target, targetInfo) ) {
-                        foundImpl = true;
-                        break;
-                    }
-                }
-            }
-            targetInfo.weakBindCoalese = true;
-        }
-        else {
-            // walk images in load-order to find first that implements this symbol
-            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, weakImport, 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 && 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, weakImport, 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;
-        if ( weakImport ) {
-            target.absolute.kind  = Image::ResolvedSymbolTarget::kindAbsolute;
-            target.absolute.value = 0;
-            return true;
-        }
-        if ( ! fromImage.loadAddress()->hasChainedFixups() ) {
-            // support old binaries where symbols have been stripped and have weak_bind to itself
-            targetInfo.skippableWeakDef = true;
-            return true;
-        }
-
-        _diag.error("symbol '%s' not found, expected to be weak-def coalesced by '%s'", symbolName, fromImage.path());
-    }
-    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, weakImport, target, targetInfo) )
-                return true;
-        }
-
-        if ( weakImport ) {
-            target.absolute.kind  = Image::ResolvedSymbolTarget::kindAbsolute;
-            target.absolute.value = 0;
-            // Record that we found a missing weak import so that the objc optimizer doens't have to check
-            fromImage.hasMissingWeakImports = true;
-            return true;
-        }
-
-        // <rdar://problem/43315403> closures should bind missing lazy-bind symbols to a missing symbol handler in libdyld
-        if ( lazyBind && _allowMissingLazies ) {
-            if ( findMissingSymbolHandler(target, targetInfo) )
-                return true;
-        }
-
-        // symbol not found and not weak or lazy so error out
-        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 = strdup_temp(fromImage.path());
-            _launchErrorInfo->targetDylibPath   = strdup_temp(expectedInPath);
-            _launchErrorInfo->symbol            = symbolName;
-        }
+    // C++ main executables can overide operator new, check for that
+    if ( forImage.loadAddress()->isMainExecutable() && forImage.loadAddress()->hasWeakDefs() ) {
+        WrappedMachO mainWmo(forImage.loadAddress(), this, (void*)&forImage);
+        wmo_findExtraSymbolFrom(&mainWmo, patchAddr);
     }
-    return false;
-}
 
-
-bool ClosureBuilder::findMissingSymbolHandler(Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo)
-{
-   for (BuilderLoadedImage& li : _loadedImages) {
-         if ( li.loadAddress()->isDylib() && (strcmp(li.loadAddress()->installName(), "/usr/lib/system/libdyld.dylib") == 0) ) {
-            if ( findSymbolInImage(li.loadAddress(), "__dyld_missing_symbol_abort", 0, false, false, target, targetInfo) ) {
-                return true;
-            }
-            break;
-        }
-    }
-    return false;
-}
+    uint64_t chainStartsOffset = forImage.loadAddress()->chainStartsOffset();
+    writer.setChainedFixups(chainStartsOffset, targets);
+ }
 
 void ClosureBuilder::depthFirstRecurseSetInitInfo(uint32_t loadIndex, InitInfo initInfos[], uint32_t& initOrder, bool& hasError)
 {
@@ -1756,11 +1779,10 @@ void ClosureBuilder::depthFirstRecurseSetInitInfo(uint32_t loadIndex, InitInfo i
         hasError = true;
         return;
     }
-
     for (const Image::LinkedImage& dep : _loadedImages[loadIndex].dependents) {
         if ( dep.imageNum() == kMissingWeakLinkedImage )
             continue;
-        ClosureBuilder::BuilderLoadedImage& depLi = findLoadedImage(dep.imageNum());
+        const ClosureBuilder::BuilderLoadedImage& depLi = findLoadedImage(dep.imageNum());
         uint32_t depLoadIndex = (uint32_t)_loadedImages.index(depLi);
         if ( dep.kind() == Image::LinkKind::upward ) {
             if ( !initInfos[depLoadIndex].visited )
@@ -1843,20 +1865,19 @@ void ClosureBuilder::addClosureInfo(LaunchClosureWriter& closureWriter)
 
     // record which is libdyld
     assert(_libDyldImageNum != 0);
-    Image::ResolvedSymbolTarget entryLocation;
-    ResolvedTargetInfo          entryInfo;
-    if ( findSymbolInImage(findLoadedImage(_libDyldImageNum).loadAddress(), "__ZN5dyld318entryVectorForDyldE", 0, false, false, entryLocation, entryInfo) ) {
+    const BuilderLoadedImage& libdyldImage = findLoadedImage(_libDyldImageNum);
+    WrappedMachO libdyldWmo(libdyldImage.loadAddress(), this, (void*)&libdyldImage);
+    FixupTarget libdyldEntryTarget;
+    if ( libdyldWmo.findSymbolIn(_diag, "__ZN5dyld318entryVectorForDyldE", 0, libdyldEntryTarget) ) {
         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 ( libdyldEntryTarget.kind == MachOAnalyzerSet::FixupTarget::Kind::bindToImage ) {
+            libDyldEntry = (dyld3::LibDyldEntryVector*)((uint8_t*)libdyldEntryTarget.foundInImage._mh + libdyldEntryTarget.offsetInImage);
         }
-        if ( (libDyldEntry != nullptr) && ((libDyldEntry->binaryFormatVersion & LibDyldEntryVector::kBinaryFormatVersionMask) == dyld3::closure::kFormatVersion) )
+        // peak at entry vector to see if version is compatible
+        if ( (libDyldEntry != nullptr) && ((libDyldEntry->binaryFormatVersion & LibDyldEntryVector::kBinaryFormatVersionMask) == dyld3::closure::kFormatVersion) ) {
+            Image::ResolvedSymbolTarget entryLocation = makeResolvedTarget(libdyldEntryTarget);
             closureWriter.setLibDyldEntry(entryLocation);
+        }
         else
             _diag.error("libdyld.dylib entry vector is incompatible");
     }
@@ -1869,13 +1890,13 @@ void ClosureBuilder::addClosureInfo(LaunchClosureWriter& closureWriter)
     closureWriter.setTopImageNum(mainProgImageNum);
 
     // add entry
-    uint32_t    entryOffset;
+    uint64_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;
+        location.image.offset   = (uint32_t)entryOffset;
         if ( usesCRT )
             closureWriter.setStartEntry(location);
         else
@@ -1913,7 +1934,7 @@ void ClosureBuilder::invalidateInitializerRoots()
             for (Image::LinkedImage depIndex : li.dependents) {
                 if ( depIndex.imageNum() == kMissingWeakLinkedImage )
                     continue;
-                BuilderLoadedImage& depImage = findLoadedImage(depIndex.imageNum());
+                const BuilderLoadedImage& depImage = findLoadedImage(depIndex.imageNum());
                 // If a dependent is bad, or a new image num, or an override, then we need this image to get a new closure
                 if ( depImage.mustBuildClosure ) {
                     li.mustBuildClosure = true;    // mark bad
@@ -1939,6 +1960,7 @@ bool ClosureBuilder::EqualCString::equal(const char* s1, const char* s2) {
 }
 
 
+
 struct HashUInt64 {
     static size_t hash(const uint64_t& v) {
         return std::hash<uint64_t>{}(v);
@@ -2047,6 +2069,11 @@ bool ClosureBuilder::optimizeObjC(Array<ImageWriter>& writers) {
     Image::ResolvedSymbolTarget objcProtocolClassTarget;
     objcProtocolClassTarget.sharedCache.kind = Image::ResolvedSymbolTarget::kindSharedCache;
     if ( _dyldCacheIsLive ) {
+        // If we are on arm64e, the protocol ISA in the shared cache was signed.  We don't
+        // want the signature bits in the encoded value
+#if __has_feature(ptrauth_calls)
+        classProtocolVMAddr = (uint64_t)__builtin_ptrauth_strip((void*)classProtocolVMAddr, ptrauth_key_asda);
+#endif
         objcProtocolClassTarget.sharedCache.offset = classProtocolVMAddr - (uint64_t)_dyldCache;
     } else {
         objcProtocolClassTarget.sharedCache.offset = classProtocolVMAddr - _dyldCache->unslidLoadAddress();
@@ -2095,8 +2122,9 @@ bool ClosureBuilder::optimizeObjC(Array<ImageWriter>& writers) {
                     // We've tested the 64-bit chained fixups.
                     break;
                 case DYLD_CHAINED_PTR_64_OFFSET:
-                case DYLD_CHAINED_PTR_ARM64E_OFFSET:
                 case DYLD_CHAINED_PTR_ARM64E_USERLAND:
+                case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
+                case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
                     // FIXME: Test 64-bit offset chained fixups then enable this.
                     continue;
                 case DYLD_CHAINED_PTR_32:
@@ -2129,12 +2157,17 @@ bool ClosureBuilder::optimizeObjC(Array<ImageWriter>& writers) {
         image.objcImageInfoVMOffset = (uint64_t)objcImageInfo - (uint64_t)ma;
     }
 
+    // objc supports a linker set which is a magic section of duplicate objc classes to ignore
+    // We need to match that behaviour
+    Map<const char*, bool, HashCString, EqualCString> duplicateClassesToIgnore;
+    parseObjCClassDuplicates(duplicateClassesToIgnore);
+
     OverflowSafeArray<const char*>                                                          closureSelectorStrings;
     Map<const char*, dyld3::closure::Image::ObjCImageOffset, HashCString, EqualCString>     closureSelectorMap;
     OverflowSafeArray<const char*>                                                          closureDuplicateSharedCacheClassNames;
     Map<const char*, dyld3::closure::Image::ObjCDuplicateClass, HashCString, EqualCString>  closureDuplicateSharedCacheClassMap;
     for (ObjCOptimizerImage& image : objcImages) {
-        optimizeObjCClasses(objcClassOpt, sharedCacheImagesMap, closureDuplicateSharedCacheClassMap, image);
+        optimizeObjCClasses(objcClassOpt, sharedCacheImagesMap, closureDuplicateSharedCacheClassMap, duplicateClassesToIgnore, image);
         if (image.diag.hasError())
             continue;
 
@@ -2333,8 +2366,43 @@ bool ClosureBuilder::optimizeObjC(Array<ImageWriter>& writers) {
         }
 
         // Method list fixups
-        // TODO: Implement this
         STACK_ALLOC_OVERFLOW_SAFE_ARRAY(Image::MethodListFixup, methodListFixups, 512);
+        if ( !image.methodListFixups.empty() ) {
+
+            __block uint64_t lastOffset = -pointerSize;
+            for (uint64_t runtimeOffset : image.methodListFixups) {
+                bool mergedIntoPrevious = false;
+                if ( (runtimeOffset > lastOffset) && !methodListFixups.empty() ) {
+                    uint64_t skipAmount = (runtimeOffset - lastOffset - pointerSize)/pointerSize;
+                    if ( skipAmount*pointerSize != (runtimeOffset - lastOffset - pointerSize) ) {
+                        // misaligned pointer means we cannot optimize
+                    }
+                    else {
+                        if ( (methodListFixups.back().repeatCount == 1) && (methodListFixups.back().skipCount == 0) && (skipAmount <= 255) ) {
+                            methodListFixups.back().repeatCount = 2;
+                            methodListFixups.back().skipCount   = skipAmount;
+                            assert(methodListFixups.back().skipCount == skipAmount); // check overflow
+                            mergedIntoPrevious       = true;
+                        }
+                        else if ( (methodListFixups.back().skipCount == skipAmount) && (methodListFixups.back().repeatCount < 0xfff) ) {
+                            uint32_t prevRepeatCount = methodListFixups.back().repeatCount;
+                            methodListFixups.back().repeatCount += 1;
+                            assert(methodListFixups.back().repeatCount > prevRepeatCount); // check overflow
+                            mergedIntoPrevious       = true;
+                        }
+                    }
+                }
+                if ( !mergedIntoPrevious ) {
+                    Image::MethodListFixup pattern;
+                    pattern.startVmOffset = runtimeOffset;
+                    pattern.repeatCount   = 1;
+                    pattern.skipCount     = 0;
+                    assert(pattern.startVmOffset == runtimeOffset);
+                    methodListFixups.push_back(pattern);
+                }
+                lastOffset = runtimeOffset;
+            }
+        }
 
         image.writer->setObjCFixupInfo(objcProtocolClassTarget, image.objcImageInfoVMOffset, protocolFixups,
                                        selRefFixups, stableSwiftFixups, methodListFixups);
@@ -2352,6 +2420,7 @@ void ClosureBuilder::optimizeObjCSelectors(const objc_opt::objc_selopt_t* objcSe
     const dyld3::MachOAnalyzer* ma = li.loadAddress();
     uint32_t pointerSize = ma->pointerSize();
     const uint64_t loadAddress = ma->preferredLoadAddress();
+    const dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = ma->makeVMAddrConverter(li.contentRebased);
 
     // The legacy (objc1) codebase uses a bunch of sections we don't want to reason about.  If we see them just give up.
     __block bool foundBadSection = false;
@@ -2383,9 +2452,20 @@ void ClosureBuilder::optimizeObjCSelectors(const objc_opt::objc_selopt_t* objcSe
 
     uint32_t sharedCacheSentinelIndex = objcSelOpt->getSentinelIndex();
 
+    // Track the locations where we've updated selector references.  With relative method lists,
+    // we share selref slots across classes, categories, protocols, and SEL() expressions, so we may
+    // visit a location more than once
+    __block Map<uint64_t, bool, HashUInt64, EqualUInt64> seenSelectorReferenceImageOffsets;
+
     auto visitReferenceToObjCSelector = ^void(uint64_t selectorStringVMAddr, uint64_t selectorReferenceVMAddr) {
 
         uint64_t selectorUseImageOffset = selectorReferenceVMAddr - loadAddress;
+        auto selUseItAndInserted = seenSelectorReferenceImageOffsets.insert({ selectorUseImageOffset, true });
+        if ( !selUseItAndInserted.second ) {
+            // If we didn't insert the selector reference, then its already there so we should skip it
+            return;
+        }
+
         if ( (selectorUseImageOffset & 3) != 0 ) {
             image.diag.error("Unaligned selector reference fixup");
             return;
@@ -2505,49 +2585,56 @@ void ClosureBuilder::optimizeObjCSelectors(const objc_opt::objc_selopt_t* objcSe
         visitReferenceToObjCSelector(method.nameVMAddr, method.nameLocationVMAddr);
     };
 
+    auto visitMethodList = ^(uint64_t methodListVMAddr) {
+        if ( methodListVMAddr == 0 )
+            return;
+        bool isRelativeMethodList = false;
+        ma->forEachObjCMethod(methodListVMAddr, vmAddrConverter, visitMethod, &isRelativeMethodList);
+        if (image.diag.hasError())
+            return;
+        // Record the offset to the method list so that we can mark it as being uniqued
+        // We can only do this if we have a pointer based method list as relative method lists are
+        // in read-only memory
+        if ( !isRelativeMethodList )
+            image.methodListFixups.push_back(methodListVMAddr - loadAddress);
+    };
+
     auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
                         uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
                         const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
-        ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), li.contentRebased,
-                              visitMethod);
+        visitMethodList(objcClass.baseMethodsVMAddr(pointerSize));
     };
 
     auto visitCategory = ^(Diagnostics& diag, uint64_t categoryVMAddr,
                            const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) {
-        ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, li.contentRebased,
-                              visitMethod);
-        ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, li.contentRebased,
-                              visitMethod);
+        visitMethodList(objcCategory.instanceMethodsVMAddr);
+        visitMethodList(objcCategory.classMethodsVMAddr);
     };
     auto visitProtocol = ^(Diagnostics& diag, uint64_t protocolVMAddr,
                            const dyld3::MachOAnalyzer::ObjCProtocol& objCProtocol) {
-        ma->forEachObjCMethod(objCProtocol.instanceMethodsVMAddr, li.contentRebased,
-                              visitMethod);
-        ma->forEachObjCMethod(objCProtocol.classMethodsVMAddr, li.contentRebased,
-                              visitMethod);
-        ma->forEachObjCMethod(objCProtocol.optionalInstanceMethodsVMAddr, li.contentRebased,
-                              visitMethod);
-        ma->forEachObjCMethod(objCProtocol.optionalClassMethodsVMAddr, li.contentRebased,
-                              visitMethod);
+        visitMethodList(objCProtocol.instanceMethodsVMAddr);
+        visitMethodList(objCProtocol.classMethodsVMAddr);
+        visitMethodList(objCProtocol.optionalInstanceMethodsVMAddr);
+        visitMethodList(objCProtocol.optionalClassMethodsVMAddr);
     };
 
     // Walk the class list
-    ma->forEachObjCClass(image.diag, li.contentRebased, visitClass);
+    ma->forEachObjCClass(image.diag, vmAddrConverter, visitClass);
     if (image.diag.hasError())
         return;
 
     // Walk the category list
-    ma->forEachObjCCategory(image.diag, li.contentRebased, visitCategory);
+    ma->forEachObjCCategory(image.diag, vmAddrConverter, visitCategory);
     if (image.diag.hasError())
         return;
 
     // Walk the protocol list
-    ma->forEachObjCProtocol(image.diag, li.contentRebased, visitProtocol);
+    ma->forEachObjCProtocol(image.diag, vmAddrConverter, visitProtocol);
     if (image.diag.hasError())
         return;
 
     // Visit the selector refs
-    ma->forEachObjCSelectorReference(image.diag, li.contentRebased, ^(uint64_t selRefVMAddr, uint64_t selRefTargetVMAddr) {
+    ma->forEachObjCSelectorReference(image.diag, vmAddrConverter, ^(uint64_t selRefVMAddr, uint64_t selRefTargetVMAddr) {
         visitReferenceToObjCSelector(selRefTargetVMAddr, selRefVMAddr);
     });
     if (image.diag.hasError())
@@ -2605,6 +2692,7 @@ void ClosureBuilder::addDuplicateObjCClassWarning(const char* className,
 void ClosureBuilder::optimizeObjCClasses(const objc_opt::objc_clsopt_t* objcClassOpt,
                                          const Map<const dyld3::MachOAnalyzer*, bool, HashPointer, EqualPointer>& sharedCacheImagesMap,
                                          const Map<const char*, dyld3::closure::Image::ObjCDuplicateClass, HashCString, EqualCString>& duplicateSharedCacheClasses,
+                                         const Map<const char*, bool, HashCString, EqualCString>& duplicateClassesToIgnore,
                                          ObjCOptimizerImage& image) {
 
     BuilderLoadedImage& li = *image.loadedImage;
@@ -2613,15 +2701,15 @@ void ClosureBuilder::optimizeObjCClasses(const objc_opt::objc_clsopt_t* objcClas
     const dyld3::MachOAnalyzer* ma = li.loadAddress();
     const uint32_t pointerSize = ma->pointerSize();
     const uint64_t loadAddress = ma->preferredLoadAddress();
+    const dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = ma->makeVMAddrConverter(li.contentRebased);
 
     // Keep track of any missing weak imports so that we can tell if the superclasses are nil
     // This is necessary as the shared cache will be marked with 'no missing weak superclasses'
     // and so we need to continue to satisfy that constraint
     __block Map<uint64_t, bool, HashUInt64, EqualUInt64> missingWeakImportOffets;
     if (li.hasMissingWeakImports) {
-        if (ma->hasChainedFixups()) {
-            const Image* closureImage = image.writer->currentImage();
-
+        const Image* closureImage = image.writer->currentImage();
+        if ( closureImage->hasChainedFixups() ) {
             const Array<Image::ResolvedSymbolTarget> targets = closureImage->chainedTargets();
             if ( !targets.empty() ) {
                 ma->withChainStarts(_diag, closureImage->chainedStartsOffset(), ^(const dyld_chained_starts_in_image* startsInfo) {
@@ -2629,7 +2717,8 @@ void ClosureBuilder::optimizeObjCClasses(const objc_opt::objc_clsopt_t* objcClas
                                                                             const dyld_chained_starts_in_segment* segInfo, bool& fixupsStop) {
                         uint64_t fixupOffset = (uint8_t*)fixupLoc - (uint8_t*)ma;
                         uint32_t bindOrdinal;
-                        if ( fixupLoc->isBind(segInfo->pointer_format, bindOrdinal) ) {
+                        int64_t  addend;
+                        if ( fixupLoc->isBind(segInfo->pointer_format, bindOrdinal, addend) ) {
                             if ( bindOrdinal < targets.count() ) {
                                 const Image::ResolvedSymbolTarget& target = targets[bindOrdinal];
                                 if ( (target.absolute.kind == Image::ResolvedSymbolTarget::kindAbsolute) && (target.absolute.value == 0) )
@@ -2645,12 +2734,12 @@ void ClosureBuilder::optimizeObjCClasses(const objc_opt::objc_clsopt_t* objcClas
                 if (image.diag.hasError())
                     return;
             }
-        } else {
-            forEachBind(li, ^(uint64_t runtimeOffset, Image::ResolvedSymbolTarget target, const ResolvedTargetInfo& targetInfo, bool& stop) {
-                if ( (target.absolute.kind == Image::ResolvedSymbolTarget::kindAbsolute) && (target.absolute.value == 0) )
-                    missingWeakImportOffets[runtimeOffset] = true;
-            }, ^(const char *strongSymbolName) {
-            }, ^() { });
+        }
+        else {
+            closureImage->forEachBind(^(uint64_t imageOffsetToBind, Image::ResolvedSymbolTarget bindTarget, bool &stop) {
+                if ( (bindTarget.absolute.kind == Image::ResolvedSymbolTarget::kindAbsolute) && (bindTarget.absolute.value == 0) )
+                    missingWeakImportOffets[imageOffsetToBind] = true;
+            });
         }
     }
 
@@ -2658,9 +2747,9 @@ void ClosureBuilder::optimizeObjCClasses(const objc_opt::objc_clsopt_t* objcClas
     __block MachOAnalyzer::SectionCache classNameSectionCache(ma);
     __block MachOAnalyzer::SectionCache classSectionCache(ma);
 
-    ma->forEachObjCClass(image.diag, li.contentRebased, ^(Diagnostics &diag, uint64_t classVMAddr,
-                                                          uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
-                                                          const MachOAnalyzer::ObjCClassInfo &objcClass, bool isMetaClass) {
+    ma->forEachObjCClass(image.diag, vmAddrConverter, ^(Diagnostics &diag, uint64_t classVMAddr,
+                                                        uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
+                                                        const MachOAnalyzer::ObjCClassInfo &objcClass, bool isMetaClass) {
         if (isMetaClass) return;
 
         // Make sure the superclass pointer is not nil
@@ -2718,7 +2807,8 @@ void ClosureBuilder::optimizeObjCClasses(const objc_opt::objc_clsopt_t* objcClas
                 // exactly one matching class.  Check if its loaded
                 const dyld3::MachOAnalyzer* sharedCacheMA = getMachHeaderFromObjCHeaderInfo(hi, pointerSize);
                 if (sharedCacheImagesMap.find(sharedCacheMA) != sharedCacheImagesMap.end()) {
-                    addDuplicateObjCClassWarning(className, li.path(), sharedCacheMA->installName());
+                    if ( duplicateClassesToIgnore.find(className) == duplicateClassesToIgnore.end() )
+                        addDuplicateObjCClassWarning(className, li.path(), sharedCacheMA->installName());
 
                     // We have a duplicate class, so check if we've already got it in our map.
                     if ( duplicateSharedCacheClasses.find(className) == duplicateSharedCacheClasses.end() ) {
@@ -2738,7 +2828,8 @@ void ClosureBuilder::optimizeObjCClasses(const objc_opt::objc_clsopt_t* objcClas
                 for (uint32_t i = 0; i < count; i++) {
                     const dyld3::MachOAnalyzer* sharedCacheMA = getMachHeaderFromObjCHeaderInfo(hilist[i], pointerSize);
                     if (sharedCacheImagesMap.find(sharedCacheMA) != sharedCacheImagesMap.end()) {
-                        addDuplicateObjCClassWarning(className, li.path(), sharedCacheMA->installName());
+                        if ( duplicateClassesToIgnore.find(className) == duplicateClassesToIgnore.end() )
+                            addDuplicateObjCClassWarning(className, li.path(), sharedCacheMA->installName());
 
                         // We have a duplicate class, so check if we've already got it in our map.
                         if ( duplicateSharedCacheClasses.find(className) == duplicateSharedCacheClasses.end() ) {
@@ -2826,18 +2917,14 @@ void ClosureBuilder::optimizeObjCProtocols(const objc_opt::objc_protocolopt2_t*
     const dyld3::MachOAnalyzer* ma = li.loadAddress();
     const uint32_t pointerSize = ma->pointerSize();
     const uint64_t loadAddress = ma->preferredLoadAddress();
+    const dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = ma->makeVMAddrConverter(li.contentRebased);
 
     // Protocol names and data may be in different sections depending on swift vs objc so handle multiple sections
     __block MachOAnalyzer::SectionCache protocolNameSectionCache(ma);
     __block MachOAnalyzer::SectionCache protocolSectionCache(ma);
 
-    ma->forEachObjCProtocol(image.diag, li.contentRebased, ^(Diagnostics &diag, uint64_t protocolVMAddr,
-                                                             const dyld3::MachOAnalyzer::ObjCProtocol &objCProtocol) {
-        if ( objCProtocol.requiresObjCReallocation ) {
-            // We can't optimize this protocol as the runtime needs all fields to be present
-            diag.error("Protocol is too small to be optimized");
-            return;
-        }
+    ma->forEachObjCProtocol(image.diag, vmAddrConverter, ^(Diagnostics &diag, uint64_t protocolVMAddr,
+                                                           const dyld3::MachOAnalyzer::ObjCProtocol &objCProtocol) {
         if ( objCProtocol.isaVMAddr != 0 ) {
             // We can't optimize this protocol if it has an ISA as we want to override it
             diag.error("Protocol ISA cannot be non-zero");
@@ -2956,6 +3043,40 @@ void ClosureBuilder::optimizeObjCProtocols(const objc_opt::objc_protocolopt2_t*
     });
 }
 
+void ClosureBuilder::parseObjCClassDuplicates(Map<const char*, bool, HashCString, EqualCString>& duplicateClassesToIgnore) {
+    const ClosureBuilder::BuilderLoadedImage& mainLi = _loadedImages[_mainProgLoadIndex];
+
+    const dyld3::MachOAnalyzer* ma = mainLi.loadAddress();
+
+    const uint32_t pointerSize = ma->pointerSize();
+    const intptr_t slide = ma->getSlide();
+    const dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = ma->makeVMAddrConverter(mainLi.contentRebased);
+
+    uint64_t sectionSize = 0;
+    const void* section = ma->findSectionContent("__DATA", "__objc_dupclass", sectionSize);
+
+    if ( !section )
+        return;
+
+    // Ignore sections which are the wrong size
+    if ( (sectionSize % pointerSize) != 0 )
+        return;
+
+    // Copied from objc-abi.h
+    typedef struct _objc_duplicate_class {
+        uint32_t version;
+        uint32_t flags;
+        const char name[64];
+    } objc_duplicate_class;
+
+    for (uint64_t offset = 0; offset != sectionSize; offset += pointerSize) {
+        uint64_t vmAddr = *(uint64_t*)((uint64_t)section + offset);
+        vmAddr = vmAddrConverter.convertToVMAddr(vmAddr);
+        const objc_duplicate_class* duplicateClass = (const objc_duplicate_class*)(vmAddr + slide);
+        duplicateClassesToIgnore.insert({ duplicateClass->name, true });
+    }
+}
+
 // used at launch by dyld when kernel has already mapped main executable
 const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fileInfo, bool allowInsertFailures)
 {
@@ -2966,12 +3087,10 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fil
     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, _archs, _platform);
@@ -2981,9 +3100,36 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fil
         _diag.error("not a main executable");
         return nullptr;
     }
+    if ( _platform == Platform::macOS ) {
+        // If this is an iOSMac program running on macOS, switch platforms
+        if ( mainExecutable->builtForPlatform(Platform::iOSMac, true) ) {
+            //_platform = Platform::iOSMac;
+            Platform* selfPlatform = const_cast<Platform*>(&_platform);
+            *selfPlatform = Platform::iOSMac;
+        }
+#if (TARGET_OS_OSX && TARGET_CPU_ARM64)
+        else if ( mainExecutable->builtForPlatform(Platform::iOS, true) ) {
+            //_platform = Platform::iOS;
+            Platform* selfPlatform = const_cast<Platform*>(&_platform);
+            *selfPlatform = Platform::iOS;
+        }
+#endif
+        if ( mainExecutable->usesObjCGarbageCollection() ) {
+            _diag.error("program requires ObjC Garbage Collection which is no longer supported");
+            return nullptr;
+        }
+    }
+    // <rdar://problem/63308841> licenseware apps that zero out lazy bind opcodes cannot be pre-bound
+    if ( mainExecutable->hasStompedLazyOpcodes() )
+        _makeMinimalClosure = true;
+
     _isLaunchClosure    = true;
     _allowMissingLazies = true;
 
+#if BUILDING_CACHE_BUILDER
+    _makingClosuresInCache = true;
+#endif
+
     _nextIndex = 0;
 
     // add main executable
@@ -2998,7 +3144,10 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fil
     mainEntry.isBadImage                = false;
     mainEntry.mustBuildClosure          = true;
     mainEntry.hasMissingWeakImports     = false;
+    mainEntry.hasInterposingTuples      = false;    // only dylibs not in the dyld cache can have interposing tuples
     mainEntry.overrideImageNum          = 0;
+    mainEntry.exportsTrieOffset         = 0;
+    mainEntry.exportsTrieSize           = 0;
 
     // Set the executable load path so that @executable_path can use it later
     _mainProgLoadPath = fileInfo.path;
@@ -3010,7 +3159,8 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fil
         if ( !findImage(dylibPath, chainMain, foundTopImage, LinkageType::kInserted, 0, true) ) {
             if ( !allowInsertFailures ) {
                 if ( _diag.noError() )
-                    _diag.error("could not load inserted dylib %s", dylibPath);
+                    // if no other error was reported while trying to find the library, that means it is missing
+                    _diag.error("could not load inserted dylib '%s' because image not found", dylibPath);
                 stop = true;
                 return;
             }
@@ -3044,10 +3194,22 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fil
 
     // now that everything loaded, set _libDyldImageNum and _libSystemImageNum
     for (BuilderLoadedImage& li : _loadedImages) {
-        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;
+        if ( mainExecutable->builtForPlatform(Platform::driverKit) ) {
+            if ( li.loadAddress()->isDylib() && (strcmp(li.loadAddress()->installName(), "/System/DriverKit/usr/lib/system/libdyld.dylib") == 0) )
+                _libDyldImageNum = li.imageNum;
+            else if ( strcmp(li.path(), "/System/DriverKit/usr/lib/libSystem.dylib") == 0 )
+                _libSystemImageNum = li.imageNum;
+        } else {
+            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;
+        }
+        // don't use minimal closures when interposing is in play because we don't have runtime support to do interposing
+        if ( li.hasInterposingTuples ) {
+            _makeMinimalClosure = false;
+            _leaveRebasesAsOpcodes = false;
+        }
     }
 
     // only some images need to go into closure (non-rooted ones from dyld cache do not)
@@ -3061,7 +3223,8 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fil
         }
    }
 
-    bool optimizedObjC = optimizeObjC(writers);
+    // only build objc closure info when building full closures
+    bool optimizedObjC = !_makeMinimalClosure && optimizeObjC(writers);
 
     // Note we have to compute the init order after buildImage as buildImage may set hasInits to true
     for (uintptr_t imageIndex = 0, writerIndex = 0; imageIndex != _loadedImages.count(); ++imageIndex) {
@@ -3117,7 +3280,7 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fil
                 MachOLoaded::DependentToMachOLoaded reexportFinder = ^(const MachOLoaded* mh, uint32_t depIndex) {
                     return (const MachOLoaded*)findDependent(mh, depIndex);
                 };
-                //fprintf(stderr, "'%s' overrides '%s'\n", li.loadedFileInfo.path, cacheImage->path());
+                //fprintf(stderr, "'%s' overrides something in cache\n", li.loadedFileInfo.path);
                 _dyldCache->forEachPatchableExport(imageIndex, ^(uint32_t cacheOffsetOfImpl, const char* symbolName) {
                     dyld3::MachOAnalyzer::FoundSymbol foundInfo;
                     Diagnostics                       patchDiag;
@@ -3131,70 +3294,46 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const LoadedFileInfo& fil
                         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, 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;
+                        // this means the symbol is missing in the cache override dylib, see it moved to a sibling
+                        // <rdar://problem/59196856> allow patched impls to move between re-export sibling dylibs
+                        bool foundViaParent = false;
+                        for (const BuilderLoadedImage& li2 : _loadedImages) {
+                            if ( (li2.overrideImageNum != 0) && (li2.imageNum != li.imageNum) ) {
+                                for (Image::LinkedImage aDep : li2.dependents) {
+                                    if ( (aDep.kind() == Image::LinkKind::reExport) && (aDep.imageNum() == li.imageNum) ) {
+                                        if ( li2.loadAddress()->findExportedSymbol(patchDiag, symbolName, false, foundInfo, reexportFinder) ) {
+                                             const MachOAnalyzer* impDylib = (const MachOAnalyzer*)foundInfo.foundInDylib;
+                                             patch.replacement.image.kind     = Image::ResolvedSymbolTarget::kindImage;
+                                             patch.replacement.image.imageNum = findLoadedImage(impDylib).imageNum;
+                                             patch.replacement.image.offset   = foundInfo.value;
+                                             foundViaParent = true;
+                                             //fprintf(stderr, "found patch target '%s' previously in '%s', now in '%s'\n", symbolName, li.path(), li2.path());
+                                             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;
-                            }
+                        if ( !foundViaParent ) {
+                            // symbol is missing from override, set other cached dylibs that used it to NULL
+                            //fprintf(stderr, "could not find symbol '%s' in %s \n", symbolName, li.path());
+                            patch.replacement.absolute.kind    = Image::ResolvedSymbolTarget::kindAbsolute;
+                            patch.replacement.absolute.value   = 0;
                         }
                     }
-                }
+                    patches.push_back(patch);
+                });
+                closureWriter.addCachePatches(patches);
             }
         }
 
         // record any cache patching needed because weak-def C++ symbols override dyld cache
-        if ( !_weakDefCacheOverrides.empty() )
+        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
-
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
     uint32_t progVarsOffset;
     if ( mainExecutable->hasProgramVars(_diag, progVarsOffset) ) {
         // on macOS binaries may have a __dyld section that has ProgramVars to use
@@ -3240,10 +3379,10 @@ const DlopenClosure* ClosureBuilder::makeDlopenClosure(const char* path, const L
 {
     dyld3::ScopedTimer timer(DBG_DYLD_TIMING_BUILD_CLOSURE, 0, 0, 0);
     // set up stack based storage for all arrays
-    BuilderLoadedImage  loadImagesStorage[300];
+    BuilderLoadedImage  loadImagesStorage[256];
     Image::LinkedImage  dependenciesStorage[128];
     Closure::PatchEntry cachePatchStorage[64];
-    _loadedImages.setInitialStorage(loadImagesStorage, 300);
+    _loadedImages.setInitialStorage(loadImagesStorage, 256);
     _dependencies.setInitialStorage(dependenciesStorage, 128);
     _weakDefCacheOverrides.setInitialStorage(cachePatchStorage, 64);
     ArrayFinalizer<BuilderLoadedImage> scopedCleanup(_loadedImages, ^(BuilderLoadedImage& li) { if (li.unmapWhenDone) {_fileSystem.unloadFile(li.loadedFileInfo); li.unmapWhenDone=false;} });
@@ -3272,8 +3411,11 @@ const DlopenClosure* ClosureBuilder::makeDlopenClosure(const char* path, const L
         entry.isBadImage                 = false;
         entry.mustBuildClosure           = false;
         entry.hasMissingWeakImports      = false;
+        entry.hasInterposingTuples       = !inDyldCache && ma->isDylib() && ma->hasInterposingTuples();
         entry.overrideImageNum           = 0;
-        if ( !inDyldCache && image->isOverrideOfDyldCacheImage(overrideImageNum) ) {
+        entry.exportsTrieOffset          = 0;
+        entry.exportsTrieSize            = 0;
+        if ( image->isOverrideOfDyldCacheImage(overrideImageNum) ) {
             entry.overrideImageNum  = overrideImageNum;
             canUseSharedCacheClosure = false;
         }
@@ -3282,7 +3424,7 @@ const DlopenClosure* ClosureBuilder::makeDlopenClosure(const char* path, const L
         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)
@@ -3302,13 +3444,13 @@ const DlopenClosure* ClosureBuilder::makeDlopenClosure(const char* path, const L
     }
 
     // add top level dylib being dlopen()ed
-    BuilderLoadedImage* foundTopImage;
+    BuilderLoadedImage* foundTopImage = nullptr;
     _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, LinkageType::kDynamic, 0, canUseSharedCacheClosure) ) {
+    LoadedImageChain chainMain = { nullptr, _loadedImages[_mainProgLoadIndex] };
+    LoadedImageChain chainCaller = { &chainMain, callerImage };
+    if ( !findImage(path, chainCaller, foundTopImage, LinkageType::kDynamic, 0, canUseSharedCacheClosure) ) {
         // If we didn't find the image, it might be a symlink to something in the dyld cache that is not on disk
         if ( (_dyldCache != nullptr) && !_dyldCache->header.dylibsExpectedOnDisk ) {
             char resolvedPath[PATH_MAX];
@@ -3363,6 +3505,11 @@ const DlopenClosure* ClosureBuilder::makeDlopenClosure(const char* path, const L
     // RTLD_NOW means fail the dlopen() if a symbol cannot be bound
     _allowMissingLazies = !forceBindLazies;
 
+    // If we got this far, we are not using a prebuilt dlopen-closure
+    // Since dlopen closures are never saved to disk, don't put fixups into the closure
+    // Except if interposing is used, since we don't have plumbing to apply interposing dynamically
+    _makeMinimalClosure = !mainClosure->hasInterposings();
+
     // only some images need to go into closure (ones from dyld cache do not, unless the cache format changed)
     STACK_ALLOC_ARRAY(ImageWriter, writers, _loadedImages.count());
     if ( _foundNonCachedImage || _foundDyldCacheRoots ) {
@@ -3476,18 +3623,14 @@ const LaunchClosure* ClosureBuilder::makeLaunchClosure(const char* mainPath, boo
 {
     char realerPath[MAXPATHLEN];
     closure::LoadedFileInfo loadedFileInfo = MachOAnalyzer::load(_diag, _fileSystem, mainPath, _archs, _platform, realerPath);
-    const MachOAnalyzer* mh = (const MachOAnalyzer*)loadedFileInfo.fileContent;
-    loadedFileInfo.path = mainPath;
-    if (_diag.hasError())
+    if ( _diag.hasError() )
         return nullptr;
+    loadedFileInfo.path = mainPath;
+    const MachOAnalyzer* mh = (const MachOAnalyzer*)loadedFileInfo.fileContent;
     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);
@@ -3500,7 +3643,7 @@ void ClosureBuilder::setDyldCacheInvalidFormatVersion() {
 
 
 // used by dyld shared cache builder
-const ImageArray* ClosureBuilder::makeDyldCacheImageArray(bool customerCache, const Array<CachedDylibInfo>& dylibs, const Array<CachedDylibAlias>& aliases)
+const ImageArray* ClosureBuilder::makeDyldCacheImageArray(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
@@ -3510,7 +3653,6 @@ const ImageArray* ClosureBuilder::makeDyldCacheImageArray(bool customerCache, co
 
     _makingDyldCacheImages = true;
     _allowMissingLazies    = false;
-    _makingCustomerCache   = customerCache;
     _aliases               = &aliases;
 
     // build _loadedImages[] with every dylib in cache
@@ -3527,7 +3669,10 @@ const ImageArray* ClosureBuilder::makeDyldCacheImageArray(bool customerCache, co
         entry.isBadImage                    = false;
         entry.mustBuildClosure              = false;
         entry.hasMissingWeakImports         = false;
+        entry.hasInterposingTuples          = false;    // dylibs in dyld cache cannot have interposing tuples
         entry.overrideImageNum              = 0;
+        entry.exportsTrieOffset             = 0;
+        entry.exportsTrieSize               = 0;
         _loadedImages.push_back(entry);
     }
 
@@ -3589,7 +3734,10 @@ const ImageArray* ClosureBuilder::makeOtherDylibsImageArray(const Array<LoadedFi
         entry.isBadImage                    = false;
         entry.mustBuildClosure              = false;
         entry.hasMissingWeakImports         = false;
+        entry.hasInterposingTuples          = false;    // all images here have passed canHavePrecomputedDlopenClosure() which does not allow interposing tuples
         entry.overrideImageNum              = 0;
+        entry.exportsTrieOffset             = 0;
+        entry.exportsTrieSize               = 0;
         _loadedImages.push_back(entry);
     }
 
@@ -3720,7 +3868,7 @@ void ClosureBuilder::buildLoadOrderRecurse(Array<LoadedImage>& loadedList, const
 
 void ClosureBuilder::buildLoadOrder(Array<LoadedImage>& loadedList, const Array<const ImageArray*>& imagesArrays, const Closure* toAdd)
 {
-    const dyld3::closure::Image* topImage = ImageArray::findImage(imagesArrays, toAdd->topImage());
+    const dyld3::closure::Image* topImage = ImageArray::findImage(imagesArrays, toAdd->topImageNum());
        loadedList.push_back(LoadedImage::make(topImage));
        buildLoadOrderRecurse(loadedList, imagesArrays, topImage);
 }
@@ -3821,5 +3969,6 @@ void ObjCClassOpt::write(const PerfectHashT& phash, const Array<std::pair<const
     });
 }
 
+
 } // namespace closure
 } // namespace dyld3
index 14554bafc62a6da9bbd67e559d6a7d46ec093f0b..deadf359af97b82385679fdf0de9d41be0189268 100644 (file)
@@ -32,6 +32,7 @@
 #include "PathOverrides.h"
 #include "DyldSharedCache.h"
 #include "MachOAnalyzer.h"
+#include "MachOAnalyzerSet.h"
 #include "Map.h"
 #include "Loading.h"
 
@@ -43,25 +44,20 @@ struct objc_selopt_t;
 struct objc_protocolopt2_t;
 }
 
+typedef dyld3::MachOAnalyzerSet::WrappedMachO   WrappedMachO;
+
 namespace dyld3 {
 
+class RootsChecker;
 
 namespace closure {
 
+class ClosureAnalyzerSet;
 
-
-class VIS_HIDDEN ClosureBuilder
+class VIS_HIDDEN ClosureBuilder : public dyld3::MachOAnalyzerSet
 {
 public:
 
-    struct LaunchErrorInfo
-    {
-        uintptr_t       kind;
-        const char*     clientOfDylibPath;
-        const char*     targetDylibPath;
-        const char*     symbol;
-    };
-
     struct ResolvedTargetInfo
     {
         const MachOLoaded* foundInDylib;
@@ -75,26 +71,23 @@ public:
         int                libOrdinal;
     };
 
-    struct CacheDylibsBindingHandlers
-    {
-        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 dyld_chained_starts_in_image* starts, const Array<Image::ResolvedSymbolTarget>& targets, const Array<ResolvedTargetInfo>& targetInfos);
-   };
+    typedef void (^DylibFixupHandler)(const MachOLoaded* fixupIn, uint64_t fixupLocRuntimeOffset, PointerMetaData pmd, const MachOAnalyzerSet::FixupTarget& target);
 
     enum class AtPath { none, all, onlyInRPaths };
 
-                                ClosureBuilder(uint32_t startImageNum, const FileSystem& fileSystem, const DyldSharedCache* dyldCache, bool dyldCacheIsLive,
+                                ClosureBuilder(uint32_t startImageNum, const FileSystem& fileSystem, const RootsChecker& rootsChecker,
+                                               const DyldSharedCache* dyldCache, bool dyldCacheIsLive,
                                                const GradedArchs& archs, const PathOverrides& pathOverrides,
                                                AtPath atPathHandling=AtPath::all, bool allowRelativePaths=true, LaunchErrorInfo* errorInfo=nullptr,
-                                               Platform platform=MachOFile::currentPlatform(), const CacheDylibsBindingHandlers* handlers=nullptr);
+                                               Platform platform=MachOFile::currentPlatform(), DylibFixupHandler dylibFixupHandler=nullptr);
                                 ~ClosureBuilder();
     Diagnostics&                diagnostics() { return _diag; }
 
     const LaunchClosure*        makeLaunchClosure(const LoadedFileInfo& fileInfo, bool allowInsertFailures);
 
     const LaunchClosure*        makeLaunchClosure(const char* mainPath,bool allowInsertFailures);
-
+    void                        makeMinimalClosures() { _makeMinimalClosure = true; }
+    void                        setCanSkipEncodingRebases() { _leaveRebasesAsOpcodes = true; }
 
     static const DlopenClosure* sRetryDlopenClosure;
     const DlopenClosure*        makeDlopenClosure(const char* dylibPath, const LaunchClosure* mainClosure, const Array<LoadedImage>& loadedList,
@@ -102,7 +95,8 @@ public:
                                                   closure::ImageNum* topImageNum);
 
     ImageNum                    nextFreeImageNum() const { return _startImageNum + _nextIndex; }
-
+    Platform                    platform() const { return _platform; }
+    
     void                        setDyldCacheInvalidFormatVersion();
     void                        disableInterposing() { _interposingDisabled = true; }
 
@@ -126,20 +120,20 @@ public:
         const char*             aliasPath;
     };
 
-    const ImageArray*           makeDyldCacheImageArray(bool customerCache, const Array<CachedDylibInfo>& dylibs, const Array<CachedDylibAlias>& aliases);
+    const ImageArray*           makeDyldCacheImageArray(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:
-
+    friend ClosureAnalyzerSet;
 
     struct InitInfo
     {
-        uint32_t    initOrder;
-        bool        danglingUpward;
-        bool        visited;
+        uint32_t   initOrder       : 30,
+                   danglingUpward  :  1,
+                   visited         :  1;
     };
 
     struct BuilderLoadedImage
@@ -154,8 +148,11 @@ private:
                                     isBadImage              : 1,
                                     mustBuildClosure        : 1,
                                     hasMissingWeakImports   : 1,
-                                    padding                 : 12,
+                                    hasInterposingTuples    : 1,
+                                    padding                 : 11,
                                     overrideImageNum        : 12;
+        uint32_t                    exportsTrieOffset;
+        uint32_t                    exportsTrieSize;
         LoadedFileInfo              loadedFileInfo;
 
         // Convenience method to get the information from the loadedFileInfo
@@ -187,36 +184,27 @@ private:
                                       uint32_t compatVersion, bool canUseSharedCacheClosure);
     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                    addFixupInfo(ImageWriter& writer, BuilderLoadedImage& forImage);
     void                    addChainedFixupInfo(ImageWriter& writer, BuilderLoadedImage& forImage);
     void                    addInterposingTuples(LaunchClosureWriter& writer, const Image* image, const MachOAnalyzer* mh);
     void                    computeInitOrder(ImageWriter& writer, uint32_t loadIndex);
     void                    addClosureInfo(LaunchClosureWriter& closureWriter);
     void                    depthFirstRecurseSetInitInfo(uint32_t loadIndex, InitInfo initInfos[], uint32_t& initOrder, bool& hasError);
-    bool                    findSymbol(BuilderLoadedImage& fromImage, int libraryOrdinal, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend,
-                                       Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo);
-    bool                    findSymbolInImage(const MachOAnalyzer* macho, const char* symbolName, uint64_t addend, bool followReExports,
-                                              bool weakImport, 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);
+    const BuilderLoadedImage& findLoadedImage(ImageNum imageNum) const;
     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[]);
+    bool                    expandAtExecutablePath(const char* loadPath, bool fromLCRPATH, bool fromLCRPATHinMain, char fixedPath[]);
     void                    addMustBeMissingPath(const char* path);
     void                    addSkippedFile(const char* path, uint64_t inode, uint64_t mtime);
-    const char*             strdup_temp(const char* path);
+    const char*             strdup_temp(const char* path) const;
     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),
-                                                                      void (^missingLazyBindHandler)());
-    bool                    findMissingSymbolHandler(Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo);
+    void                    addOperatorCachePatches(BuilderLoadedImage& forImage);
+    void                    addWeakDefCachePatch(uint32_t cachedDylibIndex, uint32_t exportCacheOffset, const FixupTarget& patchTarget);
 
     struct HashCString {
         static size_t hash(const char* v);
@@ -284,6 +272,7 @@ private:
         OverflowSafeArray<SelectorFixup>                                                    selectorFixups;
         Map<const char*, dyld3::closure::Image::ObjCImageOffset, HashCString, EqualCString> selectorMap;
         std::optional<uint64_t>                                                             methodNameVMOffset;
+        OverflowSafeArray<uint64_t>                                                         methodListFixups;
     };
 
     bool                    optimizeObjC(Array<ImageWriter>& writers);
@@ -293,6 +282,7 @@ private:
     void                    optimizeObjCClasses(const objc_opt::objc_clsopt_t* objcClassOpt,
                                                 const Map<const dyld3::MachOAnalyzer*, bool, HashPointer, EqualPointer>& sharedCacheImagesMap,
                                                 const Map<const char*, dyld3::closure::Image::ObjCDuplicateClass, HashCString, EqualCString>& duplicateSharedCacheClasses,
+                                                const Map<const char*, bool, HashCString, EqualCString>& duplicateClassesToIgnore,
                                                 ObjCOptimizerImage& image);
     void                    optimizeObjCProtocols(const objc_opt::objc_protocolopt2_t* objcProtocolOpt,
                                                   const Map<const dyld3::MachOAnalyzer*, bool, HashPointer, EqualPointer>& sharedCacheImagesMap,
@@ -303,38 +293,52 @@ private:
                                                          const char* duplicateDefinitionPath,
                                                          const char* canonicalDefinitionPath);
 
+    void                    parseObjCClassDuplicates(Map<const char*, bool, HashCString, EqualCString>& duplicateClassesToIgnore);
+
     static bool             inLoadedImageArray(const Array<LoadedImage>& loadedList, ImageNum imageNum);
     static void             buildLoadOrderRecurse(Array<LoadedImage>& loadedList, const Array<const ImageArray*>& imagesArrays, const Image* toAdd);
     void                    invalidateInitializerRoots();
+    Image::ResolvedSymbolTarget makeResolvedTarget(const FixupTarget& target) const;
+
+    // MachOAnalyzerSet implementations
+    void                mas_forEachImage(void (^handler)(const WrappedMachO& anImage, bool hidden, bool& stop)) const override;
+    bool                mas_fromImageWeakDefLookup(const WrappedMachO& fromWmo, const char* symbolName, uint64_t addend, CachePatchHandler patcher, FixupTarget& target) const override;
+    void                mas_mainExecutable(WrappedMachO& anImage) const override;
+    void*               mas_dyldCache() const override;
+    bool                wmo_dependent(const WrappedMachO* image, uint32_t depIndex, WrappedMachO& childObj, bool& missingWeakDylib) const override;
+    const char*         wmo_path(const WrappedMachO* image) const override;
+    ExportsTrie         wmo_getExportsTrie(const WrappedMachO* image) const override;
+    bool                wmo_missingSymbolResolver(const WrappedMachO* fromWmo, bool weakImport, bool lazyBind, const char* symbolName, const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const override;
+
 
     const FileSystem&                       _fileSystem;
+    const RootsChecker&                     _rootsChecker;
     const DyldSharedCache* const            _dyldCache;
     const PathOverrides&                    _pathOverrides;
     const GradedArchs&                      _archs;
     Platform const                          _platform;
     uint32_t const                          _startImageNum;
     const ImageArray*                       _dyldImageArray                 = nullptr;
-    const CacheDylibsBindingHandlers*       _handlers                       = nullptr;
+    DylibFixupHandler                       _dylibFixupHandler              = nullptr;
     const Array<CachedDylibAlias>*          _aliases                        = nullptr;
     const AtPath                            _atPathHandling                 = AtPath::none;
     uint32_t                                _mainProgLoadIndex              = 0;
     const char*                             _mainProgLoadPath               = nullptr;
     Diagnostics                             _diag;
     LaunchErrorInfo*                        _launchErrorInfo                = nullptr;
-    PathPool*                               _tempPaths                      = nullptr;
+    mutable PathPool*                       _tempPaths                      = nullptr;
     PathPool*                               _mustBeMissingPaths             = nullptr;
     OverflowSafeArray<SkippedFile>          _skippedFiles;
     uint32_t                                _nextIndex                      = 0;
-    OverflowSafeArray<BuilderLoadedImage,2048>   _loadedImages;
+    OverflowSafeArray<BuilderLoadedImage,2048>  _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;
     OverflowSafeArray<uint8_t>              _objcSelectorsHashTable;
     OverflowSafeArray<Image::ObjCSelectorImage> _objcSelectorsHashTableImages;
     OverflowSafeArray<uint8_t>              _objcClassesHashTable;
     OverflowSafeArray<uint8_t>              _objcProtocolsHashTable;
-    OverflowSafeArray<Image::ObjCClassImage>    _objcClassesHashTableImages;
+    OverflowSafeArray<Image::ObjCClassImage> _objcClassesHashTableImages;
     OverflowSafeArray<uint8_t>              _objcClassesDuplicatesHashTable;
     PathPool*                               _objcDuplicateClassWarnings     = nullptr;
     uint32_t                                _alreadyInitedIndex             = 0;
@@ -342,7 +346,6 @@ private:
     bool                                    _makingDyldCacheImages          = false;
     bool                                    _dyldCacheIsLive                = false;    // means kernel is rebasing dyld cache content being viewed
     bool                                    _makingClosuresInCache          = false;
-    bool                                    _makingCustomerCache            = false;
     bool                                    _allowRelativePaths             = false;
     bool                                    _atPathUsed                     = false;
     bool                                    _interposingTuplesUsed          = false;
@@ -352,13 +355,39 @@ private:
     bool                                    _foundNonCachedImage            = false;    // true means we have one or more images from disk we need to build closure(s) for
     bool                                    _foundDyldCacheRoots            = false;    // true if one or more images are roots of the shared cache
     bool                                    _foundMissingLazyBinds          = false;    // true if one or more images having missing lazy binds
+    bool                                    _makeMinimalClosure             = false;
     bool                                    _interposingDisabled            = false;
+    bool                                    _leaveRebasesAsOpcodes          = false;
     ImageNum                                _libDyldImageNum                = 0;
     ImageNum                                _libSystemImageNum              = 0;
 };
 
+class VIS_HIDDEN RebasePatternBuilder
+{
+public:
+            RebasePatternBuilder(OverflowSafeArray<closure::Image::RebasePattern>& entriesStorage, uint64_t ptrSize);
+    void    add(uint64_t runtimeOffset);
+private:
+    OverflowSafeArray<closure::Image::RebasePattern>&   _rebaseEntries;
+    uint64_t                                            _lastLocation;
+    const uint64_t                                      _ptrSize;
+
+    static const Image::RebasePattern                   _s_maxLeapPattern;
+    static const uint64_t                               _s_maxLeapCount;
+};
 
 
+class VIS_HIDDEN BindPatternBuilder
+{
+public:
+            BindPatternBuilder(OverflowSafeArray<closure::Image::BindPattern>& entriesStorage, uint64_t ptrSize);
+    void    add(uint64_t runtimeOffset, Image::ResolvedSymbolTarget target, bool weakBindCoalese);
+private:
+    OverflowSafeArray<closure::Image::BindPattern>&   _bindEntries;
+    const uint64_t                                    _ptrSize;
+    uint64_t                                          _lastOffset;
+    Image::ResolvedSymbolTarget                       _lastTarget;
+};
 
 
 } //  namespace closure
index 3f1a7ece7151cb7f0a9dbcc8d6c30908158f9695..2c0ed4f3fe5eaa325e886ce239f2dbff4e096c56 100644 (file)
@@ -39,7 +39,7 @@ struct LoadedFileInfo {
     uint64_t     fileContentLen             = 0;
     uint64_t     sliceOffset                = 0;
     uint64_t     sliceLen            : 63,
-                 isSipProtected      : 1;
+                 isOSBinary          : 1;
     uint64_t     inode                      = 0;
     uint64_t     mtime                      = 0;
     void (*unload)(const LoadedFileInfo&)   = nullptr;
index a5f6705261bd6f53e27109dece37c36f32d4a023..7df22bbe905d634c4c167d6082f2a8dcfc098944 100644 (file)
@@ -34,9 +34,14 @@ bool FileSystemNull::loadFile(const char* path, LoadedFileInfo& info, char reale
 }
 
 void FileSystemNull::unloadFile(const LoadedFileInfo& info) const {
+    if (info.unload)
+        info.unload(info);
 }
 
 void FileSystemNull::unloadPartialFile(LoadedFileInfo& info, uint64_t keepStartOffset, uint64_t keepLength) const {
+    // 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 FileSystemNull::fileExists(const char* path, uint64_t* inode, uint64_t* mtime,
index cb620b388a3fe2da5504b38f2a3756081623161e..8a635791e369727f722933ab38498fdf2a4bdb1f 100644 (file)
 
 #include "ClosureFileSystemPhysical.h"
 
+#include <TargetConditionals.h>
+
 #include <string.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <fcntl.h>
-#if BUILDING_UPDATE_DYLD_CACHE_BUILDER
-  #include <rootless.h>
-#endif
 #include <sys/errno.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
@@ -38,6 +37,9 @@
   #include <sandbox.h>
   #include <sandbox/private.h>
 #endif
+#include <TargetConditionals.h>
+#include "MachOFile.h"
+#include "MachOAnalyzer.h"
 
 using dyld3::closure::FileSystemPhysical;
 
@@ -45,7 +47,7 @@ bool FileSystemPhysical::getRealPath(const char possiblePath[MAXPATHLEN], char r
     __block bool success = false;
     // first pass: open file and ask kernel for canonical path
     forEachPath(possiblePath, ^(const char* aPath, unsigned prefixLen, bool& stop) {
-        int fd = ::open(aPath, O_RDONLY, 0);
+        int fd = dyld3::open(aPath, O_RDONLY, 0);
         if ( fd != -1 ) {
             char tempPath[MAXPATHLEN];
             success = (fcntl(fd, F_GETPATH, tempPath) == 0);
@@ -110,6 +112,8 @@ void FileSystemPhysical::forEachPath(const char* path, void (^handler)(const cha
     }
     if ( _rootPath != nullptr ) {
         strlcpy(altPath, _rootPath, PATH_MAX);
+        if ( path[0] != '/' )
+            strlcat(altPath, "/", PATH_MAX);
         strlcat(altPath, path, PATH_MAX);
         handler(altPath, (unsigned)strlen(_rootPath), stop);
         if ( stop )
@@ -144,9 +148,8 @@ bool FileSystemPhysical::loadFile(const char* path, LoadedFileInfo& info, char r
     // open file
     __block int fd;
     __block struct stat statBuf;
-    __block bool sipProtected = false;
     forEachPath(path, ^(const char* aPath, unsigned prefixLen, bool& stop) {
-        fd = ::open(aPath, O_RDONLY, 0);
+        fd = dyld3::open(aPath, O_RDONLY, 0);
         if ( fd == -1 ) {
             int openErrno = errno;
             if ( (openErrno == EPERM) && sandboxBlockedOpen(path) )
@@ -183,9 +186,6 @@ bool FileSystemPhysical::loadFile(const char* path, LoadedFileInfo& info, char r
                     }
                     else
                         strcpy(realerPath, realPathWithin);
-    #if BUILDING_UPDATE_DYLD_CACHE_BUILDER
-                    sipProtected = (rootless_check_trusted_fd(fd) == 0);
-    #endif
                     stop = true;
                 }
                 else {
@@ -217,7 +217,7 @@ bool FileSystemPhysical::loadFile(const char* path, LoadedFileInfo& info, char r
     info.fileContentLen = statBuf.st_size;
     info.sliceOffset = 0;
     info.sliceLen = statBuf.st_size;
-    info.isSipProtected = sipProtected;
+    info.isOSBinary = false;
     info.inode = statBuf.st_ino;
     info.mtime = statBuf.st_mtime;
     info.path  = path;
@@ -240,6 +240,27 @@ bool FileSystemPhysical::loadFile(const char* path, LoadedFileInfo& info, char r
     }
     info.fileContent = wholeFile;
 
+    // if this is an arm64e mach-o or a fat file with an arm64e slice we need to record if it is an OS binary
+#if TARGET_OS_OSX && __arm64e__
+    const MachOAnalyzer* ma = (MachOAnalyzer*)wholeFile;
+    if ( ma->hasMachOMagic() ) {
+        if ( (ma->cputype == CPU_TYPE_ARM64) && ((ma->cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) ) {
+            if ( ma->isOSBinary(fd, 0, info.fileContentLen) )
+                info.isOSBinary = true;
+        }
+    }
+    else if ( const FatFile* fat = FatFile::isFatFile(wholeFile) ) {
+        Diagnostics diag;
+        fat->forEachSlice(diag, info.fileContentLen, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
+            if ( (sliceCpuType == CPU_TYPE_ARM64) && ((sliceCpuSubType & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) ) {
+                uint64_t sliceOffset = (uint8_t*)sliceStart-(uint8_t*)wholeFile;
+                const MachOAnalyzer* sliceMA = (MachOAnalyzer*)((uint8_t*)wholeFile + sliceOffset);
+                if ( sliceMA->isOSBinary(fd, sliceOffset, sliceSize) )
+                    info.isOSBinary = true;
+            }
+        });
+    }
+#endif
     // Set unmap as the unload method.
     info.unload = [](const LoadedFileInfo& info) {
         ::munmap((void*)info.fileContent, (size_t)info.fileContentLen);
@@ -257,11 +278,11 @@ void FileSystemPhysical::unloadFile(const LoadedFileInfo& info) const {
 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);
+        ::munmap((void*)info.fileContent, (size_t)trunc_page(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)));
+        uintptr_t start = round_page((uintptr_t)info.fileContent + keepStartOffset + keepLength);
+        uintptr_t end = (uintptr_t)info.fileContent + info.fileContentLen;
+        ::munmap((void*)start, end - start);
     }
     info.fileContent = (const void*)((char*)info.fileContent + keepStartOffset);
     info.fileContentLen = keepLength;
@@ -272,7 +293,7 @@ bool FileSystemPhysical::fileExists(const char* path, uint64_t* inode, uint64_t*
     __block bool result = false;
     forEachPath(path, ^(const char* aPath, unsigned prefixLen, bool& stop) {
         struct stat statBuf;
-        if ( ::stat(aPath, &statBuf) == 0 ) {
+        if ( dyld3::stat(aPath, &statBuf) == 0 ) {
             if (inode)
                 *inode = statBuf.st_ino;
             if (mtime)
index 9df3d40c09f6ea186ec2031604e3b74628d49e13..e73c9ad40af62cffac6e47e9b7336cb61f278e4d 100644 (file)
@@ -49,7 +49,7 @@ static std::string printTarget(const Array<const ImageArray*>& imagesArrays, Ima
                 return std::string("bind to ") + targetImage->leafName() + " - " + hex8(-signExtend);
             }
             else
-                return std::string("bind to ") + targetImage->leafName() + " + " + hex8(target.image.offset);
+                return std::string("bind to ") + hex4(target.image.imageNum) + "-" + targetImage->leafName() + " + " + hex8(target.image.offset);
             break;
         case Image::ResolvedSymbolTarget::kindSharedCache:
             return std::string("bind to dyld cache + ") + hex8(target.sharedCache.offset);
@@ -139,8 +139,6 @@ static const char* nameForType(TypedBytes::Type type) {
         return "libDyldEntry";
     case TypedBytes::Type::libSystemNum:
         return "libSystemNum";
-    case TypedBytes::Type::bootUUID:
-        return "bootUUID";
     case TypedBytes::Type::mainEntry:
         return "mainEntry";
     case TypedBytes::Type::startEntry:
@@ -209,7 +207,6 @@ static Node buildImageNode(const Image* image, const Array<const ImageArray*>& i
     imageNode.map["has-plus-loads"].value = (image->mayHavePlusLoads() ? "true" : "false");
     imageNode.map["never-unload"].value = (image->neverUnload() ? "true" : "false");
     imageNode.map["has-precomputed-objc"].value = (image->hasPrecomputedObjC() ? "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() ) {
@@ -286,6 +283,12 @@ static Node buildImageNode(const Image* image, const Array<const ImageArray*>& i
             }
         });
 
+        uint64_t expectedInode;
+        uint64_t expectedMtime;
+        if ( image->hasFileModTimeAndInode(expectedInode, expectedMtime) ) {
+            imageNode.map["file-inode"].value = hex(expectedInode);
+            imageNode.map["file-mod-time"].value = hex(expectedMtime);
+        }
 
         if ( printFixups ) {
             image->forEachFixup(^(uint64_t imageOffsetToRebase, bool &stop) {
@@ -426,6 +429,10 @@ static Node buildImageNode(const Image* image, const Array<const ImageArray*>& i
         imageNode.map["override-of-dyld-cache-image"].value = ImageArray::findImage(imagesArrays, cacheImageNum)->path();
        }
 
+    if ( image->inDyldCache() && image->overridableDylib() ) {
+        imageNode.map["overridable-dylib"].value = "true";
+    }
+
 
 #if 0
     // add things to init before this image
@@ -478,7 +485,7 @@ static Node buildClosureNode(const DlopenClosure* closure, const Array<const Ima
     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["func-image-num"].value         = hex4(patchEntry.overriddenDylibInCache);
         patchNode.map["replacement"].value            = printTarget(imagesArrays, patchEntry.replacement);
         root.map["dyld-cache-fixups"].array.push_back(patchNode);
     });
@@ -516,7 +523,7 @@ static Node buildClosureNode(const LaunchClosure* closure, const Array<const Ima
     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["func-image-num"].value         = hex4(patchEntry.overriddenDylibInCache);
         patchNode.map["replacement"].value            = printTarget(imagesArrays, patchEntry.replacement);
         root.map["dyld-cache-fixups"].array.push_back(patchNode);
     });
@@ -558,14 +565,6 @@ static Node buildClosureNode(const LaunchClosure* closure, const Array<const Ima
         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());
 
     // add env-vars if they exist
index 2501f87b74aae28d69598ebf5e64f34e9c619ddb..32c574872fd9d603d545a63314125dcb1e610423 100644 (file)
@@ -312,6 +312,16 @@ void ImageWriter::setBindInfo(const Array<Image::BindPattern>& fixups)
     append(TypedBytes::Type::bindFixups, fixups.begin(), (uint32_t)fixups.count()*sizeof(Image::BindPattern));
 }
 
+void ImageWriter::setFixupsNotEncoded()
+{
+    getFlags().fixupsNotEncoded = true;
+}
+
+void ImageWriter::setRebasesNotEncoded()
+{
+    getFlags().rebasesNotEncoded = true;
+}
+
 void ImageWriter::setChainedFixups(uint64_t runtimeStartsStructOffset, const Array<Image::ResolvedSymbolTarget>& targets)
 {
     getFlags().hasChainedFixups = true;
@@ -397,6 +407,7 @@ void ImageWriter::setAsOverrideOf(ImageNum imageNum)
 {
     uint32_t temp = imageNum;
     append(TypedBytes::Type::imageOverride, &temp, sizeof(temp));
+    getFlags().hasOverrideImageNum = true;
 }
 
 void ImageWriter::setInitsOrder(const ImageNum images[], uint32_t count)
@@ -621,17 +632,6 @@ 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::setHasProgramVars(uint32_t offset)
 {
     getFlags().hasProgVars = true;
index d190deeff688b39bac22399282999b6d540e4f91..9d44fa0f948ad3510577723b5218d86f1a9ba22a 100644 (file)
@@ -97,6 +97,7 @@ public:
     void        setMappingInfo(uint64_t sliceOffset, uint64_t vmSize);
     void        setFileInfo(uint64_t inode, uint64_t modTime);
     void        setRebaseInfo(const Array<Image::RebasePattern>&);
+    void        setRebasesNotEncoded();
     void        setTextRebaseInfo(const Array<Image::TextFixupPattern>&);
     void        setBindInfo(const Array<Image::BindPattern>&);
     void        setObjCFixupInfo(const Image::ResolvedSymbolTarget& objcProtocolClassTarget,
@@ -108,6 +109,7 @@ public:
     void        setAsOverrideOf(ImageNum);
     void        setInitsOrder(const ImageNum images[], uint32_t count);
     void        setChainedFixups(uint64_t runtimeStartsStructOffset, const Array<Image::ResolvedSymbolTarget>& targets);
+    void        setFixupsNotEncoded();
 
     const Image* currentImage();
 
@@ -160,7 +162,6 @@ public:
     void                    setMustExistFiles(const Array<LaunchClosure::SkippedFile>& files);
     void                    addInterposingTuples(const Array<InterposingTuple>& tuples);
     void                    setDyldCacheUUID(const uuid_t);
-    void                    setBootUUID(const char* uuid);
     void                    addEnvVar(const char* envVar);
     void                    setObjCSelectorInfo(const Array<uint8_t>& hashTable, const Array<Image::ObjCSelectorImage>& hashTableImages);
     void                    setObjCClassAndProtocolInfo(const Array<uint8_t>& classHashTable, const Array<uint8_t>& protocolHashTable,
diff --git a/dyld3/Defines.h b/dyld3/Defines.h
new file mode 100644 (file)
index 0000000..723da1a
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+* Copyright (c) 2019 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@
+*/
+
+// For now just for compact info, but in the long run we can use this to centralize convenience macros and configuration options
+
+#ifndef DYLD_DEFINES_H
+#define DYLD_DEFINES_H
+
+#define VIS_HIDDEN __attribute__((visibility("hidden")))
+
+#ifndef BUILD_FOR_TESTING
+#define BUILD_FOR_TESTING 0
+#endif
+
+#endif /* DYLD_DEFINES_H */
index 647053e85a21f78966eca7e798ea8338255190f7..234f36cbd43d55c13b5ff778fc8b5475a387b7af 100644 (file)
@@ -21,6 +21,7 @@
  * @APPLE_LICENSE_HEADER_END@
  */
 
+#include <iostream>
 
 #include <stdint.h>
 #include <stdio.h>
@@ -41,6 +42,7 @@
 #include <dirent.h>
 #include <mach/mach.h>
 #include <mach/machine.h>
+#include <mach/mach_time.h>
 #include <mach-o/loader.h>
 #include <mach-o/nlist.h>
 #include <mach-o/fat.h>
@@ -95,9 +97,9 @@ void Diagnostics::error(const char* format, va_list list)
         return;
 
     if (_prefix.empty()) {
-        fprintf(stderr, "%s", _simple_string(_buffer));
+        fprintf(stderr, "%s\n", _simple_string(_buffer));
     } else {
-        fprintf(stderr, "[%s] %s", _prefix.c_str(), _simple_string(_buffer));
+        fprintf(stderr, "[%s] %s\n", _prefix.c_str(), _simple_string(_buffer));
     }
 #endif
 }
@@ -125,6 +127,14 @@ void Diagnostics::assertNoError() const
         abort_report_np("%s", _simple_string(_buffer));
 }
 
+bool Diagnostics::errorMessageContains(const char* subString) const
+{
+    if ( _buffer == nullptr )
+        return false;
+    return (strstr(_simple_string(_buffer), subString) != nullptr);
+}
+
+
 #if !BUILDING_CACHE_BUILDER
 const char* Diagnostics::errorMessage() const
 {
@@ -213,5 +223,53 @@ void Diagnostics::clearWarnings()
 #endif
 }
 
+#if BUILDING_CACHE_BUILDER
+void TimeRecorder::pushTimedSection() {
+    openTimings.push_back(mach_absolute_time());
+}
+
+void TimeRecorder::recordTime(const char* format, ...) {
+    uint64_t t = mach_absolute_time();
+    uint64_t previousTime = openTimings.back();
+    openTimings.pop_back();
+
+    char*   output_string = nullptr;
+    va_list list;
+    va_start(list, format);
+    vasprintf(&output_string, format, list);
+    va_end(list);
+
+    if (output_string != nullptr) {
+        timings.push_back(TimingEntry {
+            .time = t - previousTime,
+            .logMessage = std::string(output_string),
+            .depth = (int)openTimings.size()
+        });
+    }
+
+    openTimings.push_back(mach_absolute_time());
+}
+
+void TimeRecorder::popTimedSection() {
+    openTimings.pop_back();
+}
+
+static inline uint32_t absolutetime_to_milliseconds(uint64_t abstime)
+{
+    return (uint32_t)(abstime/1000/1000);
+}
+
+void TimeRecorder::logTimings() {
+    for (const TimingEntry& entry : timings) {
+        for (int i = 0 ; i < entry.depth ; i++) {
+            std::cerr << "  ";
+        }
+        std::cerr << "time to " << entry.logMessage << " " << absolutetime_to_milliseconds(entry.time) << "ms" << std::endl;
+    }
+
+    timings.clear();
+}
+#endif
+
 #endif
 
index 21cdf41222848743e3fd09c55fb15b7cd789e53a..b7b5e511fb1095f4011f3a92333fd51d76e2c5c2 100644 (file)
@@ -57,6 +57,7 @@ public:
     bool            noError() const;
     void            clearError();
     void            assertNoError() const;
+    bool            errorMessageContains(const char* subString) const;
 
 #if !BUILDING_CACHE_BUILDER
     const char*                     errorMessage() const;
@@ -68,6 +69,7 @@ public:
 #endif
 
 private:
+
     void*                    _buffer = nullptr;
 #if BUILDING_CACHE_BUILDER
     std::string              _prefix;
@@ -76,7 +78,37 @@ private:
 #endif
 };
 
+#if BUILDING_CACHE_BUILDER
+
+class VIS_HIDDEN TimeRecorder
+{
+public:
+    // Call pushTimedSection(), then mark events with recordTime. Call popTimedSection() to stop the current timing session.
+    // This is stack-based, so you can start a sub-timer with pushTimedSection() / recordTime / recordTime... / popTimedSection()
+    // inside a first timed section.
+    // Call logTimings() to print everything.
+
+    // Start a new timed section.
+    void pushTimedSection();
+
+    // Records the time taken since the last pushTimedSection() / recordTime() at the current level
+    void recordTime(const char* format, ...);
+
+    // Stop the current timed section and pop back one level.
+    void popTimedSection();
 
+    void logTimings();
+private:
+    struct TimingEntry {
+        uint64_t time;
+        std::string logMessage;
+        int depth;
+    };
+
+    std::vector<uint64_t> openTimings;
+    std::vector<TimingEntry> timings;
+};
 
+#endif /* BUILDING_CACHE_BUILDER */
 
 #endif // Diagnostics_h
index 5c98739997075e941ecb57524f1b21130e8e92b8..aea4de665f39a2f83065809e79db2da9cf3e0529 100644 (file)
 
 #include <string>
 #include <map>
+#include <sstream>
 #include <vector>
 
 namespace dyld3 {
 namespace json {
 
+enum class NodeValueType {
+    Default,
+    String,
+    RawValue,
+};
+
 struct Node
 {
+    NodeValueType               type = NodeValueType::Default;
     std::string                 value;
     std::map<std::string, Node> map;
     std::vector<Node>           array;
+
+    inline Node()
+    : type(NodeValueType::Default), value(), map(), array() { }
+
+    inline Node(std::string string)
+    : type(NodeValueType::String), value(string), map(), array() { }
+
+    inline Node(const char *string) : Node(std::string{string}) { }
+
+    inline Node(bool b)
+    : type(NodeValueType::RawValue), value(b ? "true" : "false")
+    , map(), array() { }
+
+    inline Node(int64_t i64)
+    : type(NodeValueType::RawValue), value(), map(), array()
+    {
+        std::ostringstream os{};
+        os << i64;
+        value = os.str();
+    }
+
+    inline Node(uint64_t u64)
+    : type(NodeValueType::RawValue), value(), map(), array()
+    {
+        std::ostringstream os{};
+        os << u64;
+        value = os.str();
+    }
 };
 
+static inline Node makeNode(std::string value) {
+    Node node;
+    node.value = value;
+    return node;
+}
 
 } // namespace json
 } // namespace dyld3
index f8725dda94c825e18a24b775d7cb05d8390746d6..739c5440351b6bc0f3409ccda6d123d257bea297 100644 (file)
@@ -34,6 +34,7 @@ namespace dyld3 {
 namespace json {
 
 Node readJSON(Diagnostics& diags, const char* filePath);
+Node readJSON(Diagnostics& diags, const void* contents, size_t length);
 
 // Given a map node, returns the node representing the given value.
 // If it is missing, returns a sentinel node and sets an error on the diagnostic
@@ -46,6 +47,9 @@ const Node* getOptionalValue(Diagnostics& diags, const Node& node, const char* k
 // Parses an int from the given node, or throws an error if its not an integer payload
 uint64_t parseRequiredInt(Diagnostics& diags, const Node& node);
 
+// Parses a bool from the given node, or throws an error if its not a boolean payload
+bool parseRequiredBool(Diagnostics& diags, const Node& node);
+
 // Parses a string from the given node, or throws an error if its not a string payload
 const std::string& parseRequiredString(Diagnostics& diags, const Node& node);
 
index 2f422d7625f17a3b36b7249bfe494d9741bc9143..62b92816310b35d5247ca5d1c59928d43f8250dc 100644 (file)
@@ -94,6 +94,33 @@ uint64_t parseRequiredInt(Diagnostics& diags, const Node& node) {
     return atoi(node.value.c_str());
 }
 
+bool parseRequiredBool(Diagnostics& diags, const Node& node) {
+    if (diags.hasError())
+        return false;
+
+    if (!node.array.empty()) {
+        diags.error("Cannot get integer value from array node\n");
+        return false;
+    }
+    if (!node.map.empty()) {
+        diags.error("Cannot get integer value from value node\n");
+        return false;
+    }
+    if (node.value.empty()) {
+        diags.error("Cannot get integer value from empty node\n");
+        return false;
+    }
+
+    if ( (node.value == "true") || (node.value == "1") )
+        return true;
+
+    if ( (node.value == "false") || (node.value == "0") )
+        return false;
+
+    diags.error("Boolean value should be true/false/0/1\n");
+    return false;
+}
+
 const std::string& parseRequiredString(Diagnostics& diags, const Node& node) {
     static std::string sentinelString = "";
 
@@ -168,6 +195,12 @@ Node parseNode(Diagnostics& diags, id jsonObject) {
         return node;
     }
 
+    // NSBoolean -> string
+    if ([jsonObject isKindOfClass:[NSNumber class]]) {
+        node.value = [[(NSNumber*)jsonObject stringValue] UTF8String];
+        return node;
+    }
+
     diags.error("Unknown json deserialized type\n");
     return Node();
 }
@@ -193,5 +226,17 @@ Node readJSON(Diagnostics& diags, const char* filePath) {
     return node;
 }
 
+Node readJSON(Diagnostics& diags, const void * contents, size_t length) {
+    NSData* data = [NSData dataWithBytes:contents length:length];
+    NSError* error = nil;
+    id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
+    if (!jsonObject) {
+        diags.error("Could not deserialize json because '%s'\n", [[error localizedFailureReason] UTF8String]);
+        return Node();
+    }
+
+    return parseNode(diags, jsonObject);
+}
+
 } //namespace json
 } //namespace dyld3
index 788ffaee5143e056626777a84020079dd62c621b..b473155ed94efd4deb70525326b99bd1898d3c68 100644 (file)
@@ -61,7 +61,7 @@ static inline void indentBy(uint32_t spaces, std::ostream& out) {
     }
 }
 
-static inline void printJSON(const Node& node, uint32_t indent, std::ostream& out)
+static inline void printJSON(const Node& node, uint32_t indent = 0, std::ostream& out = std::cout)
 {
     if ( !node.map.empty() ) {
         out << "{";
@@ -95,19 +95,54 @@ static inline void printJSON(const Node& node, uint32_t indent, std::ostream& ou
         out << "]";
     }
     else {
-        std::string escapedString;
-        escapedString.reserve(node.value.size());
-        for (char c : node.value) {
-            if (c == '"')
-                escapedString += '\\';
-            escapedString += c;
+        auto &value = node.value;
+        switch (node.type) {
+        case NodeValueType::Default:
+        case NodeValueType::String:
+            if (value.find('"') == std::string::npos) {
+                out << "\"" << value << "\"";
+            } else {
+                std::string escapedString;
+                escapedString.reserve(value.size());
+                for (char c : value) {
+                    if (c == '"')
+                        escapedString += '\\';
+                    escapedString += c;
+                }
+                out << "\"" << escapedString << "\"";
+            }
+            break;
+        case NodeValueType::RawValue:
+            out << value;
+            break;
         }
-        out << "\"" << escapedString << "\"";
     }
     if ( indent == 0 )
         out << "\n";
 }
 
+static inline void streamArrayBegin(bool &needsComma, std::ostream& out = std::cout)
+{
+    out << "[";
+    needsComma = false;
+}
+
+static inline void streamArrayNode(bool &needsComma, Node &node, std::ostream& out = std::cout)
+{
+    if (needsComma)
+        out << ",";
+    out << "\n";
+    indentBy(2, out);
+    printJSON(node, 2, out);
+    needsComma = true;
+}
+
+static inline void streamArrayEnd(bool &needsComma, std::ostream& out = std::cout)
+{
+    if (needsComma)
+        out << "\n";
+    out << "]\n";
+}
 
 } // namespace json
 } // namespace dyld3
index 6e5eeabc0a2e80b7cabd13484203c2cfb466d59e..dd3c974e74fd6a693a39e130436efda4c6ca1677 100644 (file)
 //#include <dispatch/dispatch.h>
 #include <mach/vm_page_size.h>
 
+#include "ClosureFileSystemPhysical.h"
 #include "MachOFile.h"
 #include "MachOLoaded.h"
 #include "MachOAnalyzer.h"
 #include "Logging.h"
 #include "Loading.h"
+#include "RootsChecker.h"
 #include "Tracing.h"
 #include "dyld2.h"
 #include "dyld_cache_format.h"
+#include "libdyldEntryVector.h"
 
 #include "objc-shared-cache.h"
 
@@ -103,11 +106,15 @@ namespace dyld3 {
 Loader::Loader(const Array<LoadedImage>& existingImages, Array<LoadedImage>& newImagesStorage,
                const void* cacheAddress, const Array<const dyld3::closure::ImageArray*>& imagesArrays,
                const closure::ObjCSelectorOpt* selOpt, const Array<closure::Image::ObjCSelectorImage>& selImages,
-               LogFunc logLoads, LogFunc logSegments, LogFunc logFixups, LogFunc logDofs)
+               const RootsChecker& rootsChecker, dyld3::Platform platform,
+               LogFunc logLoads, LogFunc logSegments, LogFunc logFixups, LogFunc logDof,
+               bool allowMissingLazies, dyld3::LaunchErrorInfo* launchErrorInfo)
        : _existingImages(existingImages), _newImages(newImagesStorage),
          _imagesArrays(imagesArrays), _dyldCacheAddress(cacheAddress), _dyldCacheSelectorOpt(nullptr),
          _closureSelectorOpt(selOpt), _closureSelectorImages(selImages),
-         _logLoads(logLoads), _logSegments(logSegments), _logFixups(logFixups), _logDofs(logDofs)
+         _rootsChecker(rootsChecker), _allowMissingLazies(allowMissingLazies), _platform(platform),
+         _logLoads(logLoads), _logSegments(logSegments), _logFixups(logFixups), _logDofs(logDof), _launchErrorInfo(launchErrorInfo)
+
 {
 #if BUILDING_DYLD
     // This is only needed for dyld and the launch closure, not the dlopen closures
@@ -122,7 +129,7 @@ void Loader::addImage(const LoadedImage& li)
     _newImages.push_back(li);
 }
 
-LoadedImage* Loader::findImage(closure::ImageNum targetImageNum)
+LoadedImage* Loader::findImage(closure::ImageNum targetImageNum) const
 {
 #if BUILDING_DYLD
     // The launch images are different in dyld vs libdyld.  In dyld, the new images are
@@ -131,7 +138,7 @@ LoadedImage* Loader::findImage(closure::ImageNum targetImageNum)
         return info;
     }
 
-    for (uint64_t index = 0; index != _newImages.count(); ++index) {
+    for (uintptr_t index = 0; index != _newImages.count(); ++index) {
         LoadedImage& info = _newImages[index];
         if ( info.image()->representsImageNum(targetImageNum) ) {
             // Try cache this entry for next time
@@ -180,11 +187,16 @@ uintptr_t Loader::resolveTarget(closure::Image::ResolvedSymbolTarget target)
 
 void Loader::completeAllDependents(Diagnostics& diag, bool& someCacheImageOverridden)
 {
-    // accumulate all image overrides
-    STACK_ALLOC_ARRAY(ImageOverride, overrides, _existingImages.maxCount() + _newImages.maxCount());
+    bool iOSonMac = (_platform == Platform::iOSMac);
+#if (TARGET_OS_OSX && TARGET_CPU_ARM64)
+    if ( _platform == Platform::iOS )
+        iOSonMac = true;
+#endif
+    // accumulate all image overrides (512 is placeholder for max unzippered twins in dyld cache)
+    STACK_ALLOC_ARRAY(ImageOverride, overrides, _existingImages.maxCount() + _newImages.maxCount() + 512);
     for (const auto anArray : _imagesArrays) {
-        // ignore prebuilt Image* in dyld cache
-        if ( anArray->startImageNum() < dyld3::closure::kFirstLaunchClosureImageNum )
+        // ignore prebuilt Image* in dyld cache, except for MacCatalyst apps where unzipped twins can override each other
+        if ( (anArray->startImageNum() < dyld3::closure::kFirstLaunchClosureImageNum) && !iOSonMac )
             continue;
         anArray->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
             ImageOverride overrideEntry;
@@ -207,7 +219,7 @@ void Loader::completeAllDependents(Diagnostics& diag, bool& someCacheImageOverri
     uintptr_t index = 0;
     while ( (index < _newImages.count()) && diag.noError() ) {
         const closure::Image* image = _newImages[index].image();
-        //fprintf(stderr, "completeAllDependents(): looking at dependents of %s\n", image->path());
+        //dyld::log("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) {
@@ -221,7 +233,7 @@ void Loader::completeAllDependents(Diagnostics& diag, bool& someCacheImageOverri
                 // 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());
+                     //dyld::log("  load imageNum=0x%05X, image path=%s\n", depImageNum, depImage->path());
                      if ( _newImages.freeCount() == 0 ) {
                          diag.error("too many initial images");
                          stop = true;
@@ -241,8 +253,11 @@ void Loader::completeAllDependents(Diagnostics& diag, bool& someCacheImageOverri
     }
 }
 
-void Loader::mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool fromOFI)
+void Loader::mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool fromOFI, bool* closureOutOfDate, bool* recoverable)
 {
+    *closureOutOfDate = false;
+    *recoverable      = true;
+
     // scan array and map images not already loaded
     for (LoadedImage& info : _newImages) {
         if ( info.loadedAddress() != nullptr ) {
@@ -267,21 +282,21 @@ void Loader::mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool from
         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) ) {
+                if ( dyld3::stat(info.image()->path(), &statBuf) == 0 ) {
+                    dyld3::closure::FileSystemPhysical fileSystem;
+                    if ( _rootsChecker.onDiskFileIsRoot(info.image()->path(), (const DyldSharedCache*)_dyldCacheAddress, info.image(),
+                                                        &fileSystem, statBuf.st_ino, statBuf.st_mtime) ) {
+                        if ( ((const DyldSharedCache*)_dyldCacheAddress)->header.dylibsExpectedOnDisk ) {
                             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 {
-                        diag.error("dylib file not expected on disk, must be a root '%s'", info.image()->path());
+                        *closureOutOfDate = true;
                     }
                 }
                 else if ( (_dyldCacheAddress != nullptr) && ((dyld_cache_header*)_dyldCacheAddress)->dylibsExpectedOnDisk ) {
                     diag.error("dylib file missing, was in dyld shared cache '%s'", info.image()->path());
+                    *closureOutOfDate = true;
                 }
             }
             if ( diag.noError() ) {
@@ -297,28 +312,28 @@ void Loader::mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool from
             }
         }
         else {
-            mapImage(diag, info, fromOFI);
+            mapImage(diag, info, fromOFI, closureOutOfDate);
             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 : _newImages) {
-            if ( (info.state() == LoadedImage::State::mapped) && !info.image()->inDyldCache() && !info.leaveMapped() ) {
-                _logSegments("dyld: unmapping %s\n", info.image()->path());
-                unmapImage(info);
-            }
-        }
+    if ( diag.hasError() ) {
+        // need to clean up by unmapping any images just mapped
+        unmapAllImages();
         return;
     }
 
-    // apply fixups
+    // apply fixups to all but main executable
+    LoadedImage* mainInfo = nullptr;
     for (LoadedImage& info : _newImages) {
         // images in shared cache do not need fixups applied
         if ( info.image()->inDyldCache() )
             continue;
+        if ( info.loadedAddress()->filetype == MH_EXECUTE ) {
+            mainInfo = &info;
+            continue;
+        }
         // previously loaded images were previously fixed up
         if ( info.state() < LoadedImage::State::fixedUp ) {
             applyFixupsToImage(diag, info);
@@ -327,6 +342,26 @@ void Loader::mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool from
             info.setState(LoadedImage::State::fixedUp);
         }
     }
+    if ( diag.hasError() ) {
+        // need to clean up by unmapping any images just mapped
+        unmapAllImages();
+        return;
+    }
+
+    if ( mainInfo != nullptr ) {
+        // now apply fixups to main executable
+        // we do it in this order so that if there is a problem with the dylibs in the closure
+        // the main executable is left untouched so the closure can be rebuilt
+        applyFixupsToImage(diag, *mainInfo);
+        if ( diag.hasError() ) {
+            // need to clean up by unmapping any images just mapped
+            unmapAllImages();
+            // we have already started fixing up the main executable, so we cannot retry the launch again
+            *recoverable = false;
+            return;
+        }
+        mainInfo->setState(LoadedImage::State::fixedUp);
+    }
 
     // find and register dtrace DOFs
     if ( processDOFs ) {
@@ -344,6 +379,18 @@ void Loader::mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool from
     }
 }
 
+void Loader::unmapAllImages()
+{
+    for (LoadedImage& info : _newImages) {
+        if ( !info.image()->inDyldCache() && !info.leaveMapped() ) {
+            if ( (info.state() == LoadedImage::State::mapped) || (info.state() == LoadedImage::State::fixedUp) ) {
+                _logSegments("dyld: unmapping %s\n", info.image()->path());
+                unmapImage(info);
+            }
+        }
+    }
+}
+
 bool Loader::sandboxBlocked(const char* path, const char* kind)
 {
 #if TARGET_OS_SIMULATOR || TARGET_OS_DRIVERKIT
@@ -370,7 +417,7 @@ bool Loader::sandboxBlockedStat(const char* path)
     return sandboxBlocked(path, "file-read-metadata");
 }
 
-void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI)
+void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI, bool* closureOutOfDate)
 {
     dyld3::ScopedTimer timer(DBG_DYLD_TIMING_MAP_IMAGE, info.image()->path(), 0, 0);
 
@@ -382,11 +429,7 @@ void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI)
     bool                    isCodeSigned  = image->hasCodeSignature(codeSignFileOffset, codeSignFileSize);
 
     // open file
-#if BUILDING_DYLD
-    int fd = dyld::my_open(info.image()->path(), O_RDONLY, 0);
-#else
-    int fd = ::open(info.image()->path(), O_RDONLY, 0);
-#endif
+    int fd = dyld3::open(info.image()->path(), O_RDONLY, 0);
     if ( fd == -1 ) {
         int openErr = errno;
         if ( (openErr == EPERM) && sandboxBlockedOpen(image->path()) )
@@ -399,7 +442,7 @@ void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI)
     // get file info
     struct stat statBuf;
 #if TARGET_OS_SIMULATOR
-    if ( stat(image->path(), &statBuf) != 0 ) {
+    if ( dyld3::stat(image->path(), &statBuf) != 0 ) {
 #else
     if ( fstat(fd, &statBuf) != 0 ) {
 #endif
@@ -418,6 +461,7 @@ void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI)
     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());
+            *closureOutOfDate = true;
             close(fd);
             return;
         }
@@ -433,6 +477,23 @@ void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI)
         }
     }
 
+    if ( isCodeSigned && (sliceOffset == 0) ) {
+        uint64_t expectedFileSize = round_page_kernel(codeSignFileOffset+codeSignFileSize);
+        uint64_t actualFileSize = round_page_kernel(statBuf.st_size);
+        if ( actualFileSize < expectedFileSize ) {
+            diag.error("File size too small for code signature");
+            *closureOutOfDate = true;
+            close(fd);
+            return;
+        }
+        if ( actualFileSize != expectedFileSize ) {
+            diag.error("File size doesn't match code signature");
+            *closureOutOfDate = true;
+            close(fd);
+            return;
+        }
+    }
+
     // register code signature
     uint64_t coveredCodeLength  = UINT64_MAX;
     if ( isCodeSigned ) {
@@ -447,6 +508,13 @@ void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI)
             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());
+#if BUILDING_LIBDYLD
+                if ( errnoCopy == EBADEXEC ) {
+                    // dlopen closures many be prebuilt in to the shared cache with a code signature, but the dylib is replaced
+                    // with one without a code signature.  In that case, lets build a new closure
+                    *closureOutOfDate = true;
+                }
+#endif
             }
             else {
                 diag.error("fcntl(fd, F_ADDFILESIGS_RETURN) failed with errno=%d, sliceOffset=0x%08llX, codeBlobOffset=0x%08X, codeBlobSize=0x%08X for '%s'",
@@ -603,6 +671,7 @@ void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI)
             }
         }
         if ( diag.hasError() ) {
+            *closureOutOfDate = true;
             ::vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
             return;
         }
@@ -610,7 +679,7 @@ void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI)
 
 #endif
 
-#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR
+#if (__arm__ || __arm64__) && !TARGET_OS_SIMULATOR
     // tell kernel about fairplay encrypted regions
     uint32_t fpTextOffset;
     uint32_t fpSize;
@@ -644,7 +713,7 @@ void Loader::registerDOFs(const Array<DOFInfo>& dofs)
     if ( dofs.empty() )
         return;
 
-    int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR);
+    int fd = ::open("/dev/" DTRACEMNR_HELPER, O_RDWR);
     if ( fd < 0 ) {
         _logDofs("can't open /dev/" DTRACEMNR_HELPER " to register dtrace DOF sections\n");
     }
@@ -682,14 +751,18 @@ void Loader::registerDOFs(const Array<DOFInfo>& dofs)
 
 bool Loader::dtraceUserProbesEnabled()
 {
+#if !TARGET_OS_SIMULATOR
     uint8_t dofEnabled = *((uint8_t*)_COMM_PAGE_DTRACE_DOF_ENABLED);
     return ( (dofEnabled & 1) );
+#else
+    return false;
+#endif
 }
 
 
 void Loader::vmAccountingSetSuspended(bool suspend, LogFunc logger)
 {
-#if __arm__ || __arm64__
+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
     // <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;
@@ -700,6 +773,21 @@ void Loader::vmAccountingSetSuspended(bool suspend, LogFunc logger)
 #endif
 }
 
+static const char* targetString(const MachOAnalyzerSet::FixupTarget& target)
+{
+    switch (target.kind ) {
+        case MachOAnalyzerSet::FixupTarget::Kind::rebase:
+            return "rebase";
+        case MachOAnalyzerSet::FixupTarget::Kind::bindAbsolute:
+            return "abolute";
+        case MachOAnalyzerSet::FixupTarget::Kind::bindToImage:
+            return target.foundSymbolName;
+        case MachOAnalyzerSet::FixupTarget::Kind::bindMissingSymbol:
+            return "missing";
+    }
+    return "";
+}
+
 void Loader::applyFixupsToImage(Diagnostics& diag, LoadedImage& info)
 {
     dyld3::ScopedTimer timer(DBG_DYLD_TIMING_APPLY_FIXUPS, (uint64_t)info.loadedAddress(), 0, 0);
@@ -712,93 +800,186 @@ void Loader::applyFixupsToImage(Diagnostics& diag, LoadedImage& info)
     
     if ( overrideOfCache )
         vmAccountingSetSuspended(true, _logFixups);
-    image->forEachFixup(^(uint64_t imageOffsetToRebase, bool& stop) {
-        // this is a rebase, add slide
-        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) {
-        // this is a bind, set to target
-        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 imageOffsetToStartsInfo, const Array<closure::Image::ResolvedSymbolTarget>& targets, bool& stop) {
-        // this is a chain of fixups, fix up all
-        STACK_ALLOC_OVERFLOW_SAFE_ARRAY(const void*, targetAddrs, 128);
-        targetAddrs.reserve(targets.count());
-        for (uint32_t i=0; i < targets.count(); ++i)
-            targetAddrs.push_back((void*)resolveTarget(targets[i]));
-        ((dyld3::MachOAnalyzer*)(info.loadedAddress()))->withChainStarts(diag, imageOffsetToStartsInfo, ^(const dyld_chained_starts_in_image* starts) {
-            info.loadedAddress()->fixupAllChainedFixups(diag, starts, slide, targetAddrs, ^(void* loc, void* newValue) {
-                _logFixups("dyld: fixup: %s:%p = %p\n", leafName, loc, newValue);
+    if ( image->fixupsNotEncoded() ) {
+        WrappedMachO wmo((MachOAnalyzer*)info.loadedAddress(), this, (void*)info.image());
+        wmo.forEachFixup(diag,
+        ^(uint64_t fixupLocRuntimeOffset, PointerMetaData pmd, const FixupTarget& target, bool& stop) {
+            uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + fixupLocRuntimeOffset);
+            uintptr_t value;
+            switch ( target.kind ) {
+                case MachOAnalyzerSet::FixupTarget::Kind::rebase:
+                case MachOAnalyzerSet::FixupTarget::Kind::bindToImage:
+                    value = (uintptr_t)(target.foundInImage._mh) + target.offsetInImage;
+                    break;
+                case MachOAnalyzerSet::FixupTarget::Kind::bindAbsolute:
+                    value = (uintptr_t)target.offsetInImage;
+                    break;
+                case MachOAnalyzerSet::FixupTarget::Kind::bindMissingSymbol:
+                    if ( _launchErrorInfo ) {
+                        _launchErrorInfo->kind              = DYLD_EXIT_REASON_SYMBOL_MISSING;
+                        _launchErrorInfo->clientOfDylibPath = info.image()->path();
+                        _launchErrorInfo->targetDylibPath   = target.foundInImage.path();
+                        _launchErrorInfo->symbol            = target.requestedSymbolName;
+                    }
+                    // we have no value to set, and forEachFixup() is about to finish
+                    return;
+            }
+    #if __has_feature(ptrauth_calls)
+            if ( pmd.authenticated )
+                value = MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::signPointer(value, fixUpLoc, pmd.usesAddrDiversity, pmd.diversity, pmd.key);
+    #endif
+            if ( pmd.high8 )
+                value |= ((uint64_t)pmd.high8 << 56);
+            _logFixups("dyld: fixup: %s:%p = %p (%s)\n", leafName, fixUpLoc, (void*)value, targetString(target));
+            *fixUpLoc = value;
+        },
+        ^(uint32_t cachedDylibIndex, uint32_t exportCacheOffset, const FixupTarget& target) {
+#if BUILDING_LIBDYLD && __x86_64__
+            // Full dlopen closures don't patch weak defs.  Bail out early if we are libdyld to match this behaviour
+            return;
+#endif
+            ((const DyldSharedCache*)_dyldCacheAddress)->forEachPatchableUseOfExport(cachedDylibIndex, exportCacheOffset, ^(dyld_cache_patchable_location patchLoc) {
+                uintptr_t* loc     = (uintptr_t*)(((uint8_t*)_dyldCacheAddress)+patchLoc.cacheOffset);
+                uintptr_t  newImpl = (uintptr_t)(target.foundInImage._mh) + target.offsetInImage + DyldSharedCache::getAddend(patchLoc);
+    #if __has_feature(ptrauth_calls)
+                if ( patchLoc.authenticated )
+                    newImpl = MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::signPointer(newImpl, loc, patchLoc.usesAddressDiversity, patchLoc.discriminator, patchLoc.key);
+    #endif
+                // ignore duplicate patch entries
+                if ( *loc != newImpl ) {
+                    _logFixups("dyld: cache patch: %p = 0x%0lX\n", loc, newImpl);
+                    *loc = newImpl;
+                }
             });
         });
-    },
-    ^(uint64_t imageOffsetToFixup) {
-        uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToFixup);
-        _logFixups("dyld: fixup objc image info: %s Setting objc image info for precomputed objc\n", leafName);
-
-        MachOAnalyzer::ObjCImageInfo *imageInfo = (MachOAnalyzer::ObjCImageInfo *)fixUpLoc;
-        ((MachOAnalyzer::ObjCImageInfo *)imageInfo)->flags |= MachOAnalyzer::ObjCImageInfo::dyldPreoptimized;
-    },
-    ^(uint64_t imageOffsetToBind, closure::Image::ResolvedSymbolTarget bindTarget, bool& stop) {
-        // this is a bind, set to target
-        uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToBind);
-        uintptr_t value = resolveTarget(bindTarget);
-        _logFixups("dyld: fixup objc protocol: %s:%p = %p\n", leafName, fixUpLoc, (void*)value);
-        *fixUpLoc = value;
-    },
-    ^(uint64_t imageOffsetToFixup, uint32_t selectorIndex, bool inSharedCache, bool &stop) {
-        // fixupObjCSelRefs
-        closure::Image::ResolvedSymbolTarget fixupTarget;
-        if ( inSharedCache ) {
-            const char* selectorString = _dyldCacheSelectorOpt->getEntryForIndex(selectorIndex);
-            fixupTarget.sharedCache.kind     = closure::Image::ResolvedSymbolTarget::kindSharedCache;
-            fixupTarget.sharedCache.offset   = (uint64_t)selectorString - (uint64_t)_dyldCacheAddress;
-        } else {
-            closure::ImageNum imageNum;
-            uint64_t vmOffset;
-            bool gotLocation = _closureSelectorOpt->getStringLocation(selectorIndex, _closureSelectorImages, imageNum, vmOffset);
-            assert(gotLocation);
-            fixupTarget.image.kind = closure::Image::ResolvedSymbolTarget::kindImage;
-            fixupTarget.image.imageNum = imageNum;
-            fixupTarget.image.offset = vmOffset;
-        }
-
-        uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToFixup);
-        uintptr_t value = resolveTarget(fixupTarget);
-        _logFixups("dyld: fixup objc selector: %s:%p(was '%s') = %p(now '%s')\n", leafName, fixUpLoc, (const char*)*fixUpLoc, (void*)value, (const char*)value);
-        *fixUpLoc = value;
-    }, ^(uint64_t imageOffsetToFixup, bool &stop) {
-        // fixupObjCStableSwift
-        // Class really is stable Swift, pretending to be pre-stable.
-        // Fix its lie.
-        uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToFixup);
-        uintptr_t value = ((*fixUpLoc) | MachOAnalyzer::ObjCClassInfo::FAST_IS_SWIFT_STABLE) & ~MachOAnalyzer::ObjCClassInfo::FAST_IS_SWIFT_LEGACY;
-        _logFixups("dyld: fixup objc stable Swift: %s:%p = %p\n", leafName, fixUpLoc, (void*)value);
-        *fixUpLoc = value;
-    }, ^(uint64_t imageOffsetToFixup, bool &stop) {
-        // TODO: Implement this
-    });
+#if BUILDING_LIBDYLD && TARGET_OS_OSX
+        // <rdar://problem/59265987> support old licenseware plugins on macOS using minimal closures
+        __block bool oldBinary = true;
+        info.loadedAddress()->forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+            if ( (platform == Platform::macOS) && (sdk >= 0x000A0F00) )
+                oldBinary = false;
+        });
+        if ( oldBinary ) {
+            // look for __DATA,__dyld section
+            info.loadedAddress()->forEachSection(^(const MachOAnalyzer::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+                if ( (strcmp(sectInfo.sectName, "__dyld") == 0) && (strcmp(sectInfo.segInfo.segName, "__DATA") == 0) ) {
+                    // dyld_func_lookup is second pointer in __dyld section
+                    uintptr_t* dyldSection = (uintptr_t*)(sectInfo.sectAddr + (uintptr_t)info.loadedAddress());
+                    _logFixups("dyld: __dyld section: %p = %p\n", &dyldSection[1], &dyld3::compatFuncLookup);
+                    dyldSection[1] = (uintptr_t)&dyld3::compatFuncLookup;
+                 }
+            });
+        }
+#endif
+    }
+    else {
+        if ( image->rebasesNotEncoded() ) {
+            // <rdar://problem/56172089> some apps have so many rebases the closure file is too big, instead we go back to rebase opcodes
+            ((MachOAnalyzer*)imageLoadAddress)->forEachRebase(diag, true, ^(uint64_t imageOffsetToRebase, bool& stop) {
+                // this is a rebase, add slide
+                uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToRebase);
+                *fixUpLoc += slide;
+                _logFixups("dyld: fixup: %s:%p += %p\n", leafName, fixUpLoc, (void*)slide);
+            });
+        }
+        image->forEachFixup(^(uint64_t imageOffsetToRebase, bool& stop) {
+            // this is a rebase, add slide
+            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) {
+            // this is a bind, set to target
+            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 imageOffsetToStartsInfo, const Array<closure::Image::ResolvedSymbolTarget>& targets, bool& stop) {
+            // this is a chain of fixups, fix up all
+            STACK_ALLOC_OVERFLOW_SAFE_ARRAY(const void*, targetAddrs, 128);
+            targetAddrs.reserve(targets.count());
+            for (uint32_t i=0; i < targets.count(); ++i)
+                targetAddrs.push_back((void*)resolveTarget(targets[i]));
+            ((dyld3::MachOAnalyzer*)(info.loadedAddress()))->withChainStarts(diag, imageOffsetToStartsInfo, ^(const dyld_chained_starts_in_image* starts) {
+                info.loadedAddress()->fixupAllChainedFixups(diag, starts, slide, targetAddrs, ^(void* loc, void* newValue) {
+                    _logFixups("dyld: fixup: %s:%p = %p\n", leafName, loc, newValue);
+                });
+            });
+        },
+        ^(uint64_t imageOffsetToFixup) {
+            uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToFixup);
+            _logFixups("dyld: fixup objc image info: %s Setting objc image info for precomputed objc\n", leafName);
+
+            MachOAnalyzer::ObjCImageInfo *imageInfo = (MachOAnalyzer::ObjCImageInfo *)fixUpLoc;
+            ((MachOAnalyzer::ObjCImageInfo *)imageInfo)->flags |= MachOAnalyzer::ObjCImageInfo::dyldPreoptimized;
+        },
+        ^(uint64_t imageOffsetToBind, closure::Image::ResolvedSymbolTarget bindTarget, bool& stop) {
+            // this is a bind, set to target
+            uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToBind);
+            uintptr_t value = resolveTarget(bindTarget);
+#if __has_feature(ptrauth_calls)
+            // Sign the ISA on arm64e.
+            // Unfortunately a hard coded value here is not ideal, but this is ABI so we aren't going to change it
+            // This matches the value in libobjc __objc_opt_ptrs: .quad x@AUTH(da, 27361, addr)
+            value = MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::signPointer(value, fixUpLoc, true, 27361, 2);
+#endif
+            _logFixups("dyld: fixup objc protocol: %s:%p = %p\n", leafName, fixUpLoc, (void*)value);
+            *fixUpLoc = value;
+        },
+        ^(uint64_t imageOffsetToFixup, uint32_t selectorIndex, bool inSharedCache, bool &stop) {
+            // fixupObjCSelRefs
+            closure::Image::ResolvedSymbolTarget fixupTarget;
+            if ( inSharedCache ) {
+                const char* selectorString = _dyldCacheSelectorOpt->getEntryForIndex(selectorIndex);
+                fixupTarget.sharedCache.kind     = closure::Image::ResolvedSymbolTarget::kindSharedCache;
+                fixupTarget.sharedCache.offset   = (uint64_t)selectorString - (uint64_t)_dyldCacheAddress;
+            } else {
+                closure::ImageNum imageNum;
+                uint64_t vmOffset;
+                bool gotLocation = _closureSelectorOpt->getStringLocation(selectorIndex, _closureSelectorImages, imageNum, vmOffset);
+                assert(gotLocation);
+                fixupTarget.image.kind = closure::Image::ResolvedSymbolTarget::kindImage;
+                fixupTarget.image.imageNum = imageNum;
+                fixupTarget.image.offset = vmOffset;
+            }
+            uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToFixup);
+            uintptr_t value = resolveTarget(fixupTarget);
+            _logFixups("dyld: fixup objc selector: %s:%p(was '%s') = %p(now '%s')\n", leafName, fixUpLoc, (const char*)*fixUpLoc, (void*)value, (const char*)value);
+            *fixUpLoc = value;
+        }, ^(uint64_t imageOffsetToFixup, bool &stop) {
+            // fixupObjCStableSwift
+            // Class really is stable Swift, pretending to be pre-stable.
+            // Fix its lie.
+            uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToFixup);
+            uintptr_t value = ((*fixUpLoc) | MachOAnalyzer::ObjCClassInfo::FAST_IS_SWIFT_STABLE) & ~MachOAnalyzer::ObjCClassInfo::FAST_IS_SWIFT_LEGACY;
+            _logFixups("dyld: fixup objc stable Swift: %s:%p = %p\n", leafName, fixUpLoc, (void*)value);
+            *fixUpLoc = value;
+        }, ^(uint64_t imageOffsetToFixup, bool &stop) {
+            // fixupObjCMethodList
+            // Set the method list to have the uniqued bit set
+            uint32_t* fixUpLoc = (uint32_t*)(imageLoadAddress + imageOffsetToFixup);
+            uint32_t value = (*fixUpLoc) | MachOAnalyzer::ObjCMethodList::methodListIsUniqued;
+            _logFixups("dyld: fixup objc method list: %s:%p = 0x%08x\n", leafName, fixUpLoc, value);
+            *fixUpLoc = value;
+        });
 
 #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);
+        __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
+    }
 
     // make any read-only data segments read-only
     if ( image->hasReadOnlyData() && !image->inDyldCache() ) {
@@ -828,13 +1009,112 @@ void Loader::setSegmentProtects(const LoadedImage& info, bool write)
 }
 #endif
 
+
+void Loader::forEachImage(void (^handler)(const LoadedImage& li, bool& stop)) const
+{
+    bool stop = false;
+    for (const LoadedImage& li : _existingImages) {
+        handler(li, stop);
+        if ( stop )
+            return;
+    }
+    for (const LoadedImage& li : _newImages) {
+        handler(li, stop);
+        if ( stop )
+            return;
+    }
+}
+
+void Loader::mas_forEachImage(void (^handler)(const WrappedMachO& wmo, bool hidden, bool& stop)) const
+{
+    forEachImage(^(const LoadedImage& li, bool& stop) {
+        WrappedMachO wmo((MachOAnalyzer*)li.loadedAddress(), this, (void*)li.image());
+        handler(wmo, li.hideFromFlatSearch(), stop);
+    });
+}
+
+
+bool Loader::wmo_missingSymbolResolver(const WrappedMachO* fromWmo, bool weakImport, bool lazyBind, const char* symbolName, const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const
+{
+    if ( weakImport ) {
+        target.offsetInImage = 0;
+        target.kind  = FixupTarget::Kind::bindAbsolute;
+        return true;
+    }
+
+    if ( lazyBind && _allowMissingLazies ) {
+        __block bool result = false;
+        forEachImage(^(const LoadedImage& li, bool& stop) {
+            if ( li.loadedAddress()->isDylib() && (strcmp(li.loadedAddress()->installName(), "/usr/lib/system/libdyld.dylib") == 0) ) {
+                WrappedMachO libdyldWmo((MachOAnalyzer*)li.loadedAddress(), this, (void*)li.image());
+                Diagnostics  diag;
+                if ( libdyldWmo.findSymbolIn(diag, "__dyld_missing_symbol_abort", 0, target) ) {
+                     // <rdar://problem/44315944> closures should bind missing lazy-bind symbols to a missing symbol handler in libdyld in flat namespace
+                     result = true;
+                }
+                stop = true;
+            }
+        });
+        return result;
+    }
+
+    // FIXME
+    return false;
+}
+
+
+void Loader::mas_mainExecutable(WrappedMachO& mainWmo) const
+{
+    forEachImage(^(const LoadedImage& li, bool& stop) {
+        if ( li.loadedAddress()->isMainExecutable() ) {
+            WrappedMachO wmo((MachOAnalyzer*)li.loadedAddress(), this, (void*)li.image());
+            mainWmo = wmo;
+            stop = true;
+        }
+    });
+}
+
+void* Loader::mas_dyldCache() const
+{
+    return (void*)_dyldCacheAddress;
+}
+
+
+bool Loader::wmo_dependent(const WrappedMachO* wmo, uint32_t depIndex, WrappedMachO& childWmo, bool& missingWeakDylib) const
+{
+    const closure::Image* image = (closure::Image*)(wmo->_other);
+    closure::ImageNum depImageNum = image->dependentImageNum(depIndex);
+    if ( depImageNum == closure::kMissingWeakLinkedImage ) {
+        missingWeakDylib = true;
+        return true;
+    }
+    else {
+        if ( LoadedImage* li = findImage(depImageNum) ) {
+            WrappedMachO foundWmo((MachOAnalyzer*)li->loadedAddress(), this, (void*)li->image());
+            missingWeakDylib = false;
+            childWmo = foundWmo;
+            return true;
+        }
+    }
+    return false;
+}
+
+
+const char* Loader::wmo_path(const WrappedMachO* wmo) const
+{
+    const closure::Image* image = (closure::Image*)(wmo->_other);
+    return image->path();
+}
+
+
+
 #if BUILDING_DYLD
 LoadedImage* Loader::LaunchImagesCache::findImage(closure::ImageNum imageNum,
                                                   Array<LoadedImage>& images) const {
     if ( (imageNum < _firstImageNum) || (imageNum >= _lastImageNum) )
         return nullptr;
 
-    uint64_t cacheIndex = imageNum - _firstImageNum;
+    unsigned int cacheIndex = imageNum - _firstImageNum;
     uint32_t imagesIndex = _imageIndices[cacheIndex];
     if ( imagesIndex == 0 )
         return nullptr;
@@ -843,16 +1123,17 @@ LoadedImage* Loader::LaunchImagesCache::findImage(closure::ImageNum imageNum,
     return &images[imagesIndex - 1];
 }
 
-void Loader::LaunchImagesCache::tryAddImage(closure::ImageNum imageNum,
-                                            uint64_t allImagesIndex) {
+void Loader::LaunchImagesCache::tryAddImage(closure::ImageNum imageNum, uint64_t allImagesIndex) const {
     if ( (imageNum < _firstImageNum) || (imageNum >= _lastImageNum) )
         return;
 
-    uint64_t cacheIndex = imageNum - _firstImageNum;
+    unsigned int cacheIndex = imageNum - _firstImageNum;
 
     // Note the index is offset by 1 so that 0's are not yet set
     _imageIndices[cacheIndex] = (uint32_t)allImagesIndex + 1;
 }
+ #endif
+
 
 void forEachLineInFile(const char* buffer, size_t bufferLen, void (^lineHandler)(const char* line, bool& stop))
 {
@@ -876,7 +1157,7 @@ void forEachLineInFile(const char* buffer, size_t bufferLen, void (^lineHandler)
 
 void forEachLineInFile(const char* path, void (^lineHandler)(const char* line, bool& stop))
 {
-    int fd = dyld::my_open(path, O_RDONLY, 0);
+    int fd = dyld3::open(path, O_RDONLY, 0);
     if ( fd != -1 ) {
         struct stat statBuf;
         if ( fstat(fd, &statBuf) == 0 ) {
@@ -890,14 +1171,13 @@ void forEachLineInFile(const char* path, void (^lineHandler)(const char* line, b
     }
 }
 
-#endif
 
 #if (BUILDING_LIBDYLD || BUILDING_DYLD)
     bool internalInstall()
     {
 #if TARGET_OS_SIMULATOR
         return false;
-#elif __IPHONE_OS_VERSION_MIN_REQUIRED
+#elif TARGET_OS_IPHONE
         uint32_t devFlags = *((uint32_t*)_COMM_PAGE_DEV_FIRM);
         return ( (devFlags & 1) == 1 );
 #else
index 5cb7cd13ca4e02a43e9b04cb31a7e64a91547161..8baece443b2417d39bdb03dc862d272348009563 100644 (file)
@@ -33,6 +33,7 @@
 
 #include "Closure.h"
 #include "MachOLoaded.h"
+#include "MachOAnalyzerSet.h"
 
 namespace objc_opt {
 struct objc_clsopt_t;
@@ -41,6 +42,15 @@ struct objc_selopt_t;
 
 namespace dyld3 {
 
+class RootsChecker;
+
+struct LaunchErrorInfo
+{
+    uintptr_t       kind;
+    const char*     clientOfDylibPath;
+    const char*     targetDylibPath;
+    const char*     symbol;
+};
 
 //
 // Tuple of info about a loaded image. Contains the loaded address, Image*, and state.
@@ -89,20 +99,23 @@ private:
 //
 // Utility class to recursively load dependents
 //
-class VIS_HIDDEN Loader {
+class VIS_HIDDEN Loader : public MachOAnalyzerSet {
 public:
         typedef bool (*LogFunc)(const char*, ...) __attribute__((format(printf, 1, 2)));
 
                         Loader(const Array<LoadedImage>& existingImages, Array<LoadedImage>& newImagesStorage,
                                const void* cacheAddress, const Array<const dyld3::closure::ImageArray*>& imagesArrays,
                                const closure::ObjCSelectorOpt* selOpt, const Array<closure::Image::ObjCSelectorImage>& selImages,
-                               LogFunc log_loads, LogFunc log_segments, LogFunc log_fixups, LogFunc log_dofs);
+                               const RootsChecker& rootsChecker, dyld3::Platform platform,
+                               LogFunc log_loads, LogFunc log_segments, LogFunc log_fixups, LogFunc log_dofs,
+                               bool allowMissingLazies=false, dyld3::LaunchErrorInfo* launchErrorInfo=nullptr);
 
     void                addImage(const LoadedImage&);
     void                completeAllDependents(Diagnostics& diag, bool& someCacheImageOverridden);
-    void                mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool fromOFI=false);
+    void                mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool fromOFI, bool* closureOutOfDate, bool* recoverable);
     uintptr_t           resolveTarget(closure::Image::ResolvedSymbolTarget target);
-    LoadedImage*        findImage(closure::ImageNum targetImageNum);
+    LoadedImage*        findImage(closure::ImageNum targetImageNum) const;
+    void                forEachImage(void (^handler)(const LoadedImage& li, bool& stop)) const;
 
     static void         unmapImage(LoadedImage& info);
     static bool         dtraceUserProbesEnabled();
@@ -126,7 +139,7 @@ private:
     struct LaunchImagesCache {
         LoadedImage*                    findImage(closure::ImageNum targetImageNum,
                                                   Array<LoadedImage>& images) const;
-        void                            tryAddImage(closure::ImageNum targetImageNum, uint64_t allImagesIndex);
+        void                            tryAddImage(closure::ImageNum targetImageNum, uint64_t allImagesIndex) const;
 
         static const uint64_t           _cacheSize = 128;
         static const closure::ImageNum  _firstImageNum = closure::kFirstLaunchClosureImageNum;
@@ -135,11 +148,11 @@ private:
         // Note, the cache stores "indices + 1" into the _allImages array.
         // 0 means we haven't cached an entry yet
         uint32_t                        _cacheStorage[_cacheSize] = { 0 };
-        Array<uint32_t>                 _imageIndices = { &_cacheStorage[0], _cacheSize, _cacheSize };
+        mutable Array<uint32_t>         _imageIndices = { &_cacheStorage[0], _cacheSize, _cacheSize };
     };
 #endif
 
-    void                mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI);
+    void                mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI, bool* closureOutOfDate);
     void                applyFixupsToImage(Diagnostics& diag, LoadedImage& info);
     void                registerDOFs(const Array<DOFInfo>& dofs);
     void                setSegmentProtects(const LoadedImage& info, bool write);
@@ -147,6 +160,16 @@ private:
     bool                sandboxBlockedOpen(const char* path);
     bool                sandboxBlockedStat(const char* path);
     bool                sandboxBlocked(const char* path, const char* kind);
+    void                unmapAllImages();
+
+    // MachOAnalyzerSet support
+    void            mas_forEachImage(void (^handler)(const WrappedMachO& anImage, bool hidden, bool& stop)) const override;
+    void            mas_mainExecutable(WrappedMachO& anImage) const override;
+    void*           mas_dyldCache() const override;
+    bool            wmo_dependent(const WrappedMachO* image, uint32_t depIndex, WrappedMachO& childObj, bool& missingWeakDylib) const override;
+    const char*     wmo_path(const WrappedMachO* image) const override;
+    bool            wmo_missingSymbolResolver(const WrappedMachO* fromWmo, bool weakImport, bool lazyBind, const char* symbolName, const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const override;
+
 
     const Array<LoadedImage>&                       _existingImages;
     Array<LoadedImage>&                             _newImages;
@@ -155,13 +178,17 @@ private:
     const objc_opt::objc_selopt_t*                  _dyldCacheSelectorOpt;
     const closure::ObjCSelectorOpt*                 _closureSelectorOpt;
     const Array<closure::Image::ObjCSelectorImage>& _closureSelectorImages;
+    const RootsChecker&                             _rootsChecker;
 #if BUILDING_DYLD
     LaunchImagesCache                               _launchImagesCache;
 #endif
+    bool                                            _allowMissingLazies;
+    dyld3::Platform                                 _platform;
     LogFunc                                         _logLoads;
     LogFunc                                         _logSegments;
     LogFunc                                         _logFixups;
     LogFunc                                         _logDofs;
+    dyld3::LaunchErrorInfo*                         _launchErrorInfo;
 };
 
 
index 8279c5c12d5f2322447c845a7d1198594dc3fb4a..4d8a04d277e08cfde68f4961a3dc80612999ba1e 100644 (file)
  */
 
 #include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
 #include <mach/mach.h>
 #include <assert.h>
 #include <limits.h>
@@ -29,6 +33,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <mach-o/reloc.h>
+#include <mach-o/x86_64/reloc.h>
 #include <mach-o/nlist.h>
 #include <TargetConditionals.h>
 
@@ -36,6 +41,8 @@
 #include "CodeSigningTypes.h"
 #include "Array.h"
 
+// FIXME: We should get this from cctools
+#define DYLD_CACHE_ADJ_V2_FORMAT 0x7F
 
 namespace dyld3 {
 
@@ -44,7 +51,7 @@ const MachOAnalyzer* MachOAnalyzer::validMainExecutable(Diagnostics& diag, const
                                                         const GradedArchs& archs, Platform platform)
 {
     const MachOAnalyzer* result = (const MachOAnalyzer*)mh;
-    if ( !result->validMachOForArchAndPlatform(diag, (size_t)sliceLength, path, archs, platform) )
+    if ( !result->validMachOForArchAndPlatform(diag, (size_t)sliceLength, path, archs, platform, true) )
         return nullptr;
     if ( !result->isDynamicExecutable() )
         return nullptr;
@@ -52,36 +59,16 @@ const MachOAnalyzer* MachOAnalyzer::validMainExecutable(Diagnostics& diag, const
     return result;
 }
 
-
-closure::LoadedFileInfo MachOAnalyzer::load(Diagnostics& diag, const closure::FileSystem& fileSystem,
-                                            const char* path, const GradedArchs& archs, Platform platform, char realerPath[MAXPATHLEN])
+bool MachOAnalyzer::loadFromBuffer(Diagnostics& diag, const closure::FileSystem& fileSystem,
+                                   const char* path, const GradedArchs& archs, Platform platform,
+                                   closure::LoadedFileInfo& info)
 {
-    // FIXME: This should probably be an assert, but if we happen to have a diagnostic here then something is wrong
-    // above us and we should quickly return instead of doing unnecessary work.
-    if (diag.hasError())
-        return closure::LoadedFileInfo();
-
-    closure::LoadedFileInfo info;
-    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 we now have an error, but succeeded, then we must have tried multiple paths, one of which errored, but
-    // then succeeded on a later path.  So clear the error.
-    if (diag.hasError())
-        diag.clearError();
-
     // 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, archs, sliceOffset, sliceLen, fatButMissingSlice) ) {
+    if ( fh->isFatFileWithSlice(diag, info.fileContentLen, archs, info.isOSBinary, sliceOffset, sliceLen, fatButMissingSlice) ) {
         // unmap anything before slice
         fileSystem.unloadPartialFile(info, sliceOffset, sliceLen);
         // Update the info to keep track of the new slice offset.
@@ -91,20 +78,20 @@ closure::LoadedFileInfo MachOAnalyzer::load(Diagnostics& diag, const closure::Fi
     else if ( diag.hasError() ) {
         // We must have generated an error in the fat file parsing so use that error
         fileSystem.unloadFile(info);
-        return closure::LoadedFileInfo();
+        return false;
     }
     else if ( fatButMissingSlice ) {
         diag.error("missing compatible arch in %s", path);
         fileSystem.unloadFile(info);
-        return closure::LoadedFileInfo();
+        return false;
     }
 
     const MachOAnalyzer* mh = (MachOAnalyzer*)info.fileContent;
 
     // validate is mach-o of requested arch and platform
-    if ( !mh->validMachOForArchAndPlatform(diag, (size_t)info.sliceLen, path, archs, platform) ) {
+    if ( !mh->validMachOForArchAndPlatform(diag, (size_t)info.sliceLen, path, archs, platform, info.isOSBinary) ) {
         fileSystem.unloadFile(info);
-        return closure::LoadedFileInfo();
+        return false;
     }
 
     // if has zero-fill expansion, re-map
@@ -113,7 +100,7 @@ closure::LoadedFileInfo MachOAnalyzer::load(Diagnostics& diag, const closure::Fi
     // on error, remove mappings and return nullptr
     if ( diag.hasError() ) {
         fileSystem.unloadFile(info);
-        return closure::LoadedFileInfo();
+        return false;
     }
 
     // now that LINKEDIT is at expected offset, finish validation
@@ -122,12 +109,93 @@ closure::LoadedFileInfo MachOAnalyzer::load(Diagnostics& diag, const closure::Fi
     // on error, remove mappings and return nullptr
     if ( diag.hasError() ) {
         fileSystem.unloadFile(info);
+        return false;
+    }
+
+    return true;
+}
+
+
+closure::LoadedFileInfo MachOAnalyzer::load(Diagnostics& diag, const closure::FileSystem& fileSystem,
+                                            const char* path, const GradedArchs& archs, Platform platform, char realerPath[MAXPATHLEN])
+{
+    // FIXME: This should probably be an assert, but if we happen to have a diagnostic here then something is wrong
+    // above us and we should quickly return instead of doing unnecessary work.
+    if (diag.hasError())
+        return closure::LoadedFileInfo();
+
+    closure::LoadedFileInfo info;
+    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 we now have an error, but succeeded, then we must have tried multiple paths, one of which errored, but
+    // then succeeded on a later path.  So clear the error.
+    if (diag.hasError())
+        diag.clearError();
+
+    bool loaded = loadFromBuffer(diag, fileSystem, path, archs, platform, info);
+    if (!loaded)
+        return {};
     return info;
 }
 
+// for use with already mmap()ed file
+bool MachOAnalyzer::isOSBinary(int fd, uint64_t sliceOffset, uint64_t sliceSize) const
+{
+#ifdef F_GETSIGSINFO
+    if ( fd == -1 )
+        return false;
+
+    uint32_t sigOffset;
+    uint32_t sigSize;
+    if ( !this->hasCodeSignature(sigOffset, sigSize) )
+        return false;
+
+    // register code signature
+    fsignatures_t sigreg;
+    sigreg.fs_file_start = sliceOffset;                // start of mach-o slice in fat file
+    sigreg.fs_blob_start = (void*)(long)sigOffset;     // start of CD in mach-o file
+    sigreg.fs_blob_size  = sigSize;                    // size of CD
+    if ( ::fcntl(fd, F_ADDFILESIGS_RETURN, &sigreg) == -1 )
+        return false;
+
+    // ask if code signature is for something in the OS
+    fgetsigsinfo siginfo = { (off_t)sliceOffset, GETSIGSINFO_PLATFORM_BINARY, 0 };
+    if ( ::fcntl(fd, F_GETSIGSINFO, &siginfo) == -1 )
+        return false;
+
+    return (siginfo.fg_sig_is_platform);
+#else
+    return false;
+#endif
+}
+
+// for use when just the fat_header has been read
+bool MachOAnalyzer::sliceIsOSBinary(int fd, uint64_t sliceOffset, uint64_t sliceSize)
+{
+    if ( fd == -1 )
+        return false;
+
+    // need to mmap() slice so we can find the code signature
+       void* mappedSlice = ::mmap(nullptr, sliceSize, PROT_READ, MAP_PRIVATE, fd, sliceOffset);
+       if ( mappedSlice == MAP_FAILED )
+               return false;
+
+    const MachOAnalyzer* ma = (MachOAnalyzer*)mappedSlice;
+    bool result = ma->isOSBinary(fd, sliceOffset, sliceSize);
+    ::munmap(mappedSlice, sliceSize);
+
+    return result;
+}
+
+
+
 #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
@@ -145,7 +213,7 @@ uint64_t MachOAnalyzer::mappedSize() const
     return vmSpace;
 }
 
-bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics& diag, size_t sliceLength, const char* path, const GradedArchs& archs, Platform platform) const
+bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics& diag, size_t sliceLength, const char* path, const GradedArchs& archs, Platform reqPlatform, bool isOSBinary) const
 {
     // must start with mach-o magic value
     if ( (this->magic != MH_MAGIC) && (this->magic != MH_MAGIC_64) ) {
@@ -153,7 +221,7 @@ bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics& diag, size_t slice
         return false;
     }
 
-    if ( !archs.grade(this->cputype, this->cpusubtype) ) {
+    if ( !archs.grade(this->cputype, this->cpusubtype, isOSBinary) ) {
         diag.error("could not use '%s' because it is not a compatible arch", path);
         return false;
     }
@@ -163,11 +231,12 @@ bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics& diag, size_t slice
         case MH_EXECUTE:
         case MH_DYLIB:
         case MH_BUNDLE:
-        case MH_DYLINKER:
            break;
-#if BUILDING_DYLDINFO
+#if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL || BUILDING_RUN_STATIC
         // Allow offline tools to analyze binaries dyld doesn't load
+        case MH_DYLINKER:
         case MH_KEXT_BUNDLE:
+        case MH_FILESET:
             break;
 #endif
         default:
@@ -182,21 +251,55 @@ bool MachOAnalyzer::validMachOForArchAndPlatform(Diagnostics& diag, size_t slice
 
     // filter out static executables
     if ( (this->filetype == MH_EXECUTE) && !isDynamicExecutable() ) {
-#if !BUILDING_DYLDINFO
+#if !BUILDING_DYLDINFO && !BUILDING_APP_CACHE_UTIL
         // dyldinfo should be able to inspect static executables such as the kernel
         diag.error("could not use '%s' because it is a static executable", path);
         return false;
 #endif
     }
 
-    // must match requested platform (do this after load commands are validated)
-    if ( !this->supportsPlatform(platform) ) {
-        diag.error("could not use '%s' because it was built for a different platform", path);
+    // HACK: If we are asking for no platform, then make sure the binary doesn't have one
+#if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL
+    if ( isFileSet() ) {
+        // A statically linked kernel collection should contain a 0 platform
+        __block bool foundPlatform = false;
+        __block bool foundBadPlatform = false;
+        forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+            foundPlatform = true;
+            if ( platform != Platform::unknown ) {
+                foundBadPlatform = true;
+            }
+        });
+        if (!foundPlatform) {
+            diag.error("could not use '%s' because we expected it to have a platform", path);
+            return false;
+        }
+        if (foundBadPlatform) {
+            diag.error("could not use '%s' because is has the wrong platform", path);
+            return false;
+        }
+    } else if ( reqPlatform == Platform::unknown ) {
+        // Unfortunately the static kernel has a platform, but kext's don't, so we can't
+        // verify the platform of the kernel.
+        if ( !isStaticExecutable() ) {
+            __block bool foundPlatform = false;
+            forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+                foundPlatform = true;
+            });
+            if (foundPlatform) {
+                diag.error("could not use '%s' because we expected it to have no platform", path);
+                return false;
+            }
+        }
+    } else
+#endif
+    if ( !this->loadableIntoProcess(reqPlatform, path) ) {
+        diag.error("could not use '%s' because it was not built for platform %s", path, MachOFile::platformName(reqPlatform));
         return false;
     }
 
     // validate dylib loads
-    if ( !validEmbeddedPaths(diag, platform, path) )
+    if ( !validEmbeddedPaths(diag, reqPlatform, path) )
         return false;
 
     // validate segments
@@ -225,7 +328,7 @@ bool MachOAnalyzer::validLinkedit(Diagnostics& diag, const char* path) const
             return false;
     }
 #if SUPPORT_ARCH_arm64e
-    else if ( (this->cputype == CPU_TYPE_ARM64) && (this->cpusubtype == CPU_SUBTYPE_ARM64E) ) {
+    else if ( (this->cputype == CPU_TYPE_ARM64) && (this->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E) ) {
         if ( !validChainedFixupsInfoOldArm64e(diag, path) )
             return false;
     }
@@ -297,11 +400,22 @@ const MachOAnalyzer* MachOAnalyzer::remapIfZeroFill(Diagnostics& diag, const clo
             diag.error("vm_allocate failure");
             return nullptr;
         }
+
         // re-map each segment read-only, with runtime layout
-        uint64_t textSegVmAddr = preferredLoadAddress();
+#if BUILDING_APP_CACHE_UTIL
+        // The auxKC is mapped with __DATA first, so we need to get either the __DATA or __TEXT depending on what is earliest
+        __block uint64_t baseAddress = ~0ULL;
+        forEachSegment(^(const SegmentInfo& info, bool& stop) {
+            baseAddress = std::min(baseAddress, info.vmAddr);
+        });
+        uint64_t textSegVMAddr = preferredLoadAddress();
+#else
+        uint64_t baseAddress = preferredLoadAddress();
+#endif
+
         forEachSegment(^(const SegmentInfo& segmentInfo, bool& stop) {
-            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 ( (segmentInfo.fileSize != 0) && (segmentInfo.vmSize != 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-baseAddress));
                 if ( r != KERN_SUCCESS ) {
                     diag.error("vm_copy() failure");
                     stop = true;
@@ -315,6 +429,30 @@ const MachOAnalyzer* MachOAnalyzer::remapIfZeroFill(Diagnostics& diag, const clo
             // make the new mapping read-only
             ::vm_protect(mach_task_self(), newMappedAddr, (vm_size_t)vmSpaceRequired, false, VM_PROT_READ);
 
+#if BUILDING_APP_CACHE_UTIL
+            if ( textSegVMAddr != baseAddress ) {
+                info.unload = [](const closure::LoadedFileInfo& info) {
+                    // Unloading binaries where __DATA is first requires working out the real range of the binary
+                    // The fileContent points at the mach_header, not the actaul start of the file content, unfortunately.
+                    const MachOAnalyzer* ma = (const MachOAnalyzer*)info.fileContent;
+                    __block uint64_t baseAddress = ~0ULL;
+                    ma->forEachSegment(^(const SegmentInfo& info, bool& stop) {
+                        baseAddress = std::min(baseAddress, info.vmAddr);
+                    });
+                    uint64_t textSegVMAddr = ma->preferredLoadAddress();
+
+                    uint64_t basePointerOffset = textSegVMAddr - baseAddress;
+                    uint8_t* bufferStart = (uint8_t*)info.fileContent - basePointerOffset;
+                    ::vm_deallocate(mach_task_self(), (vm_address_t)bufferStart, (size_t)info.fileContentLen);
+                };
+
+                // And update the file content to the new location
+                info.fileContent = (const void*)(newMappedAddr + textSegVMAddr - baseAddress);
+                info.fileContentLen = vmSpaceRequired;
+                return (const MachOAnalyzer*)info.fileContent;
+            }
+#endif
+
             // 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);
@@ -346,6 +484,10 @@ void MachOAnalyzer::analyzeSegmentsLayout(uint64_t& vmSpace, bool& hasZeroFill)
             return;
         if ( segmentInfo.writable() && (segmentInfo.fileSize !=  segmentInfo.vmSize) )
             writeExpansion = true; // zerofill at end of __DATA
+        if ( segmentInfo.vmSize == 0 ) {
+            // Always zero fill if we have zero-sized segments
+            writeExpansion = true;
+        }
         if ( segmentInfo.vmAddr < lowestVmAddr )
             lowestVmAddr = segmentInfo.vmAddr;
         if ( segmentInfo.vmAddr+segmentInfo.vmSize > highestVmAddr )
@@ -358,12 +500,76 @@ void MachOAnalyzer::analyzeSegmentsLayout(uint64_t& vmSpace, bool& hasZeroFill)
     totalVmSpace = (totalVmSpace + (pageSize - 1)) & ~(pageSize - 1);
     bool hasHole = (totalVmSpace != sumVmSizes); // segments not contiguous
 
+    // The aux KC may have __DATA first, in which case we always want to vm_copy to the right place
+    bool hasOutOfOrderSegments = false;
+#if BUILDING_APP_CACHE_UTIL
+    uint64_t textSegVMAddr = preferredLoadAddress();
+    hasOutOfOrderSegments = textSegVMAddr != lowestVmAddr;
+#endif
+
     vmSpace     = totalVmSpace;
-    hasZeroFill = writeExpansion || hasHole;
+    hasZeroFill = writeExpansion || hasHole || hasOutOfOrderSegments;
 }
 
 bool MachOAnalyzer::enforceFormat(Malformed kind) const
 {
+#if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL || BUILDING_RUN_STATIC
+    // HACK: If we are the kernel, we have a different format to enforce
+    if ( isFileSet() ) {
+        bool result = false;
+        switch (kind) {
+        case Malformed::linkeditOrder:
+        case Malformed::linkeditAlignment:
+        case Malformed::dyldInfoAndlocalRelocs:
+            result = true;
+            break;
+        case Malformed::segmentOrder:
+        // The aux KC has __DATA first
+            result = false;
+            break;
+        case Malformed::linkeditPermissions:
+        case Malformed::executableData:
+        case Malformed::writableData:
+        case Malformed::codeSigAlignment:
+        case Malformed::sectionsAddrRangeWithinSegment:
+            result = true;
+            break;
+        case Malformed::textPermissions:
+            // The kernel has its own __TEXT_EXEC for executable memory
+            result = false;
+            break;
+        }
+        return result;
+    }
+
+    if ( isStaticExecutable() ) {
+        bool result = false;
+        switch (kind) {
+        case Malformed::linkeditOrder:
+        case Malformed::linkeditAlignment:
+        case Malformed::dyldInfoAndlocalRelocs:
+            result = true;
+            break;
+        case Malformed::segmentOrder:
+            result = false;
+            break;
+        case Malformed::linkeditPermissions:
+        case Malformed::executableData:
+        case Malformed::codeSigAlignment:
+        case Malformed::textPermissions:
+        case Malformed::sectionsAddrRangeWithinSegment:
+            result = true;
+            break;
+        case Malformed::writableData:
+            // The kernel has __DATA_CONST marked as r/o
+            result = false;
+            break;
+        }
+        return result;
+    }
+
+#endif
+
     __block bool result = false;
     forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
         switch (platform) {
@@ -380,11 +586,17 @@ bool MachOAnalyzer::enforceFormat(Malformed kind) const
             case Malformed::linkeditPermissions:
             case Malformed::textPermissions:
             case Malformed::executableData:
+            case Malformed::writableData:
             case Malformed::codeSigAlignment:
                 // enforce these checks on new binaries only
                 if (sdk >= 0x000A0F00) // macOS 10.15
                     result = true;
                 break;
+            case Malformed::sectionsAddrRangeWithinSegment:
+                // enforce these checks on new binaries only
+                if (sdk >= 0x000A1000) // macOS 10.16
+                    result = true;
+                break;
             }
             break;
         case Platform::iOS:
@@ -393,6 +605,7 @@ bool MachOAnalyzer::enforceFormat(Malformed kind) const
             case Malformed::dyldInfoAndlocalRelocs:
             case Malformed::textPermissions:
             case Malformed::executableData:
+            case Malformed::writableData:
                 result = true;
                 break;
             case Malformed::linkeditAlignment:
@@ -403,6 +616,11 @@ bool MachOAnalyzer::enforceFormat(Malformed kind) const
                 if (sdk >= 0x000D0000) // iOS 13
                     result = true;
                 break;
+            case Malformed::sectionsAddrRangeWithinSegment:
+                // enforce these checks on new binaries only
+                if (sdk >= 0x000E0000) // iOS 14
+                    result = true;
+                break;
             }
             break;
         default:
@@ -622,8 +840,8 @@ bool MachOAnalyzer::validSegments(Diagnostics& diag, const char* path, size_t fi
     if ( badPermissions || badSize )
         return false;
     if ( !hasTEXT ) {
-       diag.error("in '%s' missing __TEXT segment", path);
-       return false;
+        diag.error("in '%s' missing __TEXT segment", path);
+        return false;
     }
     if ( !hasLINKEDIT ) {
        diag.error("in '%s' missing __LINKEDIT segment", path);
@@ -678,16 +896,23 @@ bool MachOAnalyzer::validSegments(Diagnostics& diag, const char* path, size_t fi
             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);
+                    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);
+                    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;
+                    bool ignoreError = !enforceFormat(Malformed::sectionsAddrRangeWithinSegment);
+#if BUILDING_APP_CACHE_UTIL
+                    if ( (seg->vmsize == 0) && !strcmp(seg->segname, "__CTF") )
+                        ignoreError = true;
+#endif
+                    if ( !ignoreError ) {
+                        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;
+                    }
                 }
             }
         }
@@ -718,10 +943,21 @@ bool MachOAnalyzer::validSegments(Diagnostics& diag, const char* path, size_t fi
 
 bool MachOAnalyzer::validMain(Diagnostics& diag, const char* path) const
 {
+    const char* executableTextSegmentName = "__TEXT";
+#if BUILDING_APP_CACHE_UTIL
+    // The kernel has __start in __TEXT_EXEC, or for x86_64 it's __HIB
+    if ( isStaticExecutable() ) {
+        if ( isArch("x86_64") || isArch("x86_64h") )
+            executableTextSegmentName = "__HIB";
+        else
+            executableTextSegmentName = "__TEXT_EXEC";
+    }
+#endif
+
     __block uint64_t textSegStartAddr = 0;
     __block uint64_t textSegStartSize = 0;
     forEachSegment(^(const SegmentInfo& info, bool& stop) {
-        if ( strcmp(info.segName, "__TEXT") == 0 ) {
+        if ( strcmp(info.segName, executableTextSegmentName) == 0 ) {
             textSegStartAddr = info.vmAddr;
             textSegStartSize = info.vmSize;
             stop = true;
@@ -779,7 +1015,7 @@ bool MachOAnalyzer::validMain(Diagnostics& diag, const char* path) const
                 }
 #endif
                 else if ( (startAddress < textSegStartAddr) || (startAddress >= textSegStartAddr+textSegStartSize) ) {
-                    diag.error("LC_UNIXTHREAD entry not in __TEXT segment");
+                    diag.error("LC_UNIXTHREAD entry not in %s segment", executableTextSegmentName);
                     stop = true;
                 }
                 break;
@@ -787,7 +1023,15 @@ bool MachOAnalyzer::validMain(Diagnostics& diag, const char* path) const
     });
     if ( diag.hasError() )
         return false;
-    if ( diag.noError() && (mainCount+threadCount == 1) )
+
+    if ( this->builtForPlatform(Platform::driverKit) ) {
+        if ( mainCount + threadCount == 0 )
+            return true;
+        diag.error("no LC_MAIN allowed for driverkit");
+        return false;
+    }
+
+    if ( mainCount+threadCount == 1 )
         return true;
 
     if ( mainCount + threadCount == 0 )
@@ -844,6 +1088,10 @@ bool MachOAnalyzer::validLinkeditLayout(Diagnostics& diag, const char* path) con
         if ( leInfo.exportsTrie->datasize != 0 )
             *bp++ = {"exports trie",            ptrSize, leInfo.exportsTrie->dataoff, leInfo.exportsTrie->datasize};
     }
+    if ( leInfo.chainedFixups != nullptr ) {
+        if ( leInfo.chainedFixups->datasize != 0 )
+            *bp++ = {"chained fixups",          ptrSize, leInfo.chainedFixups->dataoff, leInfo.chainedFixups->datasize};
+    }
     
     if ( leInfo.dynSymTab != nullptr ) {
         if ( leInfo.dynSymTab->nlocrel != 0 )
@@ -887,13 +1135,25 @@ bool MachOAnalyzer::validLinkeditLayout(Diagnostics& diag, const char* path) con
             return false;
         }
     }
-    if ( (leInfo.dyldInfo == nullptr) && (leInfo.dynSymTab == nullptr) ) {
+
+    bool checkMissingDyldInfo = true;
+#if BUILDING_DYLDINFO || BUILDING_APP_CACHE_UTIL
+    checkMissingDyldInfo = !isFileSet() && !isStaticExecutable() && !isKextBundle();
+#endif
+    if ( (leInfo.dyldInfo == nullptr) && (leInfo.dynSymTab == nullptr) && checkMissingDyldInfo ) {
         diag.error("in '%s' malformed mach-o misssing LC_DYLD_INFO and LC_DYSYMTAB", path);
         return false;
     }
+
+    // FIXME: Remove this hack
+#if BUILDING_APP_CACHE_UTIL
+    if ( isFileSet() )
+        return true;
+#endif
+
     const unsigned long blobCount = bp - blobs;
     if ( blobCount == 0 ) {
-        diag.error("in '%s' malformed mach-o misssing LINKEDIT", path);
+        diag.error("in '%s' malformed mach-o missing LINKEDIT", path);
         return false;
     }
 
@@ -971,7 +1231,7 @@ bool MachOAnalyzer::validLinkeditLayout(Diagnostics& diag, const char* path) con
 
 
 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
+                                      bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind) const
 {
     if ( !segIndexSet ) {
         diag.error("in '%s' %s missing preceding REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", path, opcodeName);
@@ -985,9 +1245,10 @@ bool MachOAnalyzer::invalidRebaseState(Diagnostics& diag, const char* opcodeName
         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() ) {
+    switch ( kind )  {
+        case Rebase::pointer32:
+        case Rebase::pointer64:
+            if ( !segments[segmentIndex].writable() && enforceFormat(Malformed::writableData) ) {
                 diag.error("in '%s' %s pointer rebase is in non-writable segment", path, opcodeName);
                 return true;
             }
@@ -996,8 +1257,8 @@ bool MachOAnalyzer::invalidRebaseState(Diagnostics& diag, const char* opcodeName
                 return true;
             }
             break;
-        case REBASE_TYPE_TEXT_ABSOLUTE32:
-        case REBASE_TYPE_TEXT_PCREL32:
+        case Rebase::textAbsolute32:
+        case Rebase::textPCrel32:
             if ( !segments[segmentIndex].textRelocs ) {
                 diag.error("in '%s' %s text rebase is in segment that does not support text relocations", path, opcodeName);
                 return true;
@@ -1011,8 +1272,8 @@ bool MachOAnalyzer::invalidRebaseState(Diagnostics& diag, const char* opcodeName
                 return true;
             }
             break;
-        default:
-            diag.error("in '%s' %s unknown rebase type %d", path, opcodeName, type);
+        case Rebase::unknown:
+            diag.error("in '%s' %s unknown rebase type", path, opcodeName);
             return true;
     }
     return false;
@@ -1030,8 +1291,8 @@ void MachOAnalyzer::getAllSegmentsInfos(Diagnostics& diag, SegmentInfo segments[
 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) )
+                          bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind, bool& stop) {
+        if ( invalidRebaseState(diag, opcodeName, path, leInfo, segments, segIndexSet, ptrSize, segmentIndex, segmentOffset, kind) )
             stop = true;
     });
     return diag.noError();
@@ -1043,8 +1304,8 @@ void MachOAnalyzer::forEachTextRebase(Diagnostics& diag, void (^handler)(uint64_
     __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 )
+                          bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind, bool& stop) {
+        if ( kind != Rebase::textAbsolute32 )
             return;
         if ( !startVmAddrSet ) {
             for (int i=0; i <= segmentIndex; ++i) {
@@ -1061,8 +1322,7 @@ void MachOAnalyzer::forEachTextRebase(Diagnostics& diag, void (^handler)(uint64_
     });
 }
 
-
-void MachOAnalyzer::forEachRebase(Diagnostics& diag, bool ignoreLazyPointers, void (^handler)(uint64_t runtimeOffset, bool& stop)) const
+void MachOAnalyzer::forEachRebase(Diagnostics& diag, void (^callback)(uint64_t runtimeOffset, bool isLazyPointerRebase, bool& stop)) const
 {
     __block bool     startVmAddrSet = false;
     __block uint64_t startVmAddr    = 0;
@@ -1070,22 +1330,29 @@ void MachOAnalyzer::forEachRebase(Diagnostics& diag, bool ignoreLazyPointers, vo
     __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;
-            }
-        });
-    }
+    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;
+                          bool segIndexSet, uint32_t ptrSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind, bool& stop) {
+        switch ( kind ) {
+            case Rebase::unknown:
+                return;
+            case Rebase::pointer32:
+            case Rebase::pointer64:
+                // We only handle these kinds for now.
+                break;
+            case Rebase::textPCrel32:
+            case Rebase::textAbsolute32:
+                return;
+        }
         if ( !startVmAddrSet ) {
             for (int i=0; i < segmentIndex; ++i) {
                 if ( strcmp(segments[i].segName, "__TEXT") == 0 ) {
@@ -1096,7 +1363,7 @@ void MachOAnalyzer::forEachRebase(Diagnostics& diag, bool ignoreLazyPointers, vo
             }
         }
         uint64_t rebaseVmAddr  = segments[segmentIndex].vmAddr + segmentOffset;
-        bool skipRebase = false;
+        bool isLazyPointerRebase = false;
         if ( (rebaseVmAddr >= lpVmAddr) && (rebaseVmAddr < lpEndVmAddr) ) {
             // rebase is in lazy pointer section
             uint64_t lpValue = 0;
@@ -1111,19 +1378,44 @@ void MachOAnalyzer::forEachRebase(Diagnostics& diag, bool ignoreLazyPointers, vo
                 bool isLazyStub = contentIsRegularStub(helperContent);
                 // ignore rebases for normal lazy pointers, but leave rebase for resolver helper stub
                 if ( isLazyStub )
-                    skipRebase = true;
+                    isLazyPointerRebase = 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);
-        }
+        uint64_t runtimeOffset = rebaseVmAddr - startVmAddr;
+        callback(runtimeOffset, isLazyPointerRebase, stop);
+    });
+}
+
+
+
+void MachOAnalyzer::forEachRebase(Diagnostics& diag, bool ignoreLazyPointers, void (^handler)(uint64_t runtimeOffset, bool& stop)) const
+{
+    forEachRebase(diag, ^(uint64_t runtimeOffset, bool isLazyPointerRebase, bool& stop) {
+        if ( isLazyPointerRebase && ignoreLazyPointers )
+            return;
+        handler(runtimeOffset, stop);
     });
 }
 
+bool MachOAnalyzer::hasStompedLazyOpcodes() const
+{
+    // if first eight bytes of lazy opcodes are zeros, then the opcodes have been stomped
+    bool result = false;
+    uint32_t size;
+    if ( const uint8_t* p = (uint8_t*)getLazyBindOpcodes(size) ) {
+        if ( size > 8 ) {
+            uint64_t content;
+            memcpy(&content, p, 8);
+            if ( content == 0 )
+                result = true;
+        }
+    }
+
+    return result;
+}
 
 bool MachOAnalyzer::contentIsRegularStub(const uint8_t* helperContent) const
 {
@@ -1141,8 +1433,8 @@ bool MachOAnalyzer::contentIsRegularStub(const uint8_t* helperContent) const
     return false;
 }
 
-static int uint32Sorter(const void* l, const void* r) {
-    if ( *((uint32_t*)l) < *((uint32_t*)r) )
+static int relocSorter(const void* l, const void* r) {
+    if ( ((relocation_info*)l)->r_address < ((relocation_info*)r)->r_address )
         return -1;
     else
         return 1;
@@ -1152,24 +1444,26 @@ static int uint32Sorter(const void* l, const void* r) {
 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
+                                                 Rebase kind, bool& stop)) const
 {
     LinkEditInfo leInfo;
     getLinkEditPointers(diag, leInfo);
     if ( diag.hasError() )
         return;
 
-    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.linkeditSegIndex+1);
+    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
     getAllSegmentsInfos(diag, segmentsInfo);
     if ( diag.hasError() )
         return;
 
+    const Rebase pointerRebaseKind = is64() ? Rebase::pointer64 : Rebase::pointer32;
+
     if ( leInfo.dyldInfo != nullptr ) {
         const uint8_t* const start = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->rebase_off);
         const uint8_t* const end   = start + leInfo.dyldInfo->rebase_size;
         const uint8_t* p           = start;
         const uint32_t ptrSize     = pointerSize();
-        uint8_t  type = 0;
+        Rebase   kind = Rebase::unknown;
         int      segIndex = 0;
         uint64_t segOffset = 0;
         uint64_t count;
@@ -1187,7 +1481,20 @@ void MachOAnalyzer::forEachRebase(Diagnostics& diag,
                     stop = true;
                     break;
                 case REBASE_OPCODE_SET_TYPE_IMM:
-                    type = immediate;
+                    switch ( immediate ) {
+                        case REBASE_TYPE_POINTER:
+                            kind = pointerRebaseKind;
+                            break;
+                        case REBASE_TYPE_TEXT_ABSOLUTE32:
+                            kind = Rebase::textAbsolute32;
+                            break;
+                        case REBASE_TYPE_TEXT_PCREL32:
+                            kind = Rebase::textPCrel32;
+                            break;
+                        default:
+                            kind = Rebase::unknown;
+                            break;
+                    }
                     break;
                 case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
                     segIndex = immediate;
@@ -1202,7 +1509,7 @@ void MachOAnalyzer::forEachRebase(Diagnostics& diag,
                     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);
+                        handler("REBASE_OPCODE_DO_REBASE_IMM_TIMES", leInfo, segmentsInfo, segIndexSet, ptrSize, segIndex, segOffset, kind, stop);
                         segOffset += ptrSize;
                         if ( stop )
                             break;
@@ -1211,14 +1518,14 @@ void MachOAnalyzer::forEachRebase(Diagnostics& diag,
                 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);
+                        handler("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo, segmentsInfo, segIndexSet, ptrSize, segIndex, segOffset, kind, 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);
+                    handler("REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo, segmentsInfo, segIndexSet, ptrSize, segIndex, segOffset, kind, stop);
                     segOffset += read_uleb128(diag, p, end) + ptrSize;
                     break;
                 case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
@@ -1227,7 +1534,7 @@ void MachOAnalyzer::forEachRebase(Diagnostics& diag,
                         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);
+                        handler("REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB", leInfo, segmentsInfo, segIndexSet, ptrSize, segIndex, segOffset, kind, stop);
                         segOffset += skip + ptrSize;
                         if ( stop )
                             break;
@@ -1237,42 +1544,70 @@ void MachOAnalyzer::forEachRebase(Diagnostics& diag,
                     diag.error("unknown rebase opcode 0x%02X", opcode);
             }
         }
+        return;
     }
-    else if ( leInfo.chainedFixups != nullptr ) {
+
+    if ( leInfo.chainedFixups != nullptr ) {
         // binary uses chained fixups, so do nothing
+        // The kernel collections need to support both chained and classic relocations
+        // If we are anything other than a kernel collection, then return here as we won't have
+        // anything else to do.
+        if ( !isFileSet() )
+            return;
     }
-    else {
+
+    if ( leInfo.dynSymTab != nullptr ) {
         // old binary, walk relocations
-        const uint64_t                  relocsStartAddress = relocBaseAddress(segmentsInfo, leInfo.layout.linkeditSegIndex);
+        const uint64_t                  relocsStartAddress = localRelocBaseAddress(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);
+        STACK_ALLOC_OVERFLOW_SAFE_ARRAY(relocation_info, relocs, 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;
+                bool shouldEmitError = true;
+#if BUILDING_APP_CACHE_UTIL
+                if ( usesClassicRelocationsInKernelCollection() && (reloc->r_length == 2) && (relocSize == 3) )
+                    shouldEmitError = false;
+#endif
+                if ( shouldEmitError ) {
+                    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);
+            relocs.push_back(*reloc);
         }
-        if ( !relocAddrs.empty() ) {
-            ::qsort(&relocAddrs[0], relocAddrs.count(), sizeof(uint32_t), &uint32Sorter);
-            for (uint32_t addrOff : relocAddrs) {
+        if ( !relocs.empty() ) {
+            ::qsort(&relocs[0], relocs.count(), sizeof(relocation_info), &relocSorter);
+            for (relocation_info reloc : relocs) {
+                uint32_t addrOff = reloc.r_address;
                 uint32_t segIndex  = 0;
                 uint64_t segOffset = 0;
-                if ( segIndexAndOffsetForAddress(relocsStartAddress+addrOff, segmentsInfo, leInfo.layout.linkeditSegIndex, segIndex, segOffset) ) {
-                    uint8_t type = REBASE_TYPE_POINTER;
+                uint64_t addr = 0;
+#if BUILDING_APP_CACHE_UTIL
+                // xnu for x86_64 has __HIB mapped before __DATA, so offsets appear to be
+                // negative
+                if ( isStaticExecutable() || isFileSet() ) {
+                    addr = relocsStartAddress + (int32_t)addrOff;
+                } else {
+                    addr = relocsStartAddress + addrOff;
+                }
+#else
+                addr = relocsStartAddress + addrOff;
+#endif
+                if ( segIndexAndOffsetForAddress(addr, segmentsInfo, leInfo.layout.linkeditSegIndex, segIndex, segOffset) ) {
+                    Rebase kind = (reloc.r_length == 2) ? Rebase::pointer32 : Rebase::pointer64;
                     if ( this->cputype == CPU_TYPE_I386 ) {
                         if ( segmentsInfo[segIndex].executable() )
-                            type = REBASE_TYPE_TEXT_ABSOLUTE32;
+                            kind = Rebase::textAbsolute32;
                     }
-                    handler("local relocation", leInfo, segmentsInfo, true, ptrSize, segIndex, segOffset, type , stop);
+                    handler("local relocation", leInfo, segmentsInfo, true, ptrSize, segIndex, segOffset, kind, stop);
                 }
                 else {
                     diag.error("local relocation has out of range r_address");
@@ -1288,7 +1623,7 @@ void MachOAnalyzer::forEachRebase(Diagnostics& diag,
             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);
+                handler("local relocation", leInfo, segmentsInfo, true, ptrSize, segIndex, segOffset, pointerRebaseKind, indStop);
             }
             else {
                 diag.error("local relocation has out of range r_address");
@@ -1310,10 +1645,16 @@ bool MachOAnalyzer::segIndexAndOffsetForAddress(uint64_t addr, const SegmentInfo
     return false;
 }
 
-uint64_t MachOAnalyzer::relocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const
+uint64_t MachOAnalyzer::localRelocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const
 {
-    if ( is64() ) {
-        // x86_64 reloc base address is first writable segment
+    if ( isArch("x86_64") || isArch("x86_64h") ) {
+#if BUILDING_APP_CACHE_UTIL
+        if ( isKextBundle() ) {
+            // for kext bundles the reloc base address starts at __TEXT segment
+            return segmentsInfos[0].vmAddr;
+        }
+#endif
+        // for all other kinds, the x86_64 reloc base address starts at first writable segment (usually __DATA)
         for (uint32_t i=0; i < segCount; ++i) {
             if ( segmentsInfos[i].writable() )
                 return segmentsInfos[i].vmAddr;
@@ -1322,6 +1663,31 @@ uint64_t MachOAnalyzer::relocBaseAddress(const SegmentInfo segmentsInfos[], uint
     return segmentsInfos[0].vmAddr;
 }
 
+uint64_t MachOAnalyzer::externalRelocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const
+{
+    // Dyld caches are too large for a raw r_address, so everything is an offset from the base address
+    if ( inDyldCache() ) {
+        return preferredLoadAddress();
+    }
+
+#if BUILDING_APP_CACHE_UTIL
+    if ( isKextBundle() ) {
+        // for kext bundles the reloc base address starts at __TEXT segment
+        return preferredLoadAddress();
+    }
+#endif
+
+    if ( isArch("x86_64") || isArch("x86_64h") ) {
+        // for x86_64 reloc base address starts at first writable segment (usually __DATA)
+        for (uint32_t i=0; i < segCount; ++i) {
+            if ( segmentsInfos[i].writable() )
+                return segmentsInfos[i].vmAddr;
+        }
+    }
+    // For everyone else we start at 0
+    return 0;
+}
+
 
 
 void MachOAnalyzer::forEachIndirectPointer(Diagnostics& diag, void (^handler)(uint64_t pointerAddress, bool bind, int bindLibOrdinal, const char* bindSymbolName, 
@@ -1344,6 +1710,12 @@ void MachOAnalyzer::forEachIndirectPointer(Diagnostics& diag, void (^handler)(ui
     uint32_t                symCount                 = leInfo.symTab->nsyms;
     uint32_t                poolSize                 = leInfo.symTab->strsize;
     __block bool            stop                     = false;
+
+    // Old kexts put S_LAZY_SYMBOL_POINTERS on the __got section, even if they didn't have indirect symbols to prcess.
+    // In that case, skip the loop as there shouldn't be anything to process
+    if ( (indirectSymbolTableCount == 0) && isKextBundle() )
+        return;
+
     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);
@@ -1429,7 +1801,7 @@ bool MachOAnalyzer::validBindInfo(Diagnostics& diag, const char* path) const
             stop = true;
         }
     }, ^(const char* symbolName) {
-    }, ^() { });
+    });
     return diag.noError();
 }
 
@@ -1477,8 +1849,14 @@ bool MachOAnalyzer::invalidBindState(Diagnostics& diag, const char* opcodeName,
             }
             break;
         case BIND_TYPE_TEXT_ABSOLUTE32:
-        case BIND_TYPE_TEXT_PCREL32:
-            if ( !segments[segmentIndex].textRelocs ) {
+        case BIND_TYPE_TEXT_PCREL32: {
+            // Text relocations are permitted in x86_64 kexts
+            bool forceAllowTextRelocs = false;
+#if BUILDING_APP_CACHE_UTIL
+            if ( isKextBundle() && (isArch("x86_64") || isArch("x86_64h")) )
+                forceAllowTextRelocs = true;
+#endif
+            if ( !forceAllowTextRelocs && !segments[segmentIndex].textRelocs ) {
                 diag.error("in '%s' %s text bind is in segment that does not support text relocations", path, opcodeName);
                 return true;
             }
@@ -1491,6 +1869,7 @@ bool MachOAnalyzer::invalidBindState(Diagnostics& diag, const char* opcodeName,
                 return true;
             }
             break;
+        }
         default:
             diag.error("in '%s' %s unknown bind type %d", path, opcodeName, type);
             return true;
@@ -1498,10 +1877,9 @@ bool MachOAnalyzer::invalidBindState(Diagnostics& diag, const char* opcodeName,
     return false;
 }
 
-void MachOAnalyzer::forEachBind(Diagnostics& diag, void (^handler)(uint64_t runtimeOffset, int libOrdinal, const char* symbolName,
+void MachOAnalyzer::forEachBind(Diagnostics& diag, void (^handler)(uint64_t runtimeOffset, int libOrdinal, uint8_t type, const char* symbolName,
                                                                   bool weakImport, bool lazyBind, uint64_t addend, bool& stop),
-                                                   void (^strongHandler)(const char* symbolName),
-                                                   void (^missingLazyBindHandler)()) const
+                                                   void (^strongHandler)(const char* symbolName)) const
 {
     __block bool     startVmAddrSet = false;
     __block uint64_t startVmAddr    = 0;
@@ -1520,21 +1898,28 @@ void MachOAnalyzer::forEachBind(Diagnostics& diag, void (^handler)(uint64_t runt
         }
         uint64_t bindVmOffset  = segments[segmentIndex].vmAddr + segmentOffset;
         uint64_t runtimeOffset = bindVmOffset - startVmAddr;
-        handler(runtimeOffset, libOrdinal, symbolName, weakImport, lazyBind, addend, stop);
+        handler(runtimeOffset, libOrdinal, type, symbolName, weakImport, lazyBind, addend, stop);
     }, ^(const char* symbolName) {
         strongHandler(symbolName);
-    }, ^() {
-        missingLazyBindHandler();
     });
 }
 
+void MachOAnalyzer::forEachBind(Diagnostics& diag, void (^handler)(uint64_t runtimeOffset, int libOrdinal, const char* symbolName,
+                                                                  bool weakImport, bool lazyBind, uint64_t addend, bool& stop),
+                                                   void (^strongHandler)(const char* symbolName)) const
+{
+    forEachBind(diag, ^(uint64_t runtimeOffset, int libOrdinal, uint8_t type, const char* symbolName,
+                        bool weakImport, bool lazyBind, uint64_t addend, bool &stop) {
+        handler(runtimeOffset, libOrdinal, symbolName, weakImport, lazyBind, addend, stop);
+    }, strongHandler);
+}
+
 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, bool lazyBind, uint64_t addend, bool& stop),
-                                 void (^strongHandler)(const char* symbolName),
-                                 void (^missingLazyBindHandler)()) const
+                                 void (^strongHandler)(const char* symbolName)) const
 {
     const uint32_t  ptrSize = this->pointerSize();
     bool            stop    = false;
@@ -1544,7 +1929,7 @@ void MachOAnalyzer::forEachBind(Diagnostics& diag,
     if ( diag.hasError() )
         return;
 
-    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.linkeditSegIndex+1);
+    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
     getAllSegmentsInfos(diag, segmentsInfo);
     if ( diag.hasError() )
         return;
@@ -1723,9 +2108,9 @@ void MachOAnalyzer::forEachBind(Diagnostics& diag,
                         break;
                 }
             }
-            if ( lazyDoneCount > lazyBindCount+7 )
-                missingLazyBindHandler();
-            //    diag.error("lazy bind opcodes missing binds");
+            if ( lazyDoneCount > lazyBindCount+7 ) {
+                // diag.error("lazy bind opcodes missing binds");
+            }
         }
         if ( diag.hasError() )
             return;
@@ -1816,9 +2201,9 @@ void MachOAnalyzer::forEachBind(Diagnostics& diag,
     else if ( leInfo.chainedFixups != nullptr ) {
         // binary uses chained fixups, so do nothing
     }
-    else {
+    else if ( leInfo.dynSymTab != nullptr ) {
         // old binary, process external relocations
-        const uint64_t                  relocsStartAddress = relocBaseAddress(segmentsInfo, leInfo.layout.linkeditSegIndex);
+        const uint64_t                  relocsStartAddress = externalRelocBaseAddress(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() ;
@@ -1830,13 +2215,35 @@ void MachOAnalyzer::forEachBind(Diagnostics& diag,
         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;
+            bool isBranch = false;
+#if BUILDING_APP_CACHE_UTIL
+            if ( isKextBundle() ) {
+                // kext's may have other kinds of relocations, eg, branch relocs.  Skip them
+                if ( isArch("x86_64") || isArch("x86_64h") ) {
+                    if ( reloc->r_type == X86_64_RELOC_BRANCH ) {
+                        if ( reloc->r_length != 2 ) {
+                            diag.error("external relocation has wrong r_length");
+                            break;
+                        }
+                        if ( reloc->r_pcrel != true ) {
+                            diag.error("external relocation should be pcrel");
+                            break;
+                        }
+                        isBranch = true;
+                    }
+                }
+            }
+#endif
+
+            if ( !isBranch ) {
+                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;
@@ -1859,12 +2266,13 @@ void MachOAnalyzer::forEachBind(Diagnostics& diag,
                         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);
+                        uint64_t        addend     = (reloc->r_length == 3) ? *((uint64_t*)content) : *((uint32_t*)content);
                         // Handle defined weak def symbols which need to get a special ordinal
                         if ( ((n_type & N_TYPE) == N_SECT) && ((n_type & N_EXT) != 0) && ((n_desc & N_WEAK_DEF) != 0) )
                             libOrdinal = BIND_SPECIAL_DYLIB_WEAK_LOOKUP;
+                        uint8_t type = isBranch ? BIND_TYPE_TEXT_PCREL32 : BIND_TYPE_POINTER;
                         handler("external relocation", leInfo, segmentsInfo, true, true, dylibCount, libOrdinal,
-                                ptrSize, segIndex, segOffset, BIND_TYPE_POINTER, symbolName, weakImport, false, addend, stop);
+                                ptrSize, segIndex, segOffset, type, symbolName, weakImport, false, addend, stop);
                     }
                 }
             }
@@ -1900,7 +2308,7 @@ bool MachOAnalyzer::validChainedFixupsInfo(Diagnostics& diag, const char* path)
     if ( diag.hasError() )
         return false;
 
-    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.linkeditSegIndex+1);
+    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
     getAllSegmentsInfos(diag, segmentsInfo);
     if ( diag.hasError() )
         return false;
@@ -1915,14 +2323,11 @@ bool MachOAnalyzer::validChainedFixupsInfo(Diagnostics& diag, const char* path)
         diag.error("chained fixups, starts_offset exceeds LC_DYLD_CHAINED_FIXUPS size");
         return false;
     }
-    if ( chainsHeader->imports_offset >= leInfo.chainedFixups->datasize )  {
+    if ( chainsHeader->imports_offset > leInfo.chainedFixups->datasize )  {
         diag.error("chained fixups, imports_offset exceeds LC_DYLD_CHAINED_FIXUPS size");
         return false;
     }
-    if ( chainsHeader->imports_count >= 0xFFFF )  {
-        diag.error("chained fixups, imports_count exceeds 64K");
-        return false;
-    }
+   
     uint32_t formatEntrySize;
     switch ( chainsHeader->imports_format ) {
         case DYLD_CHAINED_IMPORT:
@@ -1950,11 +2355,33 @@ bool MachOAnalyzer::validChainedFixupsInfo(Diagnostics& diag, const char* path)
     // validate dyld_chained_starts_in_image
     const dyld_chained_starts_in_image* startsInfo = (dyld_chained_starts_in_image*)((uint8_t*)chainsHeader + chainsHeader->starts_offset);
     if ( startsInfo->seg_count != leInfo.layout.linkeditSegIndex+1 ) {
-         diag.error("chained fixups, seg_count does not match number of segments");
-         return false;
+        // We can have fewer segments than the count, so long as those we are missing have no relocs
+        // This can happen because __CTF is inserted by ctf_insert after linking, and between __DATA and __LINKEDIT, but has no relocs
+        // ctf_insert updates the load commands to put __CTF between __DATA and __LINKEDIT, but doesn't update the chained fixups data structures
+        if ( startsInfo->seg_count > (leInfo.layout.linkeditSegIndex + 1) ) {
+            diag.error("chained fixups, seg_count exceeds number of segments");
+            return false;
+        }
+
+        // We can have fewer segments than the count, so long as those we are missing have no relocs
+        uint32_t numNoRelocSegments = 0;
+        uint32_t numExtraSegments = (leInfo.layout.lastSegIndex + 1) - startsInfo->seg_count;
+        for (unsigned i = 0; i != numExtraSegments; ++i) {
+            // Check each extra segment before linkedit
+            const SegmentInfo& segInfo = segmentsInfo[leInfo.layout.linkeditSegIndex - (i + 1)];
+            if ( segInfo.vmSize == 0 )
+                ++numNoRelocSegments;
+        }
+
+        if ( numNoRelocSegments != numExtraSegments ) {
+            diag.error("chained fixups, seg_count does not match number of segments");
+            return false;
+        }
     }
     const uint64_t baseAddress = preferredLoadAddress();
     uint32_t maxValidPointerSeen = 0;
+    uint16_t pointer_format_for_all = 0;
+    bool pointer_format_found = false;
     const uint8_t* endOfStarts = (uint8_t*)chainsHeader + chainsHeader->imports_offset;
     for (uint32_t i=0; i < startsInfo->seg_count; ++i) {
         uint32_t segInfoOffset = startsInfo->seg_info_offset[i];
@@ -1972,8 +2399,16 @@ bool MachOAnalyzer::validChainedFixupsInfo(Diagnostics& diag, const char* path)
             diag.error("chained fixups, page_size not 4KB or 16KB in segment #%d", i);
             return false;
         }
-        if ( segInfo->pointer_format > 10 ) {
-            diag.error("chained fixups, unknown pointer_format %d in segment #%d", segInfo->pointer_format, i);
+        if ( segInfo->pointer_format > 12 ) {
+            diag.error("chained fixups, unknown pointer_format in segment #%d", i);
+            return false;
+        }
+        if ( !pointer_format_found ) {
+            pointer_format_for_all = segInfo->pointer_format;
+            pointer_format_found = true;
+        }
+        if ( segInfo->pointer_format != pointer_format_for_all) {
+            diag.error("chained fixups, pointer_format not same for all segments %d and %d", segInfo->pointer_format, pointer_format_for_all);
             return false;
         }
         if ( segInfo->segment_offset != (segmentsInfo[i].vmAddr - baseAddress) ) {
@@ -2032,6 +2467,30 @@ bool MachOAnalyzer::validChainedFixupsInfo(Diagnostics& diag, const char* path)
         }
 
     }
+    // validate import table size can fit
+    if ( chainsHeader->imports_count != 0 ) {
+        uint32_t maxBindOrdinal = 0;
+        switch (pointer_format_for_all) {
+            case DYLD_CHAINED_PTR_32:
+                maxBindOrdinal = 0x0FFFFF; // 20-bits
+                break;
+            case DYLD_CHAINED_PTR_ARM64E:
+            case DYLD_CHAINED_PTR_ARM64E_USERLAND:
+            case DYLD_CHAINED_PTR_ARM64E_OFFSET:
+                maxBindOrdinal = 0x00FFFF; // 16-bits
+                break;
+            case DYLD_CHAINED_PTR_64:
+            case DYLD_CHAINED_PTR_64_OFFSET:
+            case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
+                maxBindOrdinal = 0xFFFFFF; // 24 bits
+                break;
+        }
+        if ( chainsHeader->imports_count >= maxBindOrdinal )  {
+            diag.error("chained fixups, imports_count (%d) exceeds max of %d", chainsHeader->imports_count, maxBindOrdinal);
+            return false;
+        }
+    }
+
     // validate max_valid_pointer is larger than last segment
     if ( (maxValidPointerSeen != 0) && !inDyldCache() ) {
         uint64_t lastSegmentLastVMAddr = segmentsInfo[leInfo.layout.linkeditSegIndex-1].vmAddr + segmentsInfo[leInfo.layout.linkeditSegIndex-1].vmSize;
@@ -2112,7 +2571,7 @@ void MachOAnalyzer::parseOrgArm64eChainedFixups(Diagnostics& diag, void (^target
     if ( diag.hasError() )
         return;
 
-    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.linkeditSegIndex+1);
+    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
     getAllSegmentsInfos(diag, segmentsInfo);
     if ( diag.hasError() )
         return;
@@ -2218,7 +2677,7 @@ void MachOAnalyzer::forEachChainedFixupTarget(Diagnostics& diag, void (^callback
     if ( diag.hasError() )
         return;
 
-    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.linkeditSegIndex+1);
+    BLOCK_ACCCESSIBLE_ARRAY(SegmentInfo, segmentsInfo, leInfo.layout.lastSegIndex+1);
     getAllSegmentsInfos(diag, segmentsInfo);
     if ( diag.hasError() )
         return;
@@ -2352,7 +2811,7 @@ bool MachOAnalyzer::hasProgramVars(Diagnostics& diag, uint32_t& progVarsOffset)
     // macOS 10.5               ProgramVars are in __dyld section in main executable and 7 pointers in size
     // macOS 10.4 and earlier   ProgramVars need to be looked up by name in nlist of main executable
 
-    uint32_t offset;
+    uint64_t offset;
     bool     usesCRT;
     if ( getEntry(offset, usesCRT) && usesCRT ) {
         // is pre-10.8 program
@@ -2373,10 +2832,74 @@ bool MachOAnalyzer::hasProgramVars(Diagnostics& diag, uint32_t& progVarsOffset)
     return false;
 }
 
-bool MachOAnalyzer::hasInitializer(Diagnostics& diag, bool contentRebased, const void* dyldCache) const
+// Convert from a (possibly) live pointer to a vmAddr
+uint64_t MachOAnalyzer::VMAddrConverter::convertToVMAddr(uint64_t value) const {
+    if ( contentRebased ) {
+        if ( value == 0 )
+            return 0;
+        // The value may have been signed.  Strip the signature if that is the case
+#if __has_feature(ptrauth_calls)
+        value = (uint64_t)__builtin_ptrauth_strip((void*)value, ptrauth_key_asia);
+#endif
+        value -= slide;
+        return value;
+    }
+    if ( chainedPointerFormat != 0 ) {
+        auto* chainedValue = (MachOAnalyzer::ChainedFixupPointerOnDisk*)&value;
+        uint64_t targetRuntimeOffset;
+        if ( chainedValue->isRebase(chainedPointerFormat, preferredLoadAddress, targetRuntimeOffset) ) {
+            value = preferredLoadAddress + targetRuntimeOffset;
+        }
+        return value;
+    }
+
+#if !(BUILDING_LIBDYLD || BUILDING_DYLD)
+    typedef MachOAnalyzer::VMAddrConverter VMAddrConverter;
+    if ( sharedCacheChainedPointerFormat != VMAddrConverter::SharedCacheFormat::none ) {
+        switch ( sharedCacheChainedPointerFormat ) {
+            case VMAddrConverter::SharedCacheFormat::none:
+                assert(false);
+            case VMAddrConverter::SharedCacheFormat::v2_x86_64_tbi: {
+                const uint64_t   deltaMask    = 0x00FFFF0000000000;
+                const uint64_t   valueMask    = ~deltaMask;
+                const uint64_t   valueAdd     = preferredLoadAddress;
+                value = (value & valueMask);
+                if ( value != 0 ) {
+                    value += valueAdd;
+                }
+                break;
+            }
+            case VMAddrConverter::SharedCacheFormat::v3: {
+                // Just use the chained pointer format for arm64e
+                auto* chainedValue = (MachOAnalyzer::ChainedFixupPointerOnDisk*)&value;
+                uint64_t targetRuntimeOffset;
+                if ( chainedValue->isRebase(DYLD_CHAINED_PTR_ARM64E, preferredLoadAddress,
+                                            targetRuntimeOffset) ) {
+                    value = preferredLoadAddress + targetRuntimeOffset;
+                }
+                break;
+            }
+        }
+        return value;
+    }
+#endif
+
+    return value;
+}
+
+MachOAnalyzer::VMAddrConverter MachOAnalyzer::makeVMAddrConverter(bool contentRebased) const {
+    MachOAnalyzer::VMAddrConverter vmAddrConverter;
+    vmAddrConverter.preferredLoadAddress   = preferredLoadAddress();
+    vmAddrConverter.slide                  = getSlide();
+    vmAddrConverter.chainedPointerFormat   = hasChainedFixups() ? chainedPointerFormat() : 0;
+    vmAddrConverter.contentRebased         = contentRebased;
+    return vmAddrConverter;
+}
+
+bool MachOAnalyzer::hasInitializer(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, const void* dyldCache) const
 {
     __block bool result = false;
-    forEachInitializer(diag, contentRebased, ^(uint32_t offset) {
+    forEachInitializer(diag, vmAddrConverter, ^(uint32_t offset) {
         result = true;
     }, dyldCache);
     return result;
@@ -2433,7 +2956,7 @@ public:
     dyld3::OverflowSafeArray<SegmentRange> segments { localAlloc, sizeof(localAlloc) / sizeof(localAlloc[0]) };
 };
 
-void MachOAnalyzer::forEachInitializer(Diagnostics& diag, bool contentRebased, void (^callback)(uint32_t offset), const void* dyldCache) const
+void MachOAnalyzer::forEachInitializer(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, void (^callback)(uint32_t offset), const void* dyldCache) const
 {
     __block SegmentRanges executableSegments;
     forEachSegment(^(const SegmentInfo& info, bool& stop) {
@@ -2472,33 +2995,12 @@ void MachOAnalyzer::forEachInitializer(Diagnostics& diag, bool contentRebased, v
 
     // next any function pointers in mod-init section
     const unsigned ptrSize          = pointerSize();
-    const bool     useChainedFixups = hasChainedFixups();
-    const uint16_t pointerFormat    = useChainedFixups ? this->chainedPointerFormat() : 0;
     forEachInitializerPointerSection(diag, ^(uint32_t sectionOffset, uint32_t sectionSize, const uint8_t* content, bool& stop) {
         if ( ptrSize == 8 ) {
             const uint64_t* initsStart = (uint64_t*)content;
             const uint64_t* initsEnd   = (uint64_t*)((uint8_t*)content + sectionSize);
             for (const uint64_t* p=initsStart; p < initsEnd; ++p) {
-                uint64_t anInit = *p;
-                if ( contentRebased ) {
-                    // The function pointer may have been signed.  Strip the signature if that is the case
-#if __has_feature(ptrauth_calls)
-                    anInit = (uint64_t)__builtin_ptrauth_strip((void*)anInit, ptrauth_key_asia);
-#endif
-                    anInit -= slide;
-                }
-                else if ( useChainedFixups ) {
-                    uint64_t initFuncRuntimeOffset;
-                    ChainedFixupPointerOnDisk* aChainedInit = (ChainedFixupPointerOnDisk*)p;
-                    if ( aChainedInit->isRebase(pointerFormat, loadAddress, initFuncRuntimeOffset) ) {
-                        anInit = loadAddress+initFuncRuntimeOffset;
-                    }
-                    else {
-                        diag.error("initializer is not rebased");
-                        stop = true;
-                        break;
-                    }
-                }
+                uint64_t anInit = vmAddrConverter.convertToVMAddr(*p);
                 if ( !executableSegments.contains(anInit) ) {
                      diag.error("initializer 0x%0llX does not point within executable segment", anInit);
                      stop = true;
@@ -2511,22 +3013,7 @@ void MachOAnalyzer::forEachInitializer(Diagnostics& diag, bool contentRebased, v
             const uint32_t* initsStart = (uint32_t*)content;
             const uint32_t* initsEnd   = (uint32_t*)((uint8_t*)content + sectionSize);
             for (const uint32_t* p=initsStart; p < initsEnd; ++p) {
-                uint32_t anInit = *p;
-                if ( contentRebased ) {
-                    anInit -= slide;
-                }
-                else if ( useChainedFixups ) {
-                    uint64_t initFuncRuntimeOffset;
-                    ChainedFixupPointerOnDisk* aChainedInit = (ChainedFixupPointerOnDisk*)p;
-                    if ( aChainedInit->isRebase(pointerFormat, loadAddress, initFuncRuntimeOffset) ) {
-                        anInit = (uint32_t)(loadAddress+initFuncRuntimeOffset);
-                    }
-                    else {
-                        diag.error("initializer is not rebased");
-                        stop = true;
-                        break;
-                    }
-                }
+                uint32_t anInit = (uint32_t)vmAddrConverter.convertToVMAddr(*p);
                 if ( !executableSegments.contains(anInit) ) {
                      diag.error("initializer 0x%0X does not point within executable segment", anInit);
                      stop = true;
@@ -2575,16 +3062,16 @@ void MachOAnalyzer::forEachInitializer(Diagnostics& diag, bool contentRebased, v
     });
 }
 
-bool MachOAnalyzer::hasTerminators(Diagnostics& diag, bool contentRebased) const
+bool MachOAnalyzer::hasTerminators(Diagnostics& diag, const VMAddrConverter& vmAddrConverter) const
 {
     __block bool result = false;
-    forEachTerminator(diag, contentRebased, ^(uint32_t offset) {
+    forEachTerminator(diag, vmAddrConverter, ^(uint32_t offset) {
         result = true;
     });
     return result;
 }
 
-void MachOAnalyzer::forEachTerminator(Diagnostics& diag, bool contentRebased, void (^callback)(uint32_t offset)) const
+void MachOAnalyzer::forEachTerminator(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, void (^callback)(uint32_t offset)) const
 {
     __block SegmentRanges executableSegments;
     forEachSegment(^(const SegmentInfo& info, bool& stop) {
@@ -2603,11 +3090,8 @@ void MachOAnalyzer::forEachTerminator(Diagnostics& diag, bool contentRebased, vo
 
     // next any function pointers in mod-term section
     const unsigned ptrSize          = pointerSize();
-    const bool     useChainedFixups = hasChainedFixups();
     forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool& stop) {
         if ( (info.sectFlags & SECTION_TYPE) == S_MOD_TERM_FUNC_POINTERS ) {
-            uint64_t initFuncRuntimeOffset;
-            const uint16_t pointerFormat = useChainedFixups ? this->chainedPointerFormat() : 0;
             const uint8_t* content;
             content = (uint8_t*)(info.sectAddr + slide);
             if ( (info.sectSize % ptrSize) != 0 ) {
@@ -2629,25 +3113,7 @@ void MachOAnalyzer::forEachTerminator(Diagnostics& diag, bool contentRebased, vo
                 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 ) {
-                        // The function pointer may have been signed.  Strip the signature if that is the case
-#if __has_feature(ptrauth_calls)
-                        anInit = (uint64_t)__builtin_ptrauth_strip((void*)anInit, ptrauth_key_asia);
-#endif
-                        anInit -= slide;
-                    }
-                    else if ( useChainedFixups ) {
-                        ChainedFixupPointerOnDisk* aChainedInit = (ChainedFixupPointerOnDisk*)p;
-                        if ( aChainedInit->isRebase(pointerFormat, loadAddress, initFuncRuntimeOffset) ) {
-                            anInit = loadAddress+initFuncRuntimeOffset;
-                        }
-                        else {
-                            diag.error("terminator is not rebased");
-                            stop = true;
-                            break;
-                        }
-                    }
+                    uint64_t anInit = vmAddrConverter.convertToVMAddr(*p);
                     if ( !executableSegments.contains(anInit) ) {
                          diag.error("terminator 0x%0llX does not point within executable segment", anInit);
                          stop = true;
@@ -2660,21 +3126,7 @@ void MachOAnalyzer::forEachTerminator(Diagnostics& diag, bool contentRebased, vo
                 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;
-                    }
-                    else if ( useChainedFixups ) {
-                        ChainedFixupPointerOnDisk* aChainedInit = (ChainedFixupPointerOnDisk*)p;
-                        if ( aChainedInit->isRebase(pointerFormat, loadAddress, initFuncRuntimeOffset) ) {
-                            anInit = (uint32_t)(loadAddress+initFuncRuntimeOffset);
-                        }
-                        else {
-                            diag.error("terminator is not rebased");
-                            stop = true;
-                            break;
-                        }
-                    }
+                    uint32_t anInit = (uint32_t)vmAddrConverter.convertToVMAddr(*p);
                     if ( !executableSegments.contains(anInit) ) {
                          diag.error("terminator 0x%0X does not point within executable segment", anInit);
                          stop = true;
@@ -2718,10 +3170,26 @@ bool MachOAnalyzer::hasObjC() const
     return result;
 }
 
+bool MachOAnalyzer::usesObjCGarbageCollection() 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) ) {
+            const uint64_t  slide = (uint64_t)this - preferredLoadAddress();
+            const uint32_t* flags = (uint32_t*)(info.sectAddr + slide);
+            if ( flags[1] & 4 )
+                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) ) {
+    if ( (this->cputype == CPU_TYPE_I386) && this->builtForPlatform(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) {
@@ -2761,6 +3229,27 @@ bool MachOAnalyzer::hasPlusLoadMethod(Diagnostics& diag) const
     return result;
 }
 
+bool MachOAnalyzer::isSwiftLibrary() const
+{
+    struct objc_image_info {
+        int32_t version;
+        uint32_t flags;
+    };
+    
+    int64_t slide = getSlide();
+    __block bool result = false;
+    forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+        if ( (strncmp(sectInfo.sectName, "__objc_imageinfo", 16) == 0) && (strncmp(sectInfo.segInfo.segName, "__DATA", 6) == 0) ) {
+            objc_image_info* info =  (objc_image_info*)((uint8_t*)sectInfo.sectAddr + slide);
+            uint32_t swiftVersion = ((info->flags >> 8) & 0xFF);
+            if ( swiftVersion )
+                result = true;
+            stop = true;
+        }
+    });
+    return result;
+}
+
 const void* MachOAnalyzer::getRebaseOpcodes(uint32_t& size) const
 {
     Diagnostics diag;
@@ -2809,6 +3298,30 @@ const void* MachOAnalyzer::getSplitSeg(uint32_t& size) const
     return getLinkEditContent(leInfo.layout, leInfo.splitSegInfo->dataoff);
 }
 
+bool MachOAnalyzer::hasSplitSeg() const {
+    uint32_t splitSegSize = 0;
+    const void* splitSegStart = getSplitSeg(splitSegSize);
+    return splitSegStart != nullptr;
+}
+
+bool MachOAnalyzer::isSplitSegV1() const {
+    uint32_t splitSegSize = 0;
+    const void* splitSegStart = getSplitSeg(splitSegSize);
+    if (!splitSegStart)
+        return false;
+
+    return (*(const uint8_t*)splitSegStart) != DYLD_CACHE_ADJ_V2_FORMAT;
+}
+
+bool MachOAnalyzer::isSplitSegV2() const {
+    uint32_t splitSegSize = 0;
+    const void* splitSegStart = getSplitSeg(splitSegSize);
+    if (!splitSegStart)
+        return false;
+
+    return (*(const uint8_t*)splitSegStart) == DYLD_CACHE_ADJ_V2_FORMAT;
+}
+
 
 uint64_t MachOAnalyzer::segAndOffsetToRuntimeOffset(uint8_t targetSegIndex, uint64_t targetSegOffset) const
 {
@@ -2850,7 +3363,7 @@ uint64_t MachOAnalyzer::preferredLoadAddress() const
 }
 
 
-bool MachOAnalyzer::getEntry(uint32_t& offset, bool& usesCRT) const
+bool MachOAnalyzer::getEntry(uint64_t& offset, bool& usesCRT) const
 {
     Diagnostics diag;
     offset = 0;
@@ -2858,42 +3371,19 @@ bool MachOAnalyzer::getEntry(uint32_t& offset, bool& usesCRT) const
         if ( cmd->cmd == LC_MAIN ) {
             entry_point_command* mainCmd = (entry_point_command*)cmd;
             usesCRT = false;
-            offset = (uint32_t)mainCmd->entryoff;
+            offset = 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());
+            offset = 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;
-        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;
-    }
-    return startAddress;
-}
-
 
 void MachOAnalyzer::forEachInterposingSection(Diagnostics& diag, void (^handler)(uint64_t vmOffset, uint64_t vmSize, bool& stop)) const
 {
@@ -2977,43 +3467,19 @@ bool MachOAnalyzer::usesLibraryValidation() const
 
 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");
-    }
+    if (!MachOFile::canHavePrecomputedDlopenClosure(path, failureReason))
+        return false;
 
-    // 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");
+    // prebuilt closures use the cdhash of the dylib to verify that the dylib is still the same
+    // at runtime as when the shared cache processed it.  We must have a code signature to record this information
+    uint32_t codeSigFileOffset;
+    uint32_t codeSigSize;
+    if ( !hasCodeSignature(codeSigFileOffset, codeSigSize) ) {
+        failureReason("no code signature");
+        return false;
     }
 
-    // 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");
-    }
+    __block bool retval = true;
 
     // images that use dynamic_lookup, bundle_loader, or have weak-defs cannot have dlopen closure pre-computed
     Diagnostics diag;
@@ -3046,23 +3512,9 @@ bool MachOAnalyzer::canHavePrecomputedDlopenClosure(const char* path, void (^fai
             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");
-    }
-    
-    // Don't precompute iOSMac for now until dyld3 support is there.
-    if ( supportsPlatform(Platform::iOSMac) && !supportsPlatform(Platform::macOS) ) {
-        retval = false;
-        failureReason("UIKitForMac binary");
-    }
-
     return retval;
 }
 
@@ -3093,8 +3545,6 @@ bool MachOAnalyzer::hasUnalignedPointerFixups() const
             }
         },
         ^(const char* symbolName) {
-        },
-        ^() {
         });
         forEachRebase(diag, true, ^(uint64_t runtimeOffset, bool& stop) {
             if ( (runtimeOffset & 7) != 0 ) {
@@ -3177,6 +3627,11 @@ void MachOAnalyzer::forEachExportedSymbol(Diagnostics& diag, ExportsCallback cal
     uint64_t trieSize;
     if ( const uint8_t* trieStart = getExportsTrie(leInfo, trieSize) ) {
         const uint8_t* trieEnd   = trieStart + trieSize;
+        // We still emit empty export trie load commands just as a placeholder to show we have
+        // no exports.  In that case, don't start recursing as we'll immediately think we ran
+        // of the end of the buffer
+        if ( trieSize == 0 )
+            return;
         bool stop = false;
         STACK_ALLOC_OVERFLOW_SAFE_ARRAY(char, cummulativeString, 4096);
         recurseTrie(diag, trieStart, trieStart, trieEnd, cummulativeString, 0, stop, callback);
@@ -3187,14 +3642,96 @@ bool MachOAnalyzer::canBePlacedInDyldCache(const char* path, void (^failureReaso
 {
     if (!MachOFile::canBePlacedInDyldCache(path, failureReason))
         return false;
+
+    // arm64e requires split seg v2 as the split seg code can't handle chained fixups for split seg v1
+    if ( isArch("arm64e") ) {
+        uint32_t splitSegSize = 0;
+        const uint8_t* infoStart = (const uint8_t*)getSplitSeg(splitSegSize);
+        if ( *infoStart != DYLD_CACHE_ADJ_V2_FORMAT ) {
+            failureReason("chained fixups requires split seg v2");
+            return false;
+        }
+    }
+
+    // <rdar://problem/57769033> dyld_cache_patchable_location only supports addend in range 0..31
+    const bool is64bit = is64();
+    __block Diagnostics diag;
+    __block bool addendTooLarge = false;
+    if ( this->hasChainedFixups() ) {
+        // with chained fixups, addends can be in the import table or embedded in a bind pointer
+        forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
+            if ( is64bit )
+                addend &= 0x00FFFFFFFFFFFFFF; // ignore TBI
+            if ( addend > 31 ) {
+                addendTooLarge = true;
+                stop = true;
+            }
+        });
+        // check each pointer for embedded addend
+        withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) {
+            forEachFixupInAllChains(diag, starts, false, ^(ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
+                switch (segInfo->pointer_format) {
+                    case DYLD_CHAINED_PTR_ARM64E:
+                    case DYLD_CHAINED_PTR_ARM64E_USERLAND:
+                    case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
+                        if ( fixupLoc->arm64e.bind.bind && !fixupLoc->arm64e.authBind.auth ) {
+                            if ( fixupLoc->arm64e.bind.addend > 31 ) {
+                                addendTooLarge = true;
+                                stop = true;
+                            }
+                        }
+                        break;
+                    case DYLD_CHAINED_PTR_64:
+                    case DYLD_CHAINED_PTR_64_OFFSET:
+                        if ( fixupLoc->generic64.rebase.bind ) {
+                            if ( fixupLoc->generic64.bind.addend > 31 ) {
+                                addendTooLarge = true;
+                                stop = true;
+                            }
+                        }
+                        break;
+                    case DYLD_CHAINED_PTR_32:
+                        if ( fixupLoc->generic32.bind.bind ) {
+                            if ( fixupLoc->generic32.bind.addend > 31 ) {
+                                addendTooLarge = true;
+                                stop = true;
+                            }
+                        }
+                        break;
+                }
+            });
+        });
+    }
+    else {
+        // scan bind opcodes for large addend
+        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, bool lazyBind, uint64_t addend, bool& stop) {
+            if ( is64bit )
+                addend &= 0x00FFFFFFFFFFFFFF; // ignore TBI
+            if ( addend > 31 ) {
+                addendTooLarge = true;
+                stop = true;
+            }
+        },
+        ^(const char* symbolName) {
+        });
+    }
+    if ( addendTooLarge ) {
+        failureReason("bind addend too large");
+        return false;
+    }
+
     if ( !(isArch("x86_64") || isArch("x86_64h")) )
         return true;
 
     if ( hasChainedFixups() )
         return true;
 
+    // evict swift dylibs with split seg v1 info
+    if ( this->isSwiftLibrary() && this->isSplitSegV1() )
+        return false;
+
     __block bool rebasesOk = true;
-    Diagnostics diag;
     uint64_t startVMAddr = preferredLoadAddress();
     uint64_t endVMAddr = startVMAddr + mappedSize();
     forEachRebase(diag, false, ^(uint64_t runtimeOffset, bool &stop) {
@@ -3220,6 +3757,223 @@ bool MachOAnalyzer::canBePlacedInDyldCache(const char* path, void (^failureReaso
     return rebasesOk;
 }
 
+#if BUILDING_APP_CACHE_UTIL
+bool MachOAnalyzer::canBePlacedInKernelCollection(const char* path, void (^failureReason)(const char*)) const
+{
+    if (!MachOFile::canBePlacedInKernelCollection(path, failureReason))
+        return false;
+
+    // App caches reguire that everything be built with split seg v2
+    // This is because v1 can't move anything other than __TEXT and __DATA
+    // but kernels have __TEXT_EXEC and other segments
+    if ( isKextBundle() ) {
+        // x86_64 kext's might not have split seg
+        if ( !isArch("x86_64") && !isArch("x86_64h") ) {
+            if ( !isSplitSegV2() ) {
+                failureReason("Missing split seg v2");
+                return false;
+            }
+        }
+    } else if ( isStaticExecutable() ) {
+        // The kernel must always have split seg V2
+        if ( !isSplitSegV2() ) {
+            failureReason("Missing split seg v2");
+            return false;
+        }
+
+        // The kernel should have __TEXT and __TEXT_EXEC
+        __block bool foundText = false;
+        __block bool foundTextExec = false;
+        __block bool foundHIB = false;
+        __block uint64_t hibernateVMAddr = 0;
+        __block uint64_t hibernateVMSize = 0;
+        forEachSegment(^(const SegmentInfo &segmentInfo, bool &stop) {
+            if ( strcmp(segmentInfo.segName, "__TEXT") == 0 ) {
+                foundText = true;
+            }
+            if ( strcmp(segmentInfo.segName, "__TEXT_EXEC") == 0 ) {
+                foundTextExec = true;
+            }
+            if ( strcmp(segmentInfo.segName, "__HIB") == 0 ) {
+                foundHIB = true;
+                hibernateVMAddr = segmentInfo.vmAddr;
+                hibernateVMSize = segmentInfo.vmSize;
+            }
+        });
+        if (!foundText) {
+            failureReason("Expected __TEXT segment");
+            return false;
+        }
+        if ( foundTextExec && foundHIB ) {
+            failureReason("Expected __TEXT_EXEC or __HIB segment, but found both");
+            return false;
+        }
+        if ( !foundTextExec && !foundHIB ) {
+            failureReason("Expected __TEXT_EXEC or __HIB segment, but found neither");
+            return false;
+        }
+
+        // The hibernate segment should be mapped before the base address
+        if ( foundHIB ) {
+            uint64_t baseAddress = preferredLoadAddress();
+            if ( greaterThanAddOrOverflow(hibernateVMAddr, hibernateVMSize, baseAddress) ) {
+                failureReason("__HIB segment should be mapped before base address");
+                return false;
+            }
+        }
+    }
+
+    // Don't allow kext's to have load addresses
+    if ( isKextBundle() && (preferredLoadAddress() != 0) ) {
+        failureReason("Has load address");
+        return false;
+    }
+
+    if (hasChainedFixups()) {
+        if ( usesClassicRelocationsInKernelCollection() ) {
+            failureReason("Cannot use fixup chains with binary expecting classic relocations");
+            return false;
+        }
+
+        __block bool fixupsOk = true;
+        __block Diagnostics diag;
+        withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) {
+            forEachFixupInAllChains(diag, starts, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc,
+                                                           const dyld_chained_starts_in_segment* segInfo, bool& stop) {
+                // We only support inputs from a few pointer format types, so that we don't need to handle them all later
+                switch (segInfo->pointer_format) {
+                    case DYLD_CHAINED_PTR_ARM64E:
+                    case DYLD_CHAINED_PTR_64:
+                    case DYLD_CHAINED_PTR_32:
+                    case DYLD_CHAINED_PTR_32_CACHE:
+                    case DYLD_CHAINED_PTR_32_FIRMWARE:
+                        failureReason("unsupported chained fixups pointer format");
+                        fixupsOk = false;
+                        stop = true;
+                        return;
+                    case DYLD_CHAINED_PTR_64_OFFSET:
+                        // arm64 kernel and kexts use this format
+                        break;
+                    case DYLD_CHAINED_PTR_ARM64E_KERNEL:
+                        // arm64e kexts use this format
+                        break;
+                    case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
+                    case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
+                        failureReason("unsupported chained fixups pointer format");
+                        fixupsOk = false;
+                        stop = true;
+                        return;
+                    default:
+                        failureReason("unknown chained fixups pointer format");
+                        fixupsOk = false;
+                        stop = true;
+                        return;
+                }
+
+                uint64_t vmOffset = (uint8_t*)fixupLoc - (uint8_t*)this;
+                // Error if the fixup location is anything other than 4/8 byte aligned
+                if ( (vmOffset & 0x3) != 0 ) {
+                    failureReason("fixup value is not 4-byte aligned");
+                    fixupsOk = false;
+                    stop = true;
+                    return;
+                }
+
+                // We also must only need 30-bits for the chain format of the resulting cache
+                if ( vmOffset >= (1 << 30) ) {
+                    failureReason("fixup value does not fit in 30-bits");
+                    fixupsOk = false;
+                    stop = true;
+                    return;
+                }
+            });
+        });
+        if (!fixupsOk)
+            return false;
+    } else {
+        // x86_64 xnu will have unaligned text/data fixups and fixups inside __HIB __text.
+        // We allow these as xnu is emitted with classic relocations
+        bool canHaveUnalignedFixups = usesClassicRelocationsInKernelCollection();
+        canHaveUnalignedFixups |= ( isArch("x86_64") || isArch("x86_64h") );
+        __block bool rebasesOk = true;
+        Diagnostics diag;
+        forEachRebase(diag, false, ^(uint64_t runtimeOffset, bool &stop) {
+            // Error if the rebase location is anything other than 4/8 byte aligned
+            if ( !canHaveUnalignedFixups && ((runtimeOffset & 0x3) != 0) ) {
+                failureReason("rebase value is not 4-byte aligned");
+                rebasesOk = false;
+                stop = true;
+                return;
+            }
+
+#if BUILDING_APP_CACHE_UTIL
+            // xnu for x86_64 has __HIB mapped before __DATA, so offsets appear to be
+            // negative.  Adjust the fixups so that we don't think they are out of
+            // range of the number of bits we have
+            if ( isStaticExecutable() ) {
+                __block uint64_t baseAddress = ~0ULL;
+                forEachSegment(^(const SegmentInfo& info, bool& stop) {
+                    baseAddress = std::min(baseAddress, info.vmAddr);
+                });
+                uint64_t textSegVMAddr = preferredLoadAddress();
+                runtimeOffset = (textSegVMAddr + runtimeOffset) - baseAddress;
+            }
+#endif
+
+            // We also must only need 30-bits for the chain format of the resulting cache
+            if ( runtimeOffset >= (1 << 30) ) {
+                failureReason("rebase value does not fit in 30-bits");
+                rebasesOk = false;
+                stop = true;
+                return;
+            }
+        });
+        if (!rebasesOk)
+            return false;
+
+        __block bool bindsOk = true;
+        forEachBind(diag, ^(uint64_t runtimeOffset, int libOrdinal, uint8_t type, const char *symbolName,
+                            bool weakImport, bool lazyBind, uint64_t addend, bool &stop) {
+
+            // Don't validate branch fixups as we'll turn then in to direct jumps instead
+            if ( type == BIND_TYPE_TEXT_PCREL32 )
+                return;
+
+            // Error if the bind location is anything other than 4/8 byte aligned
+            if ( !canHaveUnalignedFixups && ((runtimeOffset & 0x3) != 0) ) {
+                failureReason("bind value is not 4-byte aligned");
+                bindsOk = false;
+                stop = true;
+                return;
+            }
+
+            // We also must only need 30-bits for the chain format of the resulting cache
+            if ( runtimeOffset >= (1 << 30) ) {
+                failureReason("bind value does not fit in 30-bits");
+                rebasesOk = false;
+                stop = true;
+                return;
+            }
+        }, ^(const char *symbolName) {
+        });
+        if (!bindsOk)
+            return false;
+    }
+
+    return true;
+}
+
+#endif
+
+bool MachOAnalyzer::usesClassicRelocationsInKernelCollection() const {
+    // The xnu x86_64 static executable needs to do the i386->x86_64 transition
+    // so will be emitted with classic relocations
+    if ( isArch("x86_64") || isArch("x86_64h") ) {
+        return isStaticExecutable() || isFileSet();
+    }
+    return false;
+}
+
 uint64_t MachOAnalyzer::chainStartsOffset() const
 {
     const dyld_chained_fixups_header* header = chainedFixupsHeader();
@@ -3262,11 +4016,10 @@ uint16_t MachOAnalyzer::chainedPointerFormat() const
         // get pointer format from chain info struct in LINKEDIT
         return chainedPointerFormat(header);
     }
-    assert(this->cputype == CPU_TYPE_ARM64 && this->cpusubtype == CPU_SUBTYPE_ARM64E && "chainedPointerFormat() called on non-chained binary");
+    assert(this->cputype == CPU_TYPE_ARM64 && (this->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E) && "chainedPointerFormat() called on non-chained binary");
     return DYLD_CHAINED_PTR_ARM64E;
 }
 
-
 #if (BUILDING_DYLD || BUILDING_LIBDYLD) && !__arm64e__
   #define SUPPORT_OLD_ARM64E_FORMAT 0
 #else
@@ -3295,7 +4048,7 @@ void MachOAnalyzer::withChainStarts(Diagnostics& diag, uint64_t startsStructOffs
     }
 #if SUPPORT_OLD_ARM64E_FORMAT
     // don't want this code in non-arm64e dyld because it causes a stack protector which dereferences a GOT pointer before GOT is set up
-    else if ( (leInfo.dyldInfo != nullptr) && (this->cputype == CPU_TYPE_ARM64) && (this->cpusubtype == CPU_SUBTYPE_ARM64E) ) {
+    else if ( (leInfo.dyldInfo != nullptr) && (this->cputype == CPU_TYPE_ARM64) && (this->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E) ) {
         // old arm64e binary, create a dyld_chained_starts_in_image for caller
         uint64_t baseAddress = preferredLoadAddress();
         BLOCK_ACCCESSIBLE_ARRAY(uint8_t, buffer, leInfo.dyldInfo->bind_size + 512);
@@ -3342,6 +4095,35 @@ void MachOAnalyzer::withChainStarts(Diagnostics& diag, uint64_t startsStructOffs
     }
 }
 
+struct OldThreadsStartSection
+{
+    uint32_t        padding : 31,
+                    stride8 : 1;
+    uint32_t        chain_starts[1];
+};
+
+bool MachOAnalyzer::hasFirmwareChainStarts(uint16_t* pointerFormat, uint32_t* startsCount, const uint32_t** starts) const
+{
+    if ( !this->isPreload() && !this->isStaticExecutable() )
+        return false;
+
+    uint64_t sectionSize;
+    if (const dyld_chained_starts_offsets* sect = (dyld_chained_starts_offsets*)this->findSectionContent("__TEXT", "__chain_starts", sectionSize) ) {
+        *pointerFormat = sect->pointer_format;
+        *startsCount   = sect->starts_count;
+        *starts        = &sect->chain_starts[0];
+        return true;
+    }
+    if (const OldThreadsStartSection* sect = (OldThreadsStartSection*)this->findSectionContent("__TEXT", "__thread_starts", sectionSize) ) {
+        *pointerFormat = sect->stride8 ? DYLD_CHAINED_PTR_ARM64E : DYLD_CHAINED_PTR_ARM64E_FIRMWARE;
+        *startsCount   = (uint32_t)(sectionSize/4) - 1;
+        *starts        = sect->chain_starts;
+        return true;
+    }
+    return false;
+}
+
+
 MachOAnalyzer::ObjCInfo MachOAnalyzer::getObjCInfo() const
 {
     __block ObjCInfo result;
@@ -3372,27 +4154,6 @@ MachOAnalyzer::ObjCInfo MachOAnalyzer::getObjCInfo() const
     return result;
 }
 
-// Convert from a (possibly) live pointer to a vmAddr
-static uint64_t convertToVMAddr(uint64_t value, MachOAnalyzer::VMAddrConverter vmAddrConverter) {
-    if ( vmAddrConverter.contentRebased ) {
-        // The value may have been signed.  Strip the signature if that is the case
-#if __has_feature(ptrauth_calls)
-        value = (uint64_t)__builtin_ptrauth_strip((void*)value, ptrauth_key_asia);
-#endif
-        value -= vmAddrConverter.slide;
-    }
-    else if ( vmAddrConverter.chainedPointerFormat != 0 ) {
-        auto* chainedValue = (MachOAnalyzer::ChainedFixupPointerOnDisk*)&value;
-        uint64_t targetRuntimeOffset;
-        if ( chainedValue->isRebase(vmAddrConverter.chainedPointerFormat, vmAddrConverter.preferredLoadAddress,
-                                    targetRuntimeOffset) ) {
-            value = vmAddrConverter.preferredLoadAddress + targetRuntimeOffset;
-        }
-    }
-
-    return value;
-}
-
 uint64_t MachOAnalyzer::ObjCClassInfo::getReadOnlyDataField(ObjCClassInfo::ReadOnlyDataField field, uint32_t pointerSize) const {
     if (pointerSize == 8) {
         typedef uint64_t PtrTy;
@@ -3417,11 +4178,13 @@ uint64_t MachOAnalyzer::ObjCClassInfo::getReadOnlyDataField(ObjCClassInfo::ReadO
         const class_ro_t* classData = (const class_ro_t*)(dataVMAddr + vmAddrConverter.slide);
         switch (field) {
         case ObjCClassInfo::ReadOnlyDataField::name:
-            return convertToVMAddr(classData->nameVMAddr, vmAddrConverter);
+            return vmAddrConverter.convertToVMAddr(classData->nameVMAddr);
+        case ObjCClassInfo::ReadOnlyDataField::baseProtocols:
+            return vmAddrConverter.convertToVMAddr(classData->baseProtocolsVMAddr);
         case ObjCClassInfo::ReadOnlyDataField::baseMethods:
-            return convertToVMAddr(classData->baseMethodsVMAddr, vmAddrConverter);
+            return vmAddrConverter.convertToVMAddr(classData->baseMethodsVMAddr);
         case ObjCClassInfo::ReadOnlyDataField::baseProperties:
-            return convertToVMAddr(classData->basePropertiesVMAddr, vmAddrConverter);
+            return vmAddrConverter.convertToVMAddr(classData->basePropertiesVMAddr);
         case ObjCClassInfo::ReadOnlyDataField::flags:
             return classData->flags;
         }
@@ -3448,11 +4211,13 @@ uint64_t MachOAnalyzer::ObjCClassInfo::getReadOnlyDataField(ObjCClassInfo::ReadO
         const class_ro_t* classData = (const class_ro_t*)(dataVMAddr + vmAddrConverter.slide);
         switch (field) {
             case ObjCClassInfo::ReadOnlyDataField::name:
-                return convertToVMAddr(classData->nameVMAddr, vmAddrConverter);
+                return vmAddrConverter.convertToVMAddr(classData->nameVMAddr);
+            case ObjCClassInfo::ReadOnlyDataField::baseProtocols:
+                return vmAddrConverter.convertToVMAddr(classData->baseProtocolsVMAddr);
             case ObjCClassInfo::ReadOnlyDataField::baseMethods:
-                return convertToVMAddr(classData->baseMethodsVMAddr, vmAddrConverter);
+                return vmAddrConverter.convertToVMAddr(classData->baseMethodsVMAddr);
             case ObjCClassInfo::ReadOnlyDataField::baseProperties:
-                return convertToVMAddr(classData->basePropertiesVMAddr, vmAddrConverter);
+                return vmAddrConverter.convertToVMAddr(classData->basePropertiesVMAddr);
             case ObjCClassInfo::ReadOnlyDataField::flags:
                 return classData->flags;
         }
@@ -3545,7 +4310,7 @@ const char* MachOAnalyzer::getPrintableString(uint64_t stringVMAddr, MachOAnalyz
         stop = true;
     });
 
-#if BUILDING_SHARED_CACHE_UTIL
+#if BUILDING_SHARED_CACHE_UTIL || BUILDING_DYLDINFO
     // The shared cache coalesces strings in to their own section.
     // Assume its a valid pointer
     if (result == PrintableStringResult::UnknownSection) {
@@ -3578,7 +4343,7 @@ bool MachOAnalyzer::SectionCache::findSectionForVMAddr(uint64_t vmAddr, bool (^s
         // The section handler may also reject this section
         if ( sectionHandler != nullptr ) {
             if (!sectionHandler(*foundSectionInfo)) {
-                return nullptr;
+                return false;
             }
         }
 
@@ -3632,20 +4397,14 @@ bool MachOAnalyzer::SectionCache::findSectionForVMAddr(uint64_t vmAddr, bool (^s
 
     return foundValidSection;
 }
-    
-void MachOAnalyzer::forEachObjCClass(Diagnostics& diag, bool contentRebased,
+
+void MachOAnalyzer::forEachObjCClass(Diagnostics& diag, const VMAddrConverter& vmAddrConverter,
                                      void (^handler)(Diagnostics& diag, uint64_t classVMAddr,
                                                      uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
                                                      const ObjCClassInfo& objcClass, bool isMetaClass)) const {
     const uint64_t ptrSize = pointerSize();
     intptr_t slide = getSlide();
 
-    MachOAnalyzer::VMAddrConverter vmAddrConverter;
-    vmAddrConverter.preferredLoadAddress   = preferredLoadAddress();
-    vmAddrConverter.slide                  = slide;
-    vmAddrConverter.chainedPointerFormat   = hasChainedFixups() ? chainedPointerFormat() : 0;
-    vmAddrConverter.contentRebased         = contentRebased;
-
     forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
         if ( strncmp(sectInfo.segInfo.segName, "__DATA", 6) != 0 )
             return;
@@ -3661,111 +4420,39 @@ void MachOAnalyzer::forEachObjCClass(Diagnostics& diag, bool contentRebased,
 
         if ( ptrSize == 8 ) {
             typedef uint64_t PtrTy;
-            struct objc_class_t {
-                uint64_t isaVMAddr;
-                uint64_t superclassVMAddr;
-                uint64_t methodCacheBuckets;
-                uint64_t methodCacheProperties;
-                uint64_t dataVMAddrAndFastFlags;
-            };
-            // This matches "struct TargetClassMetadata" from Metadata.h in Swift
-            struct swift_class_metadata_t : objc_class_t {
-                uint32_t swiftClassFlags;
-            };
-            enum : uint64_t {
-                FAST_DATA_MASK = 0x00007ffffffffff8ULL
-            };
+            
             for (uint64_t i = 0; i != classListSize; i += sizeof(PtrTy)) {
-                uint64_t classVMAddr = convertToVMAddr(*(PtrTy*)(classList + i), vmAddrConverter);
-                uint64_t classSuperclassVMAddr = classVMAddr + offsetof(objc_class_t, superclassVMAddr);
-                uint64_t classDataVMAddr       = classVMAddr + offsetof(objc_class_t, dataVMAddrAndFastFlags);
-
-                // First call the handler on the class
-                const objc_class_t*           classPtr      = (const objc_class_t*)(classVMAddr + slide);
-                const swift_class_metadata_t* swiftClassPtr = (const swift_class_metadata_t*)classPtr;
-                ObjCClassInfo objcClass;
-                objcClass.isaVMAddr         = convertToVMAddr(classPtr->isaVMAddr, vmAddrConverter);
-                objcClass.superclassVMAddr  = convertToVMAddr(classPtr->superclassVMAddr, vmAddrConverter);
-                objcClass.dataVMAddr        = convertToVMAddr(classPtr->dataVMAddrAndFastFlags, vmAddrConverter) & FAST_DATA_MASK;
-                objcClass.vmAddrConverter   = vmAddrConverter;
-                objcClass.isSwiftLegacy     = classPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_LEGACY;
-                objcClass.isSwiftStable     = classPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_STABLE;
-                // The Swift class flags are only present if the class is swift
-                objcClass.swiftClassFlags   = (objcClass.isSwiftLegacy || objcClass.isSwiftStable) ? swiftClassPtr->swiftClassFlags : 0;
-                handler(diag, classVMAddr, classSuperclassVMAddr, classDataVMAddr, objcClass, false);
-                if (diag.hasError())
-                    return;
-
-                // Then call it on the metaclass
-                const objc_class_t*             metaClassPtr        = (const objc_class_t*)(objcClass.isaVMAddr + slide);
-                const swift_class_metadata_t*   swiftMetaClassPtr   = (const swift_class_metadata_t*)metaClassPtr;
-                ObjCClassInfo objcMetaClass;
-                objcMetaClass.isaVMAddr         = convertToVMAddr(metaClassPtr->isaVMAddr, vmAddrConverter);
-                objcMetaClass.superclassVMAddr  = convertToVMAddr(metaClassPtr->superclassVMAddr, vmAddrConverter);
-                objcMetaClass.dataVMAddr        = convertToVMAddr(metaClassPtr->dataVMAddrAndFastFlags, vmAddrConverter) & FAST_DATA_MASK;
-                objcMetaClass.vmAddrConverter   = vmAddrConverter;
-                objcMetaClass.isSwiftLegacy     = metaClassPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_LEGACY;
-                objcMetaClass.isSwiftStable     = metaClassPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_STABLE;
-                // The Swift class flags are only present if the class is swift
-                objcMetaClass.swiftClassFlags   = (objcMetaClass.isSwiftLegacy || objcMetaClass.isSwiftStable) ? swiftMetaClassPtr->swiftClassFlags : 0;
-                classSuperclassVMAddr = objcClass.isaVMAddr + offsetof(objc_class_t, superclassVMAddr);
-                classDataVMAddr       = objcClass.isaVMAddr + offsetof(objc_class_t, dataVMAddrAndFastFlags);
-                handler(diag, objcClass.isaVMAddr, classSuperclassVMAddr, classDataVMAddr, objcMetaClass, true);
+                uint64_t classVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(classList + i));
+                parseObjCClass(diag, vmAddrConverter, classVMAddr, ^(Diagnostics& classDiag, uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, const ObjCClassInfo& objcClass) {
+                    handler(classDiag, classVMAddr, classSuperclassVMAddr, classDataVMAddr, objcClass, false);
+                    if (classDiag.hasError())
+                        return;
+                    
+                    // Then parse and call for the metaclass
+                    uint64_t isaVMAddr = objcClass.isaVMAddr;
+                    parseObjCClass(classDiag, vmAddrConverter, isaVMAddr, ^(Diagnostics& metaclassDiag, uint64_t metaclassSuperclassVMAddr, uint64_t metaclassDataVMAddr, const ObjCClassInfo& objcMetaclass) {
+                        handler(metaclassDiag, isaVMAddr, metaclassSuperclassVMAddr, metaclassDataVMAddr, objcMetaclass, true);
+                    });
+                });
                 if (diag.hasError())
                     return;
             }
         } else {
             typedef uint32_t PtrTy;
-            struct objc_class_t {
-                uint32_t isaVMAddr;
-                uint32_t superclassVMAddr;
-                uint32_t methodCacheBuckets;
-                uint32_t methodCacheProperties;
-                uint32_t dataVMAddrAndFastFlags;
-            };
-            // This matches "struct TargetClassMetadata" from Metadata.h in Swift
-            struct swift_class_metadata_t : objc_class_t {
-                uint32_t swiftClassFlags;
-            };
-            enum : uint32_t {
-                FAST_DATA_MASK = 0xfffffffcUL
-            };
+
             for (uint64_t i = 0; i != classListSize; i += sizeof(PtrTy)) {
-                uint64_t classVMAddr = convertToVMAddr(*(PtrTy*)(classList + i), vmAddrConverter);
-                uint64_t classSuperclassVMAddr = classVMAddr + offsetof(objc_class_t, superclassVMAddr);
-                uint64_t classDataVMAddr       = classVMAddr + offsetof(objc_class_t, dataVMAddrAndFastFlags);
-
-                // First call the handler on the class
-                const objc_class_t*           classPtr      = (const objc_class_t*)(classVMAddr + slide);
-                const swift_class_metadata_t* swiftClassPtr = (const swift_class_metadata_t*)classPtr;
-                ObjCClassInfo objcClass;
-                objcClass.isaVMAddr         = convertToVMAddr(classPtr->isaVMAddr, vmAddrConverter);
-                objcClass.superclassVMAddr  = convertToVMAddr(classPtr->superclassVMAddr, vmAddrConverter);
-                objcClass.dataVMAddr        = convertToVMAddr(classPtr->dataVMAddrAndFastFlags, vmAddrConverter) & FAST_DATA_MASK;
-                objcClass.vmAddrConverter   = vmAddrConverter;
-                objcClass.isSwiftLegacy     = classPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_LEGACY;
-                objcClass.isSwiftStable     = classPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_STABLE;
-                // The Swift class flags are only present if the class is swift
-                objcClass.swiftClassFlags   = (objcClass.isSwiftLegacy || objcClass.isSwiftStable) ? swiftClassPtr->swiftClassFlags : 0;
-                handler(diag, classVMAddr, classSuperclassVMAddr, classDataVMAddr, objcClass, false);
-                if (diag.hasError())
-                    return;
+                uint64_t classVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(classList + i));
+                parseObjCClass(diag, vmAddrConverter, classVMAddr, ^(Diagnostics& classDiag, uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, const ObjCClassInfo& objcClass) {
+                    handler(classDiag, classVMAddr, classSuperclassVMAddr, classDataVMAddr, objcClass, false);
+                    if (classDiag.hasError())
+                        return;
 
-                // Then call it on the metaclass
-                const objc_class_t*             metaClassPtr        = (const objc_class_t*)(objcClass.isaVMAddr + slide);
-                const swift_class_metadata_t*   swiftMetaClassPtr   = (const swift_class_metadata_t*)metaClassPtr;
-                ObjCClassInfo objcMetaClass;
-                objcMetaClass.isaVMAddr         = convertToVMAddr(metaClassPtr->isaVMAddr, vmAddrConverter);
-                objcMetaClass.superclassVMAddr  = convertToVMAddr(metaClassPtr->superclassVMAddr, vmAddrConverter);
-                objcMetaClass.dataVMAddr        = convertToVMAddr(metaClassPtr->dataVMAddrAndFastFlags, vmAddrConverter) & FAST_DATA_MASK;
-                objcMetaClass.vmAddrConverter   = vmAddrConverter;
-                objcMetaClass.isSwiftLegacy     = metaClassPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_LEGACY;
-                objcMetaClass.isSwiftStable     = metaClassPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_STABLE;
-                // The Swift class flags are only present if the class is swift
-                objcMetaClass.swiftClassFlags   = (objcMetaClass.isSwiftLegacy || objcMetaClass.isSwiftStable) ? swiftMetaClassPtr->swiftClassFlags : 0;
-                classSuperclassVMAddr = objcClass.isaVMAddr + offsetof(objc_class_t, superclassVMAddr);
-                classDataVMAddr       = objcClass.isaVMAddr + offsetof(objc_class_t, dataVMAddrAndFastFlags);
-                handler(diag, objcClass.isaVMAddr, classSuperclassVMAddr, classDataVMAddr, objcMetaClass, true);
+                    // Then parse and call for the metaclass
+                    uint64_t isaVMAddr = objcClass.isaVMAddr;
+                    parseObjCClass(classDiag, vmAddrConverter, isaVMAddr, ^(Diagnostics& metaclassDiag, uint64_t metaclassSuperclassVMAddr, uint64_t metaclassDataVMAddr, const ObjCClassInfo& objcMetaclass) {
+                        handler(metaclassDiag, isaVMAddr, metaclassSuperclassVMAddr, metaclassDataVMAddr, objcMetaclass, true);
+                    });
+                });
                 if (diag.hasError())
                     return;
             }
@@ -3773,18 +4460,90 @@ void MachOAnalyzer::forEachObjCClass(Diagnostics& diag, bool contentRebased,
     });
 }
 
-void MachOAnalyzer::forEachObjCCategory(Diagnostics& diag, bool contentRebased,
+void MachOAnalyzer::parseObjCClass(Diagnostics& diag, const VMAddrConverter& vmAddrConverter,
+                                   uint64_t classVMAddr,
+                                   void (^handler)(Diagnostics& diag,
+                                                   uint64_t classSuperclassVMAddr,
+                                                   uint64_t classDataVMAddr,
+                                                   const ObjCClassInfo& objcClass)) const {
+    const uint64_t ptrSize = pointerSize();
+    intptr_t slide = getSlide();
+
+    uint64_t classSuperclassVMAddr = 0;
+    uint64_t classDataVMAddr       = 0;
+    ObjCClassInfo objcClass;
+
+    if ( ptrSize == 8 ) {
+       struct objc_class_t {
+           uint64_t isaVMAddr;
+           uint64_t superclassVMAddr;
+           uint64_t methodCacheBuckets;
+           uint64_t methodCacheProperties;
+           uint64_t dataVMAddrAndFastFlags;
+       };
+        // This matches "struct TargetClassMetadata" from Metadata.h in Swift
+        struct swift_class_metadata_t : objc_class_t {
+            uint32_t swiftClassFlags;
+        };
+        enum : uint64_t {
+            FAST_DATA_MASK = 0x00007ffffffffff8ULL
+        };
+        classSuperclassVMAddr = classVMAddr + offsetof(objc_class_t, superclassVMAddr);
+        classDataVMAddr       = classVMAddr + offsetof(objc_class_t, dataVMAddrAndFastFlags);
+
+        // First call the handler on the class
+        const objc_class_t*           classPtr      = (const objc_class_t*)(classVMAddr + slide);
+        const swift_class_metadata_t* swiftClassPtr = (const swift_class_metadata_t*)classPtr;
+        objcClass.isaVMAddr         = vmAddrConverter.convertToVMAddr(classPtr->isaVMAddr);
+        objcClass.superclassVMAddr  = vmAddrConverter.convertToVMAddr(classPtr->superclassVMAddr);
+        objcClass.methodCacheVMAddr  = classPtr->methodCacheProperties == 0 ? 0 : vmAddrConverter.convertToVMAddr(classPtr->methodCacheProperties);
+        objcClass.dataVMAddr        = vmAddrConverter.convertToVMAddr(classPtr->dataVMAddrAndFastFlags) & FAST_DATA_MASK;
+        objcClass.vmAddrConverter   = vmAddrConverter;
+        objcClass.isSwiftLegacy     = classPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_LEGACY;
+        objcClass.isSwiftStable     = classPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_STABLE;
+        // The Swift class flags are only present if the class is swift
+        objcClass.swiftClassFlags   = (objcClass.isSwiftLegacy || objcClass.isSwiftStable) ? swiftClassPtr->swiftClassFlags : 0;
+    } else {
+        struct objc_class_t {
+            uint32_t isaVMAddr;
+            uint32_t superclassVMAddr;
+            uint32_t methodCacheBuckets;
+            uint32_t methodCacheProperties;
+            uint32_t dataVMAddrAndFastFlags;
+        };
+        // This matches "struct TargetClassMetadata" from Metadata.h in Swift
+        struct swift_class_metadata_t : objc_class_t {
+            uint32_t swiftClassFlags;
+        };
+        enum : uint32_t {
+            FAST_DATA_MASK = 0xfffffffcUL
+        };
+        classSuperclassVMAddr = classVMAddr + offsetof(objc_class_t, superclassVMAddr);
+        classDataVMAddr       = classVMAddr + offsetof(objc_class_t, dataVMAddrAndFastFlags);
+
+        // First call the handler on the class
+        const objc_class_t*           classPtr      = (const objc_class_t*)(classVMAddr + slide);
+        const swift_class_metadata_t* swiftClassPtr = (const swift_class_metadata_t*)classPtr;
+        objcClass.isaVMAddr         = vmAddrConverter.convertToVMAddr(classPtr->isaVMAddr);
+        objcClass.superclassVMAddr  = vmAddrConverter.convertToVMAddr(classPtr->superclassVMAddr);
+        objcClass.methodCacheVMAddr  = classPtr->methodCacheProperties == 0 ? 0 : vmAddrConverter.convertToVMAddr(classPtr->methodCacheProperties);
+        objcClass.dataVMAddr        = vmAddrConverter.convertToVMAddr(classPtr->dataVMAddrAndFastFlags) & FAST_DATA_MASK;
+        objcClass.vmAddrConverter   = vmAddrConverter;
+        objcClass.isSwiftLegacy     = classPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_LEGACY;
+        objcClass.isSwiftStable     = classPtr->dataVMAddrAndFastFlags & ObjCClassInfo::FAST_IS_SWIFT_STABLE;
+        // The Swift class flags are only present if the class is swift
+        objcClass.swiftClassFlags   = (objcClass.isSwiftLegacy || objcClass.isSwiftStable) ? swiftClassPtr->swiftClassFlags : 0;
+    }
+                                       
+    handler(diag, classSuperclassVMAddr, classDataVMAddr, objcClass);
+}
+
+void MachOAnalyzer::forEachObjCCategory(Diagnostics& diag, const VMAddrConverter& vmAddrConverter,
                                         void (^handler)(Diagnostics& diag, uint64_t categoryVMAddr,
                                                         const dyld3::MachOAnalyzer::ObjCCategory& objcCategory)) const {
     const uint64_t ptrSize = pointerSize();
     intptr_t slide = getSlide();
 
-    MachOAnalyzer::VMAddrConverter vmAddrConverter;
-    vmAddrConverter.preferredLoadAddress   = preferredLoadAddress();
-    vmAddrConverter.slide                  = slide;
-    vmAddrConverter.chainedPointerFormat   = hasChainedFixups() ? chainedPointerFormat() : 0;
-    vmAddrConverter.contentRebased         = contentRebased;
-
     forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
         if ( strncmp(sectInfo.segInfo.segName, "__DATA", 6) != 0 )
             return;
@@ -3809,16 +4568,16 @@ void MachOAnalyzer::forEachObjCCategory(Diagnostics& diag, bool contentRebased,
                 PtrTy instancePropertiesVMAddr;
             };
             for (uint64_t i = 0; i != categoryListSize; i += sizeof(PtrTy)) {
-                uint64_t categoryVMAddr = convertToVMAddr(*(PtrTy*)(categoryList + i), vmAddrConverter);
+                uint64_t categoryVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(categoryList + i));
 
                 const objc_category_t* categoryPtr = (const objc_category_t*)(categoryVMAddr + slide);
                 ObjCCategory objCCategory;
-                objCCategory.nameVMAddr                 = convertToVMAddr(categoryPtr->nameVMAddr, vmAddrConverter);
-                objCCategory.clsVMAddr                  = convertToVMAddr(categoryPtr->clsVMAddr, vmAddrConverter);
-                objCCategory.instanceMethodsVMAddr      = convertToVMAddr(categoryPtr->instanceMethodsVMAddr, vmAddrConverter);
-                objCCategory.classMethodsVMAddr         = convertToVMAddr(categoryPtr->classMethodsVMAddr, vmAddrConverter);
-                objCCategory.protocolsVMAddr            = convertToVMAddr(categoryPtr->protocolsVMAddr, vmAddrConverter);
-                objCCategory.instancePropertiesVMAddr   = convertToVMAddr(categoryPtr->instancePropertiesVMAddr, vmAddrConverter);
+                objCCategory.nameVMAddr                 = vmAddrConverter.convertToVMAddr(categoryPtr->nameVMAddr);
+                objCCategory.clsVMAddr                  = vmAddrConverter.convertToVMAddr(categoryPtr->clsVMAddr);
+                objCCategory.instanceMethodsVMAddr      = vmAddrConverter.convertToVMAddr(categoryPtr->instanceMethodsVMAddr);
+                objCCategory.classMethodsVMAddr         = vmAddrConverter.convertToVMAddr(categoryPtr->classMethodsVMAddr);
+                objCCategory.protocolsVMAddr            = vmAddrConverter.convertToVMAddr(categoryPtr->protocolsVMAddr);
+                objCCategory.instancePropertiesVMAddr   = vmAddrConverter.convertToVMAddr(categoryPtr->instancePropertiesVMAddr);
                 handler(diag, categoryVMAddr, objCCategory);
                 if (diag.hasError())
                     return;
@@ -3834,16 +4593,16 @@ void MachOAnalyzer::forEachObjCCategory(Diagnostics& diag, bool contentRebased,
                 PtrTy instancePropertiesVMAddr;
             };
             for (uint64_t i = 0; i != categoryListSize; i += sizeof(PtrTy)) {
-                uint64_t categoryVMAddr = convertToVMAddr(*(PtrTy*)(categoryList + i), vmAddrConverter);
+                uint64_t categoryVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(categoryList + i));
 
                 const objc_category_t* categoryPtr = (const objc_category_t*)(categoryVMAddr + slide);
                 ObjCCategory objCCategory;
-                objCCategory.nameVMAddr                 = convertToVMAddr(categoryPtr->nameVMAddr, vmAddrConverter);
-                objCCategory.clsVMAddr                  = convertToVMAddr(categoryPtr->clsVMAddr, vmAddrConverter);
-                objCCategory.instanceMethodsVMAddr      = convertToVMAddr(categoryPtr->instanceMethodsVMAddr, vmAddrConverter);
-                objCCategory.classMethodsVMAddr         = convertToVMAddr(categoryPtr->classMethodsVMAddr, vmAddrConverter);
-                objCCategory.protocolsVMAddr            = convertToVMAddr(categoryPtr->protocolsVMAddr, vmAddrConverter);
-                objCCategory.instancePropertiesVMAddr   = convertToVMAddr(categoryPtr->instancePropertiesVMAddr, vmAddrConverter);
+                objCCategory.nameVMAddr                 = vmAddrConverter.convertToVMAddr(categoryPtr->nameVMAddr);
+                objCCategory.clsVMAddr                  = vmAddrConverter.convertToVMAddr(categoryPtr->clsVMAddr);
+                objCCategory.instanceMethodsVMAddr      = vmAddrConverter.convertToVMAddr(categoryPtr->instanceMethodsVMAddr);
+                objCCategory.classMethodsVMAddr         = vmAddrConverter.convertToVMAddr(categoryPtr->classMethodsVMAddr);
+                objCCategory.protocolsVMAddr            = vmAddrConverter.convertToVMAddr(categoryPtr->protocolsVMAddr);
+                objCCategory.instancePropertiesVMAddr   = vmAddrConverter.convertToVMAddr(categoryPtr->instancePropertiesVMAddr);
                 handler(diag, categoryVMAddr, objCCategory);
                 if (diag.hasError())
                     return;
@@ -3852,18 +4611,12 @@ void MachOAnalyzer::forEachObjCCategory(Diagnostics& diag, bool contentRebased,
     });
 }
 
-void MachOAnalyzer::forEachObjCProtocol(Diagnostics& diag, bool contentRebased,
+void MachOAnalyzer::forEachObjCProtocol(Diagnostics& diag, const VMAddrConverter& vmAddrConverter,
                                         void (^handler)(Diagnostics& diag, uint64_t categoryVMAddr,
                                                         const dyld3::MachOAnalyzer::ObjCProtocol& objCProtocol)) const {
     const uint64_t ptrSize = pointerSize();
     intptr_t slide = getSlide();
 
-    MachOAnalyzer::VMAddrConverter vmAddrConverter;
-    vmAddrConverter.preferredLoadAddress   = preferredLoadAddress();
-    vmAddrConverter.slide                  = slide;
-    vmAddrConverter.chainedPointerFormat   = hasChainedFixups() ? chainedPointerFormat() : 0;
-    vmAddrConverter.contentRebased         = contentRebased;
-
     forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
         if ( strncmp(sectInfo.segInfo.segName, "__DATA", 6) != 0 )
             return;
@@ -3896,19 +4649,17 @@ void MachOAnalyzer::forEachObjCProtocol(Diagnostics& diag, bool contentRebased,
                 PtrTy    classPropertiesVMAddr;
             };
             for (uint64_t i = 0; i != protocolListSize; i += sizeof(PtrTy)) {
-                uint64_t protocolVMAddr = convertToVMAddr(*(PtrTy*)(protocolList + i), vmAddrConverter);
+                uint64_t protocolVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(protocolList + i));
 
                 const protocol_t* protocolPtr = (const protocol_t*)(protocolVMAddr + slide);
                 ObjCProtocol objCProtocol;
-                objCProtocol.isaVMAddr                          = convertToVMAddr(protocolPtr->isaVMAddr, vmAddrConverter);
-                objCProtocol.nameVMAddr                         = convertToVMAddr(protocolPtr->nameVMAddr, vmAddrConverter);
-                objCProtocol.instanceMethodsVMAddr              = convertToVMAddr(protocolPtr->instanceMethodsVMAddr, vmAddrConverter);
-                objCProtocol.classMethodsVMAddr                 = convertToVMAddr(protocolPtr->classMethodsVMAddr, vmAddrConverter);
-                objCProtocol.optionalInstanceMethodsVMAddr      = convertToVMAddr(protocolPtr->optionalInstanceMethodsVMAddr, vmAddrConverter);
-                objCProtocol.optionalClassMethodsVMAddr         = convertToVMAddr(protocolPtr->optionalClassMethodsVMAddr, vmAddrConverter);
-
-                // Track if this protocol needs a reallocation in objc
-                objCProtocol.requiresObjCReallocation           = protocolPtr->size < sizeof(protocol_t);
+                objCProtocol.isaVMAddr                          = vmAddrConverter.convertToVMAddr(protocolPtr->isaVMAddr);
+                objCProtocol.nameVMAddr                         = vmAddrConverter.convertToVMAddr(protocolPtr->nameVMAddr);
+                objCProtocol.protocolsVMAddr                    = vmAddrConverter.convertToVMAddr(protocolPtr->protocolsVMAddr);
+                objCProtocol.instanceMethodsVMAddr              = vmAddrConverter.convertToVMAddr(protocolPtr->instanceMethodsVMAddr);
+                objCProtocol.classMethodsVMAddr                 = vmAddrConverter.convertToVMAddr(protocolPtr->classMethodsVMAddr);
+                objCProtocol.optionalInstanceMethodsVMAddr      = vmAddrConverter.convertToVMAddr(protocolPtr->optionalInstanceMethodsVMAddr);
+                objCProtocol.optionalClassMethodsVMAddr         = vmAddrConverter.convertToVMAddr(protocolPtr->optionalClassMethodsVMAddr);
 
                 handler(diag, protocolVMAddr, objCProtocol);
                 if (diag.hasError())
@@ -3933,19 +4684,17 @@ void MachOAnalyzer::forEachObjCProtocol(Diagnostics& diag, bool contentRebased,
                 PtrTy    classPropertiesVMAddr;
             };
             for (uint64_t i = 0; i != protocolListSize; i += sizeof(PtrTy)) {
-                uint64_t protocolVMAddr = convertToVMAddr(*(PtrTy*)(protocolList + i), vmAddrConverter);
+                uint64_t protocolVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(protocolList + i));
 
                 const protocol_t* protocolPtr = (const protocol_t*)(protocolVMAddr + slide);
                 ObjCProtocol objCProtocol;
-                objCProtocol.isaVMAddr                          = convertToVMAddr(protocolPtr->isaVMAddr, vmAddrConverter);
-                objCProtocol.nameVMAddr                         = convertToVMAddr(protocolPtr->nameVMAddr, vmAddrConverter);
-                objCProtocol.instanceMethodsVMAddr              = convertToVMAddr(protocolPtr->instanceMethodsVMAddr, vmAddrConverter);
-                objCProtocol.classMethodsVMAddr                 = convertToVMAddr(protocolPtr->classMethodsVMAddr, vmAddrConverter);
-                objCProtocol.optionalInstanceMethodsVMAddr      = convertToVMAddr(protocolPtr->optionalInstanceMethodsVMAddr, vmAddrConverter);
-                objCProtocol.optionalClassMethodsVMAddr         = convertToVMAddr(protocolPtr->optionalClassMethodsVMAddr, vmAddrConverter);
-
-                // Track if this protocol needs a reallocation in objc
-                objCProtocol.requiresObjCReallocation           = protocolPtr->size < sizeof(protocol_t);
+                objCProtocol.isaVMAddr                          = vmAddrConverter.convertToVMAddr(protocolPtr->isaVMAddr);
+                objCProtocol.nameVMAddr                         = vmAddrConverter.convertToVMAddr(protocolPtr->nameVMAddr);
+                objCProtocol.protocolsVMAddr                    = vmAddrConverter.convertToVMAddr(protocolPtr->protocolsVMAddr);
+                objCProtocol.instanceMethodsVMAddr              = vmAddrConverter.convertToVMAddr(protocolPtr->instanceMethodsVMAddr);
+                objCProtocol.classMethodsVMAddr                 = vmAddrConverter.convertToVMAddr(protocolPtr->classMethodsVMAddr);
+                objCProtocol.optionalInstanceMethodsVMAddr      = vmAddrConverter.convertToVMAddr(protocolPtr->optionalInstanceMethodsVMAddr);
+                objCProtocol.optionalClassMethodsVMAddr         = vmAddrConverter.convertToVMAddr(protocolPtr->optionalClassMethodsVMAddr);
 
                 handler(diag, protocolVMAddr, objCProtocol);
                 if (diag.hasError())
@@ -3955,20 +4704,15 @@ void MachOAnalyzer::forEachObjCProtocol(Diagnostics& diag, bool contentRebased,
     });
 }
 
-void MachOAnalyzer::forEachObjCMethod(uint64_t methodListVMAddr, bool contentRebased,
-                                      void (^handler)(uint64_t methodVMAddr, const ObjCMethod& method)) const {
+void MachOAnalyzer::forEachObjCMethod(uint64_t methodListVMAddr, const VMAddrConverter& vmAddrConverter,
+                                      void (^handler)(uint64_t methodVMAddr, const ObjCMethod& method),
+                                      bool* isRelativeMethodList) const {
     if ( methodListVMAddr == 0 )
         return;
 
     const uint64_t ptrSize = pointerSize();
     intptr_t slide = getSlide();
 
-    MachOAnalyzer::VMAddrConverter vmAddrConverter;
-    vmAddrConverter.preferredLoadAddress   = preferredLoadAddress();
-    vmAddrConverter.slide                  = slide;
-    vmAddrConverter.chainedPointerFormat   = hasChainedFixups() ? chainedPointerFormat() : 0;
-    vmAddrConverter.contentRebased         = contentRebased;
-
     if ( ptrSize == 8 ) {
         typedef uint64_t PtrTy;
         struct method_list_t {
@@ -3977,7 +4721,15 @@ void MachOAnalyzer::forEachObjCMethod(uint64_t methodListVMAddr, bool contentReb
             PtrTy       methodArrayBase; // Note this is the start the array method_t[0]
 
             uint32_t getEntsize() const {
-                return (entsize) & ~(uint32_t)3;
+                return entsize & ObjCMethodList::methodListSizeMask;
+            }
+
+            bool usesDirectOffsetsToSelectors() const {
+                return (entsize & 0x40000000) != 0;
+            }
+
+            bool usesRelativeOffsets() const {
+                return (entsize & 0x80000000) != 0;
             }
         };
 
@@ -3987,19 +4739,44 @@ void MachOAnalyzer::forEachObjCMethod(uint64_t methodListVMAddr, bool contentReb
             PtrTy impVMAddr;    // IMP
         };
 
+        struct relative_method_t {
+            int32_t nameOffset;   // SEL*
+            int32_t typesOffset;  // const char *
+            int32_t impOffset;    // IMP
+        };
+
         const method_list_t* methodList = (const method_list_t*)(methodListVMAddr + slide);
+        if ( methodList == nullptr )
+            return;
+        bool relativeMethodListsAreOffsetsToSelectors = methodList->usesDirectOffsetsToSelectors();
         uint64_t methodListArrayBaseVMAddr = methodListVMAddr + offsetof(method_list_t, methodArrayBase);
         for (unsigned i = 0; i != methodList->count; ++i) {
             uint64_t methodEntryOffset = i * methodList->getEntsize();
             uint64_t methodVMAddr = methodListArrayBaseVMAddr + methodEntryOffset;
-            const method_t* methodPtr = (const method_t*)(methodVMAddr + slide);
             ObjCMethod method;
-            method.nameVMAddr   = convertToVMAddr(methodPtr->nameVMAddr, vmAddrConverter);
-            method.typesVMAddr  = convertToVMAddr(methodPtr->typesVMAddr, vmAddrConverter);
-            method.impVMAddr    = convertToVMAddr(methodPtr->impVMAddr, vmAddrConverter);
-            method.nameLocationVMAddr = methodVMAddr + offsetof(method_t, nameVMAddr);
+            if ( methodList->usesRelativeOffsets() ) {
+                const relative_method_t* methodPtr = (const relative_method_t*)(methodVMAddr + slide);
+                if ( relativeMethodListsAreOffsetsToSelectors ) {
+                    method.nameVMAddr  = methodVMAddr + offsetof(relative_method_t, nameOffset) + methodPtr->nameOffset;
+                } else {
+                    PtrTy* nameLocation = (PtrTy*)((uint8_t*)&methodPtr->nameOffset + methodPtr->nameOffset);
+                    method.nameVMAddr   = vmAddrConverter.convertToVMAddr(*nameLocation);
+                }
+                method.typesVMAddr  = methodVMAddr + offsetof(relative_method_t, typesOffset) + methodPtr->typesOffset;
+                method.impVMAddr    = methodVMAddr + offsetof(relative_method_t, impOffset) + methodPtr->impOffset;
+                method.nameLocationVMAddr = methodVMAddr + offsetof(relative_method_t, nameOffset) + methodPtr->nameOffset;
+            } else {
+                const method_t* methodPtr = (const method_t*)(methodVMAddr + slide);
+                method.nameVMAddr   = vmAddrConverter.convertToVMAddr(methodPtr->nameVMAddr);
+                method.typesVMAddr  = vmAddrConverter.convertToVMAddr(methodPtr->typesVMAddr);
+                method.impVMAddr    = vmAddrConverter.convertToVMAddr(methodPtr->impVMAddr);
+                method.nameLocationVMAddr = methodVMAddr + offsetof(method_t, nameVMAddr);
+            }
             handler(methodVMAddr, method);
         }
+
+        if ( isRelativeMethodList != nullptr )
+            *isRelativeMethodList = methodList->usesRelativeOffsets();
     } else {
         typedef uint32_t PtrTy;
         struct method_list_t {
@@ -4008,7 +4785,15 @@ void MachOAnalyzer::forEachObjCMethod(uint64_t methodListVMAddr, bool contentReb
             PtrTy       methodArrayBase; // Note this is the start the array method_t[0]
 
             uint32_t getEntsize() const {
-                return (entsize) & ~(uint32_t)3;
+                return entsize & ObjCMethodList::methodListSizeMask;
+            }
+
+            bool usesDirectOffsetsToSelectors() const {
+                return (entsize & 0x40000000) != 0;
+            }
+
+            bool usesRelativeOffsets() const {
+                return (entsize & 0x80000000) != 0;
             }
         };
 
@@ -4018,23 +4803,48 @@ void MachOAnalyzer::forEachObjCMethod(uint64_t methodListVMAddr, bool contentReb
             PtrTy impVMAddr;    // IMP
         };
 
+        struct relative_method_t {
+            int32_t nameOffset;   // SEL*
+            int32_t typesOffset;  // const char *
+            int32_t impOffset;    // IMP
+        };
+
         const method_list_t* methodList = (const method_list_t*)(methodListVMAddr + slide);
+        if ( methodList == nullptr )
+            return;
+        bool relativeMethodListsAreOffsetsToSelectors = methodList->usesDirectOffsetsToSelectors();
         uint64_t methodListArrayBaseVMAddr = methodListVMAddr + offsetof(method_list_t, methodArrayBase);
         for (unsigned i = 0; i != methodList->count; ++i) {
             uint64_t methodEntryOffset = i * methodList->getEntsize();
             uint64_t methodVMAddr = methodListArrayBaseVMAddr + methodEntryOffset;
-            const method_t* methodPtr = (const method_t*)(methodVMAddr + slide);
             ObjCMethod method;
-            method.nameVMAddr   = convertToVMAddr(methodPtr->nameVMAddr, vmAddrConverter);
-            method.typesVMAddr  = convertToVMAddr(methodPtr->typesVMAddr, vmAddrConverter);
-            method.impVMAddr    = convertToVMAddr(methodPtr->impVMAddr, vmAddrConverter);
-            method.nameLocationVMAddr = methodVMAddr + offsetof(method_t, nameVMAddr);
+            if ( methodList->usesRelativeOffsets() ) {
+                const relative_method_t* methodPtr = (const relative_method_t*)(methodVMAddr + slide);
+                if ( relativeMethodListsAreOffsetsToSelectors ) {
+                    method.nameVMAddr  = methodVMAddr + offsetof(relative_method_t, nameOffset) + methodPtr->nameOffset;
+                } else {
+                    PtrTy* nameLocation = (PtrTy*)((uint8_t*)&methodPtr->nameOffset + methodPtr->nameOffset);
+                    method.nameVMAddr   = vmAddrConverter.convertToVMAddr(*nameLocation);
+                }
+                method.typesVMAddr  = methodVMAddr + offsetof(relative_method_t, typesOffset) + methodPtr->typesOffset;
+                method.impVMAddr    = methodVMAddr + offsetof(relative_method_t, impOffset) + methodPtr->impOffset;
+                method.nameLocationVMAddr = methodVMAddr + offsetof(relative_method_t, nameOffset) + methodPtr->nameOffset;
+            } else {
+                const method_t* methodPtr = (const method_t*)(methodVMAddr + slide);
+                method.nameVMAddr   = vmAddrConverter.convertToVMAddr(methodPtr->nameVMAddr);
+                method.typesVMAddr  = vmAddrConverter.convertToVMAddr(methodPtr->typesVMAddr);
+                method.impVMAddr    = vmAddrConverter.convertToVMAddr(methodPtr->impVMAddr);
+                method.nameLocationVMAddr = methodVMAddr + offsetof(method_t, nameVMAddr);
+            }
             handler(methodVMAddr, method);
         }
+
+        if ( isRelativeMethodList != nullptr )
+            *isRelativeMethodList = methodList->usesRelativeOffsets();
     }
 }
 
-void MachOAnalyzer::forEachObjCProperty(uint64_t propertyListVMAddr, bool contentRebased,
+void MachOAnalyzer::forEachObjCProperty(uint64_t propertyListVMAddr, const VMAddrConverter& vmAddrConverter,
                                         void (^handler)(uint64_t propertyVMAddr, const ObjCProperty& property)) const {
     if ( propertyListVMAddr == 0 )
         return;
@@ -4042,12 +4852,6 @@ void MachOAnalyzer::forEachObjCProperty(uint64_t propertyListVMAddr, bool conten
     const uint64_t ptrSize = pointerSize();
     intptr_t slide = getSlide();
 
-    MachOAnalyzer::VMAddrConverter vmAddrConverter;
-    vmAddrConverter.preferredLoadAddress   = preferredLoadAddress();
-    vmAddrConverter.slide                  = slide;
-    vmAddrConverter.chainedPointerFormat   = hasChainedFixups() ? chainedPointerFormat() : 0;
-    vmAddrConverter.contentRebased         = contentRebased;
-
     if ( ptrSize == 8 ) {
         typedef uint64_t PtrTy;
         struct property_list_t {
@@ -4072,8 +4876,8 @@ void MachOAnalyzer::forEachObjCProperty(uint64_t propertyListVMAddr, bool conten
             uint64_t propertyVMAddr = propertyListArrayBaseVMAddr + propertyEntryOffset;
             const property_t* propertyPtr = (const property_t*)(propertyVMAddr + slide);
             ObjCProperty property;
-            property.nameVMAddr         = convertToVMAddr(propertyPtr->nameVMAddr, vmAddrConverter);
-            property.attributesVMAddr   = convertToVMAddr(propertyPtr->attributesVMAddr, vmAddrConverter);
+            property.nameVMAddr         = vmAddrConverter.convertToVMAddr(propertyPtr->nameVMAddr);
+            property.attributesVMAddr   = vmAddrConverter.convertToVMAddr(propertyPtr->attributesVMAddr);
             handler(propertyVMAddr, property);
         }
     } else {
@@ -4100,25 +4904,115 @@ void MachOAnalyzer::forEachObjCProperty(uint64_t propertyListVMAddr, bool conten
             uint64_t propertyVMAddr = propertyListArrayBaseVMAddr + propertyEntryOffset;
             const property_t* propertyPtr = (const property_t*)(propertyVMAddr + slide);
             ObjCProperty property;
-            property.nameVMAddr         = convertToVMAddr(propertyPtr->nameVMAddr, vmAddrConverter);
-            property.attributesVMAddr   = convertToVMAddr(propertyPtr->attributesVMAddr, vmAddrConverter);
+            property.nameVMAddr         = vmAddrConverter.convertToVMAddr(propertyPtr->nameVMAddr);
+            property.attributesVMAddr   = vmAddrConverter.convertToVMAddr(propertyPtr->attributesVMAddr);
             handler(propertyVMAddr, property);
         }
     }
 }
 
+void MachOAnalyzer::forEachObjCProtocol(uint64_t protocolListVMAddr, const VMAddrConverter& vmAddrConverter,
+                                        void (^handler)(uint64_t protocolRefVMAddr, const ObjCProtocol&)) const
+{
+    if ( protocolListVMAddr == 0 )
+        return;
+
+    auto ptrSize = pointerSize();
+    intptr_t slide = getSlide();
+
+    if ( ptrSize == 8 ) {
+        typedef uint64_t PtrTy;
+        struct protocol_ref_t {
+            PtrTy       refVMAddr;
+        };
+        struct protocol_list_t {
+            PtrTy           count;
+            protocol_ref_t  array[];
+        };
+        struct protocol_t {
+            PtrTy    isaVMAddr;
+            PtrTy    nameVMAddr;
+            PtrTy    protocolsVMAddr;
+            PtrTy    instanceMethodsVMAddr;
+            PtrTy    classMethodsVMAddr;
+            PtrTy    optionalInstanceMethodsVMAddr;
+            PtrTy    optionalClassMethodsVMAddr;
+            PtrTy    instancePropertiesVMAddr;
+            uint32_t size;
+            uint32_t flags;
+            // Fields below this point are not always present on disk.
+            PtrTy    extendedMethodTypesVMAddr;
+            PtrTy    demangledNameVMAddr;
+            PtrTy    classPropertiesVMAddr;
+        };
+
+        const protocol_list_t* protoList = (const protocol_list_t*)(protocolListVMAddr + slide);
+        for (PtrTy i = 0; i != protoList->count; ++i) {
+            uint64_t protocolVMAddr = vmAddrConverter.convertToVMAddr(protoList->array[i].refVMAddr);
+
+            const protocol_t* protocolPtr = (const protocol_t*)(protocolVMAddr + slide);
+            ObjCProtocol objCProtocol;
+            objCProtocol.isaVMAddr                          = vmAddrConverter.convertToVMAddr(protocolPtr->isaVMAddr);
+            objCProtocol.nameVMAddr                         = vmAddrConverter.convertToVMAddr(protocolPtr->nameVMAddr);
+            objCProtocol.protocolsVMAddr                    = vmAddrConverter.convertToVMAddr(protocolPtr->protocolsVMAddr);
+            objCProtocol.instanceMethodsVMAddr              = vmAddrConverter.convertToVMAddr(protocolPtr->instanceMethodsVMAddr);
+            objCProtocol.classMethodsVMAddr                 = vmAddrConverter.convertToVMAddr(protocolPtr->classMethodsVMAddr);
+            objCProtocol.optionalInstanceMethodsVMAddr      = vmAddrConverter.convertToVMAddr(protocolPtr->optionalInstanceMethodsVMAddr);
+            objCProtocol.optionalClassMethodsVMAddr         = vmAddrConverter.convertToVMAddr(protocolPtr->optionalClassMethodsVMAddr);
+
+            handler(protocolVMAddr, objCProtocol);
+        }
+    } else {
+        typedef uint32_t PtrTy;
+        struct protocol_ref_t {
+            PtrTy       refVMAddr;
+        };
+        struct protocol_list_t {
+            PtrTy           count;
+            protocol_ref_t  array[];
+        };
+        struct protocol_t {
+            PtrTy    isaVMAddr;
+            PtrTy    nameVMAddr;
+            PtrTy    protocolsVMAddr;
+            PtrTy    instanceMethodsVMAddr;
+            PtrTy    classMethodsVMAddr;
+            PtrTy    optionalInstanceMethodsVMAddr;
+            PtrTy    optionalClassMethodsVMAddr;
+            PtrTy    instancePropertiesVMAddr;
+            uint32_t size;
+            uint32_t flags;
+            // Fields below this point are not always present on disk.
+            PtrTy    extendedMethodTypesVMAddr;
+            PtrTy    demangledNameVMAddr;
+            PtrTy    classPropertiesVMAddr;
+        };
+
+        const protocol_list_t* protoList = (const protocol_list_t*)(protocolListVMAddr + slide);
+        for (PtrTy i = 0; i != protoList->count; ++i) {
+            uint64_t protocolVMAddr = vmAddrConverter.convertToVMAddr(protoList->array[i].refVMAddr);
+
+            const protocol_t* protocolPtr = (const protocol_t*)(protocolVMAddr + slide);
+            ObjCProtocol objCProtocol;
+            objCProtocol.isaVMAddr                          = vmAddrConverter.convertToVMAddr(protocolPtr->isaVMAddr);
+            objCProtocol.nameVMAddr                         = vmAddrConverter.convertToVMAddr(protocolPtr->nameVMAddr);
+            objCProtocol.protocolsVMAddr                    = vmAddrConverter.convertToVMAddr(protocolPtr->protocolsVMAddr);
+            objCProtocol.instanceMethodsVMAddr              = vmAddrConverter.convertToVMAddr(protocolPtr->instanceMethodsVMAddr);
+            objCProtocol.classMethodsVMAddr                 = vmAddrConverter.convertToVMAddr(protocolPtr->classMethodsVMAddr);
+            objCProtocol.optionalInstanceMethodsVMAddr      = vmAddrConverter.convertToVMAddr(protocolPtr->optionalInstanceMethodsVMAddr);
+            objCProtocol.optionalClassMethodsVMAddr         = vmAddrConverter.convertToVMAddr(protocolPtr->optionalClassMethodsVMAddr);
+
+            handler(protocolVMAddr, objCProtocol);
+        }
+    }
+}
+
 
-void MachOAnalyzer::forEachObjCSelectorReference(Diagnostics& diag, bool contentRebased,
+void MachOAnalyzer::forEachObjCSelectorReference(Diagnostics& diag, const VMAddrConverter& vmAddrConverter,
                                                  void (^handler)(uint64_t selRefVMAddr, uint64_t selRefTargetVMAddr)) const {
     const uint64_t ptrSize = pointerSize();
     intptr_t slide = getSlide();
 
-    MachOAnalyzer::VMAddrConverter vmAddrConverter;
-    vmAddrConverter.preferredLoadAddress   = preferredLoadAddress();
-    vmAddrConverter.slide                  = slide;
-    vmAddrConverter.chainedPointerFormat   = hasChainedFixups() ? chainedPointerFormat() : 0;
-    vmAddrConverter.contentRebased         = contentRebased;
-
     forEachSection(^(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
         if ( strncmp(sectInfo.segInfo.segName, "__DATA", 6) != 0 )
             return;
@@ -4137,7 +5031,7 @@ void MachOAnalyzer::forEachObjCSelectorReference(Diagnostics& diag, bool content
             typedef uint64_t PtrTy;
             for (uint64_t i = 0; i != selRefsSize; i += sizeof(PtrTy)) {
                 uint64_t selRefVMAddr = selRefSectionVMAddr + i;
-                uint64_t selRefTargetVMAddr = convertToVMAddr(*(PtrTy*)(selRefs + i), vmAddrConverter);
+                uint64_t selRefTargetVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(selRefs + i));
                 handler(selRefVMAddr, selRefTargetVMAddr);
                 if (diag.hasError()) {
                     stop = true;
@@ -4148,7 +5042,7 @@ void MachOAnalyzer::forEachObjCSelectorReference(Diagnostics& diag, bool content
             typedef uint32_t PtrTy;
             for (uint64_t i = 0; i != selRefsSize; i += sizeof(PtrTy)) {
                 uint64_t selRefVMAddr = selRefSectionVMAddr + i;
-                uint64_t selRefTargetVMAddr = convertToVMAddr(*(PtrTy*)(selRefs + i), vmAddrConverter);
+                uint64_t selRefTargetVMAddr = vmAddrConverter.convertToVMAddr(*(PtrTy*)(selRefs + i));
                 handler(selRefVMAddr, selRefTargetVMAddr);
                 if (diag.hasError()) {
                     stop = true;
@@ -4249,7 +5143,7 @@ uint32_t MachOAnalyzer::loadCommandsFreeSpace() const
 }
 
 void MachOAnalyzer::forEachWeakDef(Diagnostics& diag,
-                                   void (^handler)(const char* symbolName, uintptr_t imageOffset, bool isFromExportTrie)) const {
+                                   void (^handler)(const char* symbolName, uint64_t imageOffset, bool isFromExportTrie)) const {
     uint64_t baseAddress = preferredLoadAddress();
     forEachGlobalSymbol(diag, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) {
         if ( (n_desc & N_WEAK_DEF) != 0 ) {
@@ -4268,6 +5162,7 @@ void MachOAnalyzer::forEachWeakDef(Diagnostics& diag,
     });
 }
 
+
 } // dyld3
 
 
index 62a322539328398470a9f7501855172045803c8b..1482bf51cecb26c27a8e7f18ece44e4ff2588140 100644 (file)
@@ -44,7 +44,23 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
     using MachOLoaded::getDylibInstallName;
     using MachOLoaded::FoundSymbol;
     using MachOLoaded::findExportedSymbol;
+    using MachOLoaded::forEachGlobalSymbol;
+    using MachOLoaded::forEachLocalSymbol;
+    using MachOFile::canBePlacedInDyldCache;
+    using MachOFile::forEachLoadCommand;
+    using MachOFile::removeLoadCommand;
+
+    enum class Rebase {
+        unknown,
+        pointer32,
+        pointer64,
+        textPCrel32,
+        textAbsolute32,
+    };
 
+    static bool loadFromBuffer(Diagnostics& diag, const closure::FileSystem& fileSystem,
+                               const char* path, const GradedArchs& archs, Platform platform,
+                               closure::LoadedFileInfo& info);
     static closure::LoadedFileInfo load(Diagnostics& diag, const closure::FileSystem& fileSystem,
                                         const char* logicalPath, const GradedArchs& archs, Platform platform, char realerPath[MAXPATHLEN]);
     static const MachOAnalyzer*  validMainExecutable(Diagnostics& diag, const mach_header* mh, const char* path, uint64_t sliceLength,
@@ -52,25 +68,47 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
 
     typedef void (^ExportsCallback)(const char* symbolName, uint64_t imageOffset, uint64_t flags,
                                     uint64_t other, const char* importName, bool& stop);
-    bool                validMachOForArchAndPlatform(Diagnostics& diag, size_t mappedSize, const char* path, const GradedArchs& archs, Platform platform) const;
+    bool                validMachOForArchAndPlatform(Diagnostics& diag, size_t mappedSize, const char* path, const GradedArchs& archs, Platform platform, bool isOSBinary) const;
+
+    // Caches data useful for converting from raw data to VM addresses
+    struct VMAddrConverter {
+        uint64_t preferredLoadAddress                       = 0;
+        intptr_t slide                                      = 0;
+        uint16_t chainedPointerFormat                       = 0;
+        bool contentRebased                                 = false;
+#if !(BUILDING_LIBDYLD || BUILDING_DYLD)
+        enum class SharedCacheFormat : uint8_t {
+            none            = 0,
+            v2_x86_64_tbi   = 1,
+            v3              = 2
+        };
+        SharedCacheFormat sharedCacheChainedPointerFormat   = SharedCacheFormat::none;
+#endif
+
+        uint64_t convertToVMAddr(uint64_t v) const;
+    };
+
+    VMAddrConverter     makeVMAddrConverter(bool contentRebased) const;
+
     uint64_t            mappedSize() const;
     bool                hasObjC() const;
     bool                hasPlusLoadMethod(Diagnostics& diag) const;
+    bool                usesObjCGarbageCollection() const;
+    bool                isSwiftLibrary() 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                hasProgramVars(Diagnostics& diag, uint32_t& progVarsOffset) const;
     void                forEachCDHash(void (^handler)(const 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                getEntry(uint64_t& offset, bool& usesCRT) const;
     bool                isSlideable() const;
-    bool                hasInitializer(Diagnostics& diag, bool contentRebased, const void* dyldCache=nullptr) const;
+    bool                hasInitializer(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, const void* dyldCache=nullptr) const;
     void                forEachInitializerPointerSection(Diagnostics& diag, void (^callback)(uint32_t sectionOffset, uint32_t sectionSize, const uint8_t* content, bool& stop)) const;
-    void                forEachInitializer(Diagnostics& diag, bool contentRebased, void (^callback)(uint32_t offset), const void* dyldCache=nullptr) const;
-    bool                hasTerminators(Diagnostics& diag, bool contentRebased) const;
-    void                forEachTerminator(Diagnostics& diag, bool contentRebased, void (^callback)(uint32_t offset)) const;
+    void                forEachInitializer(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, void (^callback)(uint32_t offset), const void* dyldCache=nullptr) const;
+    bool                hasTerminators(Diagnostics& diag, const VMAddrConverter& vmAddrConverter) const;
+    void                forEachTerminator(Diagnostics& diag, const VMAddrConverter& vmAddrConverter, void (^callback)(uint32_t offset)) const;
     void                forEachDOFSection(Diagnostics& diag, void (^callback)(uint32_t offset)) const;
     uint32_t            segmentCount() const;
     void                forEachExportedSymbol(Diagnostics& diag, ExportsCallback callback) const;
@@ -87,26 +125,36 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
     const void*         getBindOpcodes(uint32_t& size) const;
     const void*         getLazyBindOpcodes(uint32_t& size) const;
     const void*         getSplitSeg(uint32_t& size) const;
+    bool                hasSplitSeg() const;
+    bool                isSplitSegV1() const;
+    bool                isSplitSegV2() 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                forEachRebase(Diagnostics& diag, void (^callback)(uint64_t runtimeOffset, bool isLazyPointerRebase, 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, bool lazyBind, uint64_t addend, bool& stop),
-                                                       void (^strongHandler)(const char* symbolName),
-                                                       void (^missingLazyBindHandler)()) const;
+                                    void (^strongHandler)(const char* symbolName)) const;
+    void                forEachBind(Diagnostics& diag, void (^callback)(uint64_t runtimeOffset, int libOrdinal, uint8_t type, const char* symbolName,
+                                                                        bool weakImport, bool lazyBind, 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;
-    bool                canHavePrecomputedDlopenClosure(const char* path, void (^failureReason)(const char*)) 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 segIndexSet, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind, bool& stop)) 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, bool lazyBind, uint64_t addend, bool& stop),
-                                                       void (^strongHandler)(const char* symbolName),
-                                                       void (^missingLazyBindHandler)()) const;
+                                                       void (^strongHandler)(const char* symbolName)) const;
     bool                canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const;
+    bool                canHavePrecomputedDlopenClosure(const char* path, void (^failureReason)(const char*)) const;
+#if BUILDING_APP_CACHE_UTIL
+    bool                canBePlacedInKernelCollection(const char* path, void (^failureReason)(const char*)) const;
+#endif
+    bool                usesClassicRelocationsInKernelCollection() const;
     uint32_t            loadCommandsFreeSpace() const;
+    bool                hasStompedLazyOpcodes() const;
  
 #if DEBUG
     void                validateDyldCacheDylib(Diagnostics& diag, const char* path) const;
@@ -117,6 +165,9 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
     static uint16_t     chainedPointerFormat(const dyld_chained_fixups_header* chainHeader);
     bool                hasUnalignedPointerFixups() const;
     const dyld_chained_fixups_header* chainedFixupsHeader() const;
+    bool                hasFirmwareChainStarts(uint16_t* pointerFormat, uint32_t* startsCount, const uint32_t** starts) const;
+    bool                isOSBinary(int fd, uint64_t sliceOffset, uint64_t sliceSize) const;  // checks if binary is codesigned to be part of the OS
+    static bool         sliceIsOSBinary(int fd, uint64_t sliceOffset, uint64_t sliceSize);
 
     const MachOAnalyzer*    remapIfZeroFill(Diagnostics& diag, const closure::FileSystem& fileSystem, closure::LoadedFileInfo& info) const;
 
@@ -141,20 +192,12 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
         dyld3::OverflowSafeArray<SectionInfo> sectionInfos = { buffer, sizeof(buffer) / sizeof(buffer[0]) };
     };
 
-    // Caches data useful for converting from raw data to VM addresses
-    struct VMAddrConverter {
-        uint64_t preferredLoadAddress   = 0;
-        intptr_t slide                  = 0;
-        uint16_t chainedPointerFormat   = 0;
-        bool contentRebased             = false;
-    };
-
     struct ObjCClassInfo {
         // These fields are all present on the objc_class_t struct
         uint64_t isaVMAddr                                  = 0;
         uint64_t superclassVMAddr                           = 0;
         //uint64_t methodCacheBuckets;
-        //uint64_t methodCacheProperties;
+        uint64_t methodCacheVMAddr                          = 0;
         uint64_t dataVMAddr                                 = 0;
 
         // This field is only present if this is a Swift object, ie, has the Swift
@@ -171,6 +214,7 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
         // These are from the class_ro_t which data points to
         enum class ReadOnlyDataField {
             name,
+            baseProtocols,
             baseMethods,
             baseProperties,
             flags
@@ -180,6 +224,9 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
         uint64_t nameVMAddr(uint32_t pointerSize) const {
             return getReadOnlyDataField(ReadOnlyDataField::name, pointerSize);
         }
+        uint64_t baseProtocolsVMAddr(uint32_t pointerSize) const {
+            return getReadOnlyDataField(ReadOnlyDataField::baseProtocols, pointerSize);
+        }
         uint64_t baseMethodsVMAddr(uint32_t pointerSize) const {
             return getReadOnlyDataField(ReadOnlyDataField::baseMethods, pointerSize);
         }
@@ -214,6 +261,19 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
         }
     };
 
+    struct ObjCMethodList {
+        // This matches the bits in the objc runtime
+        enum : uint32_t {
+            methodListIsUniqued = 0x1,
+            methodListIsSorted  = 0x2,
+
+            // The size is bits 2 through 16 of the entsize field
+            // The low 2 bits are uniqued/sorted as above.  The upper 16-bits
+            // are reserved for other flags
+            methodListSizeMask  = 0x0000FFFC
+        };
+    };
+
     struct ObjCImageInfo {
         uint32_t version;
         uint32_t flags;
@@ -251,7 +311,7 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
     struct ObjCProtocol {
         uint64_t isaVMAddr;
         uint64_t nameVMAddr;
-        //uint64_t protocolsVMAddr;
+        uint64_t protocolsVMAddr;
         uint64_t instanceMethodsVMAddr;
         uint64_t classMethodsVMAddr;
         uint64_t optionalInstanceMethodsVMAddr;
@@ -263,10 +323,6 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
         //uint64_t extendedMethodTypesVMAddr;
         //uint64_t demangledNameVMAddr;
         //uint64_t classPropertiesVMAddr;
-
-        // Note this isn't in a protocol, but we use it in dyld to track if the protocol
-        // is large enough to avoid a reallocation in objc.
-        bool requiresObjCReallocation;
     };
 
     enum class PrintableStringResult {
@@ -279,29 +335,42 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
     const char* getPrintableString(uint64_t stringVMAddr, PrintableStringResult& result,
                                    SectionCache* sectionCache = nullptr,
                                    bool (^sectionHandler)(const SectionInfo& sectionInfo) = nullptr) const;
-    
-    void forEachObjCClass(Diagnostics& diag, bool contentRebased,
+
+    void parseObjCClass(Diagnostics& diag, const VMAddrConverter& vmAddrConverter,
+                        uint64_t classVMAddr,
+                        void (^handler)(Diagnostics& diag,
+                                        uint64_t classSuperclassVMAddr,
+                                        uint64_t classDataVMAddr,
+                                        const ObjCClassInfo& objcClass)) const;
+
+    void forEachObjCClass(Diagnostics& diag, const VMAddrConverter& vmAddrConverter,
                           void (^handler)(Diagnostics& diag, uint64_t classVMAddr,
                                           uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
                                           const ObjCClassInfo& objcClass, bool isMetaClass)) const;
 
-    void forEachObjCCategory(Diagnostics& diag, bool contentRebased,
+    void forEachObjCCategory(Diagnostics& diag, const VMAddrConverter& vmAddrConverter,
                              void (^handler)(Diagnostics& diag, uint64_t categoryVMAddr,
                                              const dyld3::MachOAnalyzer::ObjCCategory& objcCategory)) const;
 
-    void forEachObjCProtocol(Diagnostics& diag, bool contentRebased,
+    // lists all Protocols defined in the image
+    void forEachObjCProtocol(Diagnostics& diag, const VMAddrConverter& vmAddrConverter,
                              void (^handler)(Diagnostics& diag, uint64_t protocolVMAddr,
                                              const dyld3::MachOAnalyzer::ObjCProtocol& objCProtocol)) const;
 
     // Walk a method list starting from its vmAddr.
     // Note, classes, categories, protocols, etc, all share the same method list struture so can all use this.
-    void forEachObjCMethod(uint64_t methodListVMAddr, bool contentRebased,
-                           void (^handler)(uint64_t methodVMAddr, const ObjCMethod& method)) const;
+    void forEachObjCMethod(uint64_t methodListVMAddr, const VMAddrConverter& vmAddrConverter,
+                           void (^handler)(uint64_t methodVMAddr, const ObjCMethod& method),
+                           bool* isRelativeMethodList = nullptr) const;
 
-    void forEachObjCProperty(uint64_t propertyListVMAddr, bool contentRebased,
+    void forEachObjCProperty(uint64_t propertyListVMAddr, const VMAddrConverter& vmAddrConverter,
                              void (^handler)(uint64_t propertyVMAddr, const ObjCProperty& property)) const;
 
-    void forEachObjCSelectorReference(Diagnostics& diag, bool contentRebased,
+    // lists all Protocols on a protocol_list_t
+    void forEachObjCProtocol(uint64_t protocolListVMAddr, const VMAddrConverter& vmAddrConverter,
+                             void (^handler)(uint64_t protocolRefVMAddr, const ObjCProtocol& protocol)) const;
+
+    void forEachObjCSelectorReference(Diagnostics& diag, const VMAddrConverter& vmAddrConverter,
                                       void (^handler)(uint64_t selRefVMAddr, uint64_t selRefTargetVMAddr)) const;
 
     void forEachObjCMethodName(void (^handler)(const char* methodName)) const;
@@ -310,7 +379,7 @@ struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
 
     const ObjCImageInfo* objcImageInfo() const;
 
-    void forEachWeakDef(Diagnostics& diag, void (^handler)(const char* symbolName, uintptr_t imageOffset, bool isFromExportTrie)) const;
+    void forEachWeakDef(Diagnostics& diag, void (^handler)(const char* symbolName, uint64_t imageOffset, bool isFromExportTrie)) const;
     
 private:
 
@@ -324,7 +393,8 @@ private:
                     segSize           : 61;
        };
 
-    enum class Malformed { linkeditOrder, linkeditAlignment, linkeditPermissions, dyldInfoAndlocalRelocs, segmentOrder, textPermissions, executableData, codeSigAlignment };
+    enum class Malformed { linkeditOrder, linkeditAlignment, linkeditPermissions, dyldInfoAndlocalRelocs, segmentOrder,
+                            textPermissions, executableData, writableData, codeSigAlignment, sectionsAddrRangeWithinSegment };
     bool                    enforceFormat(Malformed) const;
 
     const uint8_t*          getContentForVMAddr(const LayoutInfo& info, uint64_t vmAddr) const;
@@ -340,7 +410,7 @@ private:
     bool                    validChainedFixupsInfoOldArm64e(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 segIndexSet, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, Rebase kind) 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;
@@ -353,19 +423,20 @@ private:
 
     void                    getAllSegmentsInfos(Diagnostics& diag, SegmentInfo segments[]) const;
     bool                    segmentHasTextRelocs(uint32_t segIndex) const;
-    uint64_t                relocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const;
+    uint64_t                localRelocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const;
+    uint64_t                externalRelocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const;
     bool                    segIndexAndOffsetForAddress(uint64_t addr, const SegmentInfo segmentsInfos[], uint32_t segCount, uint32_t& segIndex, uint64_t& segOffset) const;
     void                    parseOrgArm64eChainedFixups(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, uint16_t format, bool& stop)) const;
     bool                    contentIsRegularStub(const uint8_t* helperContent) const;
-    uint64_t                entryAddrFromThreadCmd(const thread_command* cmd) const;
     void                    recurseTrie(Diagnostics& diag, const uint8_t* const start, const uint8_t* p, const uint8_t* const end,
                                         OverflowSafeArray<char>& cummulativeString, int curStrOffset, bool& stop, MachOAnalyzer::ExportsCallback callback) const;
     void                    analyzeSegmentsLayout(uint64_t& vmSpace, bool& hasZeroFill) const;
 
 };
 
+
 } // namespace dyld3
 
 #endif /* MachOAnalyzer_h */
diff --git a/dyld3/MachOAnalyzerSet.cpp b/dyld3/MachOAnalyzerSet.cpp
new file mode 100644 (file)
index 0000000..3ca24c7
--- /dev/null
@@ -0,0 +1,530 @@
+/*
+ * Copyright (c) 2019 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET 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 "MachOAnalyzerSet.h"
+#include "DyldSharedCache.h"
+
+#if BUILDING_DYLD
+  namespace dyld { void log(const char*, ...); }
+#endif
+
+namespace dyld3 {
+
+static bool hasHigh8(uint64_t addend)
+{
+    // distinguish negative addend from TBI
+    if ( (addend >> 56) == 0 )
+        return false;
+    return ( (addend >> 48) != 0xFFFF );
+}
+
+void MachOAnalyzerSet::WrappedMachO::forEachBind(Diagnostics& diag, FixUpHandler fixUpHandler, CachePatchHandler patchHandler) const
+{
+    const bool              is64            = _mh->is64();
+    __block int             lastLibOrdinal  = 256;
+    __block const char*     lastSymbolName  = nullptr;
+    __block uint64_t        lastAddend      = 0;
+    __block FixupTarget     target;
+    __block PointerMetaData pmd;
+    _mh->forEachBind(diag, ^(uint64_t runtimeOffset, int libOrdinal, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop) {
+        if ( (symbolName == lastSymbolName) && (libOrdinal == lastLibOrdinal) && (addend == lastAddend) ) {
+            // same symbol lookup as last location
+            fixUpHandler(runtimeOffset, pmd, target, stop);
+        }
+        else if ( this->findSymbolFrom(diag, libOrdinal, symbolName, weakImport, lazyBind, addend, patchHandler, target) ) {
+            pmd.high8 = 0;
+            if ( is64 && (target.addend != 0) ) {
+                if ( hasHigh8(target.addend) ) {
+                    pmd.high8  = (target.addend >> 56);
+                    target.offsetInImage &= 0x00FFFFFFFFFFFFFFULL;
+                    target.addend        &= 0x00FFFFFFFFFFFFFFULL;
+                }
+            }
+            if ( !target.skippableWeakDef ) {
+                fixUpHandler(runtimeOffset, pmd, target, stop);
+                lastSymbolName = symbolName;
+                lastLibOrdinal = libOrdinal;
+                lastAddend     = addend;
+            }
+        }
+        else {
+            // call handler with missing symbol before stopping
+            if ( target.kind == FixupTarget::Kind::bindMissingSymbol )
+                fixUpHandler(runtimeOffset, pmd, target, stop);
+            stop = true;
+        }
+    }, ^(const char* symbolName) {
+    });
+}
+
+MachOAnalyzerSet::PointerMetaData::PointerMetaData()
+{
+    this->diversity           = 0;
+    this->high8               = 0;
+    this->authenticated       = 0;
+    this->key                 = 0;
+    this->usesAddrDiversity   = 0;
+}
+
+MachOAnalyzerSet::PointerMetaData::PointerMetaData(const MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, uint16_t pointer_format)
+{
+    this->diversity           = 0;
+    this->high8               = 0;
+    this->authenticated       = 0;
+    this->key                 = 0;
+    this->usesAddrDiversity   = 0;
+    switch ( pointer_format ) {
+        case DYLD_CHAINED_PTR_ARM64E:
+        case DYLD_CHAINED_PTR_ARM64E_KERNEL:
+        case DYLD_CHAINED_PTR_ARM64E_USERLAND:
+        case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
+        case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
+            this->authenticated = fixupLoc->arm64e.authRebase.auth;
+            if ( this->authenticated ) {
+                this->key               = fixupLoc->arm64e.authRebase.key;
+                this->usesAddrDiversity = fixupLoc->arm64e.authRebase.addrDiv;
+                this->diversity         = fixupLoc->arm64e.authRebase.diversity;
+            }
+            else if ( fixupLoc->arm64e.bind.bind == 0 ) {
+                this->high8             = fixupLoc->arm64e.rebase.high8;
+            }
+            break;
+        case DYLD_CHAINED_PTR_64:
+        case DYLD_CHAINED_PTR_64_OFFSET:
+            if ( fixupLoc->generic64.bind.bind == 0 )
+                this->high8             = fixupLoc->generic64.rebase.high8;
+            break;
+    }
+}
+
+
+void MachOAnalyzerSet::WrappedMachO::forEachFixup(Diagnostics& diag, FixUpHandler fixup, CachePatchHandler patcher) const
+{
+    uint16_t        fmPointerFormat;
+    uint32_t        fmStartsCount;
+    const uint32_t* fmStarts;
+    const MachOAnalyzer* ma = _mh;
+    const uint64_t prefLoadAddr = ma->preferredLoadAddress();
+    if ( ma->hasChainedFixups() ) {
+        // build targets table
+        STACK_ALLOC_OVERFLOW_SAFE_ARRAY(FixupTarget, targets, 512);
+        ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
+            targets.default_constuct_back();
+            FixupTarget& foundTarget = targets.back();
+            if ( !this->findSymbolFrom(diag, libOrdinal, symbolName, weakImport, false, addend, patcher, foundTarget) ) {
+                // call handler with missing symbol before stopping
+                if ( foundTarget.kind == FixupTarget::Kind::bindMissingSymbol )
+                    fixup(0, PointerMetaData(), foundTarget, stop);
+                stop = true;
+            }
+        });
+        if ( diag.hasError() )
+            return;
+
+        // walk all chains
+        ma->withChainStarts(diag, ma->chainStartsOffset(), ^(const dyld_chained_starts_in_image* startsInfo) {
+            ma->forEachFixupInAllChains(diag, startsInfo, false, ^(MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc,
+                                                                    const dyld_chained_starts_in_segment* segInfo, bool& fixupsStop) {
+                uint64_t fixupOffset = (uint8_t*)fixupLoc - (uint8_t*)ma;
+                uint64_t targetOffset;
+                uint32_t bindOrdinal;
+                int64_t  embeddedAddend;
+                PointerMetaData pmd(fixupLoc, segInfo->pointer_format);
+                if ( fixupLoc->isBind(segInfo->pointer_format, bindOrdinal, embeddedAddend) ) {
+                    if ( bindOrdinal < targets.count() ) {
+                        if ( embeddedAddend == 0 ) {
+                            if ( hasHigh8(targets[bindOrdinal].addend) ) {
+                                FixupTarget targetWithoutHigh8 = targets[bindOrdinal];
+                                pmd.high8 = (targetWithoutHigh8.addend >> 56);
+                                targetWithoutHigh8.offsetInImage &= 0x00FFFFFFFFFFFFFFULL;
+                                targetWithoutHigh8.addend        &= 0x00FFFFFFFFFFFFFFULL;
+                                fixup(fixupOffset, pmd, targetWithoutHigh8, fixupsStop);
+                            }
+                            else {
+                                fixup(fixupOffset, pmd, targets[bindOrdinal], fixupsStop);
+                            }
+                        }
+                        else {
+                            // pointer on disk encodes extra addend, make pseudo target for that
+                            FixupTarget targetWithAddend = targets[bindOrdinal];
+                            targetWithAddend.addend        += embeddedAddend;
+                            targetWithAddend.offsetInImage += embeddedAddend;
+                            fixup(fixupOffset, pmd, targetWithAddend, fixupsStop);
+                        }
+                    }
+                    else {
+                        diag.error("out of range bind ordinal %d (max %lu)", bindOrdinal, targets.count());
+                        fixupsStop = true;
+                    }
+                }
+                else if ( fixupLoc->isRebase(segInfo->pointer_format, prefLoadAddr, targetOffset) ) {
+                    FixupTarget rebaseTarget;
+                    rebaseTarget.kind               = FixupTarget::Kind::rebase;
+                    rebaseTarget.foundInImage       = *this;
+                    rebaseTarget.offsetInImage      = targetOffset & 0x00FFFFFFFFFFFFFFULL;
+                    rebaseTarget.isLazyBindRebase   = false; // FIXME
+                    fixup(fixupOffset, pmd, rebaseTarget, fixupsStop);
+                }
+            });
+        });
+    }
+    else if ( ma->hasFirmwareChainStarts(&fmPointerFormat, &fmStartsCount, &fmStarts) ) {
+        // This is firmware which only has rebases, the chain starts info is in a section (not LINKEDIT)
+        ma->forEachFixupInAllChains(diag, fmPointerFormat, fmStartsCount, fmStarts, ^(MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, bool& stop) {
+            uint64_t fixupOffset = (uint8_t*)fixupLoc - (uint8_t*)ma;
+            PointerMetaData pmd(fixupLoc, fmPointerFormat);
+            uint64_t targetOffset;
+            fixupLoc->isRebase(fmPointerFormat, prefLoadAddr, targetOffset);
+            FixupTarget rebaseTarget;
+            rebaseTarget.kind               = FixupTarget::Kind::rebase;
+            rebaseTarget.foundInImage       = *this;
+            rebaseTarget.offsetInImage      = targetOffset & 0x00FFFFFFFFFFFFFFULL;
+            rebaseTarget.isLazyBindRebase   = false;
+            fixup(fixupOffset, pmd, rebaseTarget, stop);
+        });
+    }
+    else {
+        // process all rebase opcodes
+        const bool is64 = ma->is64();
+        ma->forEachRebase(diag, ^(uint64_t runtimeOffset, bool isLazyPointerRebase, bool& stop) {
+            uint64_t* loc = (uint64_t*)((uint8_t*)ma + runtimeOffset);
+            uint64_t locValue = is64 ? *loc : *((uint32_t*)loc);
+            FixupTarget rebaseTarget;
+            PointerMetaData pmd;
+            if ( is64 )
+                pmd.high8  = (locValue >> 56);
+            rebaseTarget.kind               = FixupTarget::Kind::rebase;
+            rebaseTarget.foundInImage       = *this;
+            rebaseTarget.offsetInImage      = (locValue & 0x00FFFFFFFFFFFFFFULL) - prefLoadAddr;
+            rebaseTarget.isLazyBindRebase   = isLazyPointerRebase;
+            fixup(runtimeOffset, pmd, rebaseTarget, stop);
+        });
+        if ( diag.hasError() )
+            return;
+
+        // process all bind opcodes
+        this->forEachBind(diag, fixup, patcher);
+    }
+    if ( diag.hasError() )
+        return;
+
+    // main executable may define operator new/delete symbols that overrides weak-defs but have no fixups
+    if ( ma->isMainExecutable() && ma->hasWeakDefs() ) {
+        _set->wmo_findExtraSymbolFrom(this,  patcher);
+    }
+}
+
+
+bool MachOAnalyzerSet::wmo_findSymbolFrom(const WrappedMachO* fromWmo, Diagnostics& diag, int libOrdinal, const char* symbolName, bool weakImport,
+                                           bool lazyBind, uint64_t addend, CachePatchHandler patcher, FixupTarget& target) const
+{
+    target.libOrdinal = libOrdinal;
+    if ( libOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) {
+        __block bool found = false;
+        this->mas_forEachImage(^(const WrappedMachO& anImage, bool hidden, bool& stop) {
+            // when an image is hidden (RTLD_LOCAL) it can still look up symbols in itself
+            if ( hidden && (fromWmo->_mh != anImage._mh) )
+                return;
+            if ( anImage.findSymbolIn(diag, symbolName, addend, target) ) {
+                stop = true;
+                found = true;
+            }
+        });
+        if ( found )
+            return true;
+        // see if missing symbol resolver can find something
+        if ( fromWmo->missingSymbolResolver(weakImport, lazyBind, symbolName, "flat namespace", fromWmo->path(), target) )
+            return true;
+        // fill out target info about missing symbol
+        target.kind                 = FixupTarget::Kind::bindMissingSymbol;
+        target.requestedSymbolName  = symbolName;
+        target.foundSymbolName      = nullptr;
+        target.foundInImage         = WrappedMachO();   // no image it should be in
+
+        diag.error("symbol '%s' not found, expected in flat namespace by '%s'", symbolName, fromWmo->path());
+        return false;
+    }
+    else if ( libOrdinal == BIND_SPECIAL_DYLIB_WEAK_LOOKUP ) {
+        if ( this->mas_fromImageWeakDefLookup(*fromWmo, symbolName, addend, patcher, target) ) {
+            target.weakCoalesced = true;
+            return true;
+        }
+
+        if ( !fromWmo->_mh->hasChainedFixups() ) {
+            // support old binaries where symbols have been stripped and have weak_bind to itself
+            target.skippableWeakDef = true;
+            return true;
+        }
+        // see if missing symbol resolver can find something
+        if ( fromWmo->missingSymbolResolver(weakImport, lazyBind, symbolName, "flat namespace", fromWmo->path(), target) )
+            return true;
+        // fill out target info about missing symbol
+        target.kind                 = FixupTarget::Kind::bindMissingSymbol;
+        target.requestedSymbolName  = symbolName;
+        target.foundSymbolName      = nullptr;
+        target.foundInImage         = WrappedMachO();   // no image it should be in
+
+        diag.error("symbol '%s' not found, expected to be weak-def coalesced in '%s'", symbolName, fromWmo->path());
+        return false;
+    }
+    else {
+        int depIndex = libOrdinal - 1;
+        bool missingWeakDylib = false;
+        WrappedMachO depHelper;
+        const WrappedMachO* targetImage = nullptr;
+        if ( libOrdinal == BIND_SPECIAL_DYLIB_SELF ) {
+            targetImage = fromWmo;
+        }
+        else if ( libOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) {
+            this->mas_mainExecutable(depHelper);
+            targetImage = &depHelper;
+        }
+        else if ( fromWmo->dependent(depIndex, depHelper, missingWeakDylib) ) {
+            targetImage = &depHelper;
+        }
+        else {
+            diag.error("unknown library ordinal %d in %s", libOrdinal, fromWmo->path());
+            return false;
+        }
+        // use two-level namespace target image
+        if ( !missingWeakDylib && targetImage->findSymbolIn(diag, symbolName, addend, target) )
+            return true;
+
+        // see if missing symbol resolver can find something
+        const char* expectedInPath = missingWeakDylib ? "missing dylib" : targetImage->path();
+        if ( fromWmo->missingSymbolResolver(weakImport, lazyBind, symbolName, expectedInPath, fromWmo->path(), target) )
+            return true;
+
+        // fill out target info about missing symbol
+        target.kind                 = FixupTarget::Kind::bindMissingSymbol;
+        target.requestedSymbolName  = symbolName;
+        target.foundSymbolName      = nullptr;
+        target.foundInImage         = *targetImage;    // no image it is expected to be in
+
+        // symbol not found and not weak or lazy so error out
+        diag.error("symbol '%s' not found, expected in '%s', needed by '%s'", symbolName, expectedInPath, fromWmo->path());
+        return false;
+    }
+    return false;
+}
+
+// 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* const 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 MachOAnalyzerSet::wmo_findExtraSymbolFrom(const WrappedMachO* fromWmo, CachePatchHandler patcher) const
+{
+    for (const char* weakSymbolName : sTreatAsWeak) {
+        Diagnostics exportDiag;
+        FixupTarget dummyTarget;
+        // pretend main executable does have a use of this operator new/delete and look up the impl
+        // this has the side effect of adding a cache patch if there is an impl outside the cache
+        wmo_findSymbolFrom(fromWmo, exportDiag, -3, weakSymbolName, true, false, 0, patcher, dummyTarget);
+    }
+
+}
+
+bool MachOAnalyzerSet::WrappedMachO::findSymbolIn(Diagnostics& diag, const char* symbolName, uint64_t addend, FixupTarget& target) const
+{
+    const MachOAnalyzer* ma  = _mh;
+
+    // if exports trie location not computed yet, do it now
+    ExportsTrie exportsTrie = this->getExportsTrie();
+
+    target.foundSymbolName = nullptr;
+    if ( exportsTrie.start ) {
+        if ( const uint8_t* node = this->_mh->trieWalk(diag, exportsTrie.start, exportsTrie.end, symbolName)) {
+            const uint8_t* p = node;
+            const uint64_t flags = this->_mh->read_uleb128(diag, p, exportsTrie.end);
+            if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
+                // re-export from another dylib, lookup there
+                const uint64_t libOrdinal = ma->read_uleb128(diag, p, exportsTrie.end);
+                const char* importedName = (char*)p;
+                if ( importedName[0] == '\0' )
+                    importedName = symbolName;
+                const int depIndex = (int)(libOrdinal - 1);
+                bool missingWeakDylib;
+                WrappedMachO depHelper;
+                if ( this->dependent(depIndex, depHelper, missingWeakDylib) && !missingWeakDylib ) {
+                    if ( depHelper.findSymbolIn(diag, importedName, addend, target) ) {
+                        target.requestedSymbolName = symbolName;
+                        return true;
+                    }
+                }
+                if ( !missingWeakDylib )
+                    diag.error("re-export ordinal %lld out of range for %s", libOrdinal, symbolName);
+                return false;
+            }
+            target.kind                 = FixupTarget::Kind::bindToImage;
+            target.requestedSymbolName  = symbolName;
+            target.foundSymbolName      = symbolName;
+            target.foundInImage         = *this;
+            target.isWeakDef            = false;
+            target.addend               = addend;
+            uint64_t trieValue = ma->read_uleb128(diag, p, exportsTrie.end);
+            switch ( flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) {
+                case EXPORT_SYMBOL_FLAGS_KIND_REGULAR:
+                    target.offsetInImage = trieValue + addend;
+                    if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) {
+                        // for now, just return address of resolver helper stub
+                        // FIXME handle running resolver
+                        (void)this->_mh->read_uleb128(diag, p, exportsTrie.end);
+                    }
+                    if ( flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION )
+                        target.isWeakDef = true;
+                    break;
+                case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL:
+                    // no type checking that client expected TLV yet
+                    target.offsetInImage = trieValue;
+                    break;
+                case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE:
+                    target.kind          = FixupTarget::Kind::bindAbsolute;
+                    target.offsetInImage = trieValue + addend;
+                    break;
+                default:
+                    diag.error("unsupported exported symbol kind. flags=%llu at node offset=0x%0lX", flags, (long)(node-exportsTrie.start));
+                    return false;
+            }
+            return true;
+        }
+    }
+    else {
+        ma->forEachGlobalSymbol(diag, ^(const char* n_name, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop) {
+            if ( strcmp(n_name, symbolName) == 0 ) {
+                target.kind                 = FixupTarget::Kind::bindToImage;
+                target.foundSymbolName      = symbolName;
+                target.requestedSymbolName  = symbolName;
+                target.foundInImage         = *this;
+                target.offsetInImage        = n_value - ma->preferredLoadAddress() + addend;
+                target.addend               = addend;
+                stop = true;
+            }
+        });
+        if ( target.foundSymbolName )
+            return true;
+    }
+
+    // symbol not exported from this image
+    // if this is a dylib and has re-exported dylibs, search those too
+    if ( (ma->filetype == MH_DYLIB) && ((ma->flags & MH_NO_REEXPORTED_DYLIBS) == 0) ) {
+        __block unsigned depIndex = 0;
+        ma->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+            if ( isReExport ) {
+                bool missingWeakDylib;
+                WrappedMachO child;
+                 if ( this->dependent(depIndex, child, missingWeakDylib) && !missingWeakDylib ) {
+                    if ( child.findSymbolIn(diag, symbolName, addend, target) )
+                        stop = true;
+                }
+            }
+            ++depIndex;
+        });
+    }
+
+    return (target.foundSymbolName != nullptr);
+}
+
+
+MachOAnalyzerSet::ExportsTrie MachOAnalyzerSet::wmo_getExportsTrie(const WrappedMachO* wmo) const
+{
+    const uint8_t* start = nullptr;
+    const uint8_t* end   = nullptr;
+    uint32_t runtimeOffset;
+    uint32_t size;
+    if ( wmo->_mh->hasExportTrie(runtimeOffset, size) ) {
+        start = (uint8_t*)wmo->_mh + runtimeOffset;
+        end   = start + size;
+    }
+    return { start, end };
+}
+
+
+// scan all weak-def images in load order
+// return first non-weak defintion found
+// otherwise first weak definition found
+bool MachOAnalyzerSet::mas_fromImageWeakDefLookup(const WrappedMachO& fromWmo, const char* symbolName, uint64_t addend, CachePatchHandler patcher, FixupTarget& target) const
+{
+    // walk all images in load order, looking only at ones with weak-defs
+    const DyldSharedCache* dyldCache = (DyldSharedCache*)mas_dyldCache();
+    __block bool foundImpl = false;
+    this->mas_forEachImage(^(const WrappedMachO& anImage, bool hidden, bool& stop) {
+        if ( !anImage._mh->hasWeakDefs() )
+            return;
+        // when an image is hidden (RTLD_LOCAL) it can still look up symbols in itself
+        if ( hidden && (fromWmo._mh != anImage._mh) )
+            return;
+        FixupTarget tempTarget;
+        Diagnostics diag;
+        if ( anImage.findSymbolIn(diag, symbolName, addend, tempTarget) ) {
+            // ignore symbol re-exports, we will find the real definition later in forEachImage()
+            if ( anImage._mh != tempTarget.foundInImage._mh )
+                return;
+            if ( foundImpl && anImage._mh->inDyldCache() && (anImage._mh != target.foundInImage._mh) ) {
+                // we have already found the target, but now we see something in the dyld cache
+                // that also implements this symbol, so we need to change all caches uses of that
+                // to use the found one instead
+                uint32_t cachedDylibIndex = 0;
+                if ( dyldCache->findMachHeaderImageIndex(anImage._mh, cachedDylibIndex) ) {
+                    uintptr_t exportCacheOffset = (uint8_t*)tempTarget.foundInImage._mh + tempTarget.offsetInImage - (uint8_t*)dyldCache;
+                    patcher(cachedDylibIndex, (uint32_t)exportCacheOffset, target);
+                }
+            }
+            if ( !foundImpl ) {
+                // this is first found, so copy this to result
+                target        = tempTarget;
+                foundImpl     = true;
+            }
+            else if ( target.isWeakDef && !tempTarget.isWeakDef ) {
+                // we found a non-weak impl later on, switch to it
+                target        = tempTarget;
+            }
+        }
+    });
+    return foundImpl;
+}
+
+
+} // dyld3
+
+
diff --git a/dyld3/MachOAnalyzerSet.h b/dyld3/MachOAnalyzerSet.h
new file mode 100644 (file)
index 0000000..27f4c3d
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2019 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, 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 MachOAnalyzerSet_h
+#define MachOAnalyzerSet_h
+
+
+#include "MachOAnalyzer.h"
+#include "Array.h"
+
+
+namespace dyld3 {
+
+//
+// MachOAnalyzerSet is an abstraction to deal with sets of mach-o files. For instance,
+// if a mach-o file A binds to a symbol in mach-o file B, the MachOAnalyzerSet lets you
+// evaulate the bind such that you know where in B, the bind pointer in A needs to point.
+//
+// The goal of MachOAnalyzerSet is to be the one place for code that handles mach-o
+// file interactions, such as two-level namespace binding, weak-def coalescing, and
+// dyld cache patching.
+//
+// Conceptually, MachOAnalyzerSet is an ordered list of mach-o files. Each file is modeled
+// as an WrappedMachO object.  This is a lightweight POD struct of three pointers that
+// can be copied around.  A WrappedMachO consists of the MachOAnalyzer* that is represents,
+// a pointer to the MachOAnalyzerSet it is in, and an abstract "other" pointer that the
+// concrete implementation of MachOAnalyzerSet defines.  All uses of mach-o files in
+// the MachOAnalyzerSet method uses WrappedMachO types.
+//
+//      // This is the key method on WrappedMachO. It is called during closure building to
+//      // compile down the fixups, as well as at runtime in dyld3 minimal closure mode
+//      // to parse LINKEDIT and rebase/bind pointers.
+//      void   forEachFixup(Diagnostics& diag, FixUpHandler, CachePatchHandler) const;
+//
+//
+// It would have been nice to have virtual methods on WrappedMachO, but C++ won't allow
+// objects with vtables to be copied.  So instead the methods on WrappedMachO simply
+// forward to virtual methods in the owning MachOAnalyzerSet object.  Therefore, there
+// are two kinds of methods on MachOAnalyzerSet: methods for a WrappedMachO start with wmo_,
+// whereas methods for the whole set start with mas_.
+//
+//      // Walk all images in the set in order.  "hidden" means the image was loaded with RTLD_LOCAL
+//      void    mas_forEachImage(void (^handler)(const WrappedMachO& wmo, bool hidden, bool& stop)) const = 0;
+//
+//      // fills in mainWmo with the WrappedMachO for the main executable in the set
+//      void    mas_mainExecutable(WrappedMachO& mainWmo) const = 0;
+//
+//      // returns a pointer to the start of the dyld cache used the mach-o files in the set
+//      void*   mas_dyldCache() const = 0;
+//
+//      // For weak-def coalescing. The file fromWmo needs to bind to symbolName.  All files with weak-defs should be searched.
+//       // As a side effect of doing this binding, it may find that the symbol is bound overrides something in the dyld cache.
+//      // In that case, the CachePatchHandler function is called with info about how to patch the dyld cache.
+//      // This function has a default implementation.  Only the dyld cache builder overrides this, because the set is all the
+//      // dylibs in the dyld cache, and coalescing should only look at files actually linked.
+//      bool    mas_fromImageWeakDefLookup(const WrappedMachO& fromWmo, const char* symbolName, uint64_t addend,
+//                                         CachePatchHandler patcher, FixupTarget& target) const;
+//
+//
+//      // For a given WrappedMachO (fromWmo), find the nth dependent dylib.  If depIndex is out of range, return false.
+//      // If child is weak-linked dylib that could not be loaded, set missingWeakDylib to true and return true.
+//      // Otherwise fill in childWmo and return true
+//      bool        wmo_dependent(const WrappedMachO* fromWmo, uint32_t depIndex, WrappedMachO& childWmo, bool& missingWeakDylib) const = 0;
+//
+//      // Returns the path to the specified WrappedMachO
+//      const char* wmo_path(const WrappedMachO* wmo) const = 0;
+//
+//      // Called if a symbol cannot be found.  If false is returned, then the symbol binding code will return an error.
+//      // If true is returned, then "target" must be set.  It may be set to "NULL" as absolute/0.
+//      bool        wmo_missingSymbolResolver(const WrappedMachO* fromWmo, bool weakImport, bool lazyBind, const char* symbolName,
+//                                            const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const = 0;
+//
+//      // Returns the exports trie for the given binary. There is a default implementation which walks the load commands.
+//      // This should only be overriden if the MachOAnalyzerSet caches the export trie location.
+//      ExportsTrie wmo_getExportsTrie(const WrappedMachO* wmo) const;
+//
+//      // This handles special symbols like C++ operator new which can exist in the main executable as non-weak but
+//      // coalesce with other weak implementations. It does not need to be overridden.
+//      void        wmo_findExtraSymbolFrom(const WrappedMachO* fromWmo, CachePatchHandler ph) const;
+//
+//      // This is core logic for two-level namespace symbol look ups.  It does not need to be overridden.
+//      bool        wmo_findSymbolFrom(const WrappedMachO* fromWmo, Diagnostics& diag, int libOrdinal, const char* symbolName,
+//                                     bool weakImport, bool lazyBind, uint64_t addend, CachePatchHandler ph, FixupTarget& target) const;
+//
+//
+
+struct VIS_HIDDEN MachOAnalyzerSet
+{
+public:
+    struct FixupTarget;
+
+    struct ExportsTrie { const uint8_t* start; const uint8_t* end; } ;
+
+    // Extra info needed when setting an actual pointer value at runtime
+    struct PointerMetaData
+    {
+                    PointerMetaData();
+                    PointerMetaData(const MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, uint16_t pointer_format);
+
+        uint32_t    diversity         : 16,
+                    high8             :  8,
+                    authenticated     :  1,
+                    key               :  2,
+                    usesAddrDiversity :  1;
+    };
+
+    typedef void (^FixUpHandler)(uint64_t fixupLocRuntimeOffset, PointerMetaData pmd, const FixupTarget& target, bool& stop);
+    typedef void (^CachePatchHandler)(uint32_t cachedDylibIndex, uint32_t exportCacheOffset, const FixupTarget& target);
+
+    struct WrappedMachO
+    {
+        const MachOAnalyzer*           _mh;
+        const MachOAnalyzerSet*        _set;
+        void*                          _other;
+
+                        WrappedMachO() : _mh(nullptr), _set((nullptr)), _other((nullptr)) { }
+                        WrappedMachO(const MachOAnalyzer* ma, const MachOAnalyzerSet* mas, void* o) : _mh(ma), _set(mas), _other(o) { }
+                        ~WrappedMachO() {} 
+
+        // Used by: dyld cache building, dyld3s fixup applying, app closure building traditional format, dyldinfo tool
+        void            forEachFixup(Diagnostics& diag, FixUpHandler, CachePatchHandler) const;
+
+        // convenience functions
+        bool            dependent(uint32_t depIndex, WrappedMachO& childObj, bool& missingWeakDylib) const { return _set->wmo_dependent(this, depIndex, childObj, missingWeakDylib); }
+        const char*     path() const { return (_set ? _set->wmo_path(this) : nullptr); }
+        ExportsTrie     getExportsTrie() const { return _set->wmo_getExportsTrie(this); }
+        bool            findSymbolFrom(Diagnostics& diag, int libOrdinal, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, CachePatchHandler ph, FixupTarget& target) const
+                                        { return _set->wmo_findSymbolFrom(this, diag, libOrdinal, symbolName, weakImport, lazyBind, addend, ph, target); }
+        bool            missingSymbolResolver(bool weakImport, bool lazyBind, const char* symbolName, const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const
+                                        { return _set->wmo_missingSymbolResolver(this, weakImport, lazyBind, symbolName, expectedInDylibPath, clientPath, target); }
+
+        bool            findSymbolIn(Diagnostics& diag, const char* symbolName, uint64_t addend, FixupTarget& target) const;
+
+    private:
+        void            forEachBind(Diagnostics& diag, FixUpHandler, CachePatchHandler) const;
+    };
+
+
+    struct FixupTarget
+    {
+        enum class Kind { rebase, bindToImage, bindAbsolute, bindMissingSymbol };
+        WrappedMachO            foundInImage;
+        uint64_t                offsetInImage           = 0;     // includes addend
+        const char*             requestedSymbolName     = nullptr;
+        const char*             foundSymbolName         = nullptr;
+        uint64_t                addend                  = 0;     // already added into offsetInImage
+        int                     libOrdinal              = 0;
+        Kind                    kind                    = Kind::rebase;
+        bool                    isLazyBindRebase        = false; // target is stub helper in same image
+        bool                    isWeakDef               = false; // target symbol is a weak-def
+        bool                    weakCoalesced           = false; // target found searching all images
+        bool                    weakBoundSameImage      = false; // first weak-def was in same image as use
+        bool                    skippableWeakDef        = false; // old binary that stripped symbol, so def will never be found
+    };
+
+    virtual void    mas_forEachImage(void (^handler)(const WrappedMachO& wmo, bool hidden, bool& stop)) const = 0;
+    virtual bool    mas_fromImageWeakDefLookup(const WrappedMachO& fromWmo, const char* symbolName, uint64_t addend, CachePatchHandler patcher, FixupTarget& target) const;
+    virtual void    mas_mainExecutable(WrappedMachO& mainWmo) const = 0;
+    virtual void*   mas_dyldCache() const = 0;
+
+
+protected:
+    friend WrappedMachO;
+
+    virtual bool            wmo_dependent(const WrappedMachO* fromWmo, uint32_t depIndex, WrappedMachO& childWmo, bool& missingWeakDylib) const = 0;
+    virtual const char*     wmo_path(const WrappedMachO* wmo) const = 0;
+    virtual ExportsTrie     wmo_getExportsTrie(const WrappedMachO* wmo) const;
+    virtual bool            wmo_findSymbolFrom(const WrappedMachO* fromWmo, Diagnostics& diag, int libOrdinal, const char* symbolName, bool weakImport,
+                                               bool lazyBind, uint64_t addend, CachePatchHandler ph, FixupTarget& target) const;
+    virtual bool            wmo_missingSymbolResolver(const WrappedMachO* fromWmo, bool weakImport, bool lazyBind, const char* symbolName,
+                                                      const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const = 0;
+    virtual void            wmo_findExtraSymbolFrom(const WrappedMachO* fromWmo, CachePatchHandler ph) const;
+};
+
+
+
+} // namespace dyld3
+
+#endif /* MachOAnalyzerSet_h */
diff --git a/dyld3/MachOAppCache.cpp b/dyld3/MachOAppCache.cpp
new file mode 100644 (file)
index 0000000..b8130ad
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+* 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 "MachOAppCache.h"
+
+#include <list>
+
+#include <CoreFoundation/CFArray.h>
+#include <CoreFoundation/CFPropertyList.h>
+#include <CoreFoundation/CFString.h>
+
+#ifndef LC_FILESET_ENTRY
+#define LC_FILESET_ENTRY      (0x35 | LC_REQ_DYLD) /* used with fileset_entry_command */
+struct fileset_entry_command {
+    uint32_t        cmd;        /* LC_FILESET_ENTRY */
+    uint32_t        cmdsize;    /* includes id string */
+    uint64_t        vmaddr;     /* memory address of the dylib */
+    uint64_t        fileoff;    /* file offset of the dylib */
+    union lc_str    entry_id;   /* contained entry id */
+    uint32_t        reserved;   /* entry_id is 32-bits long, so this is the reserved padding */
+};
+#endif
+
+namespace dyld3 {
+
+void MachOAppCache::forEachDylib(Diagnostics& diag, void (^callback)(const MachOAnalyzer* ma, const char* name, bool& stop)) const {
+    const intptr_t slide = getSlide();
+    forEachLoadCommand(diag, ^(const load_command *cmd, bool &stop) {
+        if (cmd->cmd == LC_FILESET_ENTRY) {
+            const fileset_entry_command* app_cache_cmd = (const fileset_entry_command*)cmd;
+            const char* name = (char*)app_cache_cmd + app_cache_cmd->entry_id.offset;
+            callback((const MachOAnalyzer*)(app_cache_cmd->vmaddr + slide), name, stop);
+            return;
+        }
+    });
+}
+
+void MachOAppCache::forEachPrelinkInfoLibrary(Diagnostics& diags,
+                                              void (^callback)(const char* bundleName, const char* relativePath,
+                                                               const std::vector<const char*>& deps)) const  {
+
+     __block std::list<std::string> nonASCIIStrings;
+     auto getString = ^(Diagnostics& diags, CFStringRef symbolNameRef) {
+         const char* symbolName = CFStringGetCStringPtr(symbolNameRef, kCFStringEncodingUTF8);
+         if ( symbolName != nullptr )
+             return symbolName;
+
+         CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(symbolNameRef), kCFStringEncodingUTF8);
+         char buffer[len + 1];
+         if ( !CFStringGetCString(symbolNameRef, buffer, len, kCFStringEncodingUTF8) ) {
+             diags.error("Could not convert string to ASCII");
+             return (const char*)nullptr;
+         }
+         buffer[len] = '\0';
+         nonASCIIStrings.push_back(buffer);
+         return nonASCIIStrings.back().c_str();
+     };
+
+    const uint8_t* prelinkInfoBuffer = nullptr;
+    uint64_t prelinkInfoBufferSize = 0;
+    prelinkInfoBuffer = (const uint8_t*)findSectionContent("__PRELINK_INFO", "__info", prelinkInfoBufferSize);
+    if ( prelinkInfoBuffer == nullptr )
+        return;
+
+    CFReadStreamRef readStreamRef = CFReadStreamCreateWithBytesNoCopy(kCFAllocatorDefault, prelinkInfoBuffer, prelinkInfoBufferSize, kCFAllocatorNull);
+    if ( !CFReadStreamOpen(readStreamRef) ) {
+        fprintf(stderr, "Could not open plist stream\n");
+        exit(1);
+    }
+    CFErrorRef errorRef = nullptr;
+    CFPropertyListRef plistRef = CFPropertyListCreateWithStream(kCFAllocatorDefault, readStreamRef, prelinkInfoBufferSize, kCFPropertyListImmutable, nullptr, &errorRef);
+    if ( errorRef != nullptr ) {
+        CFStringRef stringRef = CFErrorCopyFailureReason(errorRef);
+        fprintf(stderr, "Could not read plist because: %s\n", CFStringGetCStringPtr(stringRef, kCFStringEncodingASCII));
+        CFRelease(stringRef);
+        exit(1);
+    }
+    assert(CFGetTypeID(plistRef) == CFDictionaryGetTypeID());
+
+    // Get the "_PrelinkInfoDictionary" array
+    CFArrayRef prelinkInfoDictionaryArrayRef = (CFArrayRef)CFDictionaryGetValue((CFDictionaryRef)plistRef, CFSTR("_PrelinkInfoDictionary"));
+    assert(CFGetTypeID(prelinkInfoDictionaryArrayRef) == CFArrayGetTypeID());
+
+    for (CFIndex i = 0; i != CFArrayGetCount(prelinkInfoDictionaryArrayRef); ++i) {
+        CFDictionaryRef kextInfoDictionary = (CFDictionaryRef)CFArrayGetValueAtIndex(prelinkInfoDictionaryArrayRef, i);
+        assert(CFGetTypeID(kextInfoDictionary) == CFDictionaryGetTypeID());
+
+        CFStringRef bundleIdentifierStringRef = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)kextInfoDictionary, CFSTR("CFBundleIdentifier"));
+        assert(CFGetTypeID(bundleIdentifierStringRef) == CFStringGetTypeID());
+
+        const char* bundleID = getString(diags, bundleIdentifierStringRef);
+        if ( bundleID == nullptr )
+            return;
+
+        const char* relativePath = nullptr;
+        CFStringRef relativePathStringRef = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)kextInfoDictionary, CFSTR("_PrelinkExecutableRelativePath"));
+        if ( relativePathStringRef != nullptr ) {
+            assert(CFGetTypeID(relativePathStringRef) == CFStringGetTypeID());
+            relativePath = getString(diags, relativePathStringRef);
+            if ( relativePath == nullptr )
+                return;
+        }
+
+        std::vector<const char*> dependencies;
+
+        CFDictionaryRef bundleLibrariesDictionaryRef = (CFDictionaryRef)CFDictionaryGetValue((CFDictionaryRef)kextInfoDictionary, CFSTR("OSBundleLibraries"));
+        if (bundleLibrariesDictionaryRef != nullptr) {
+            // Add the libraries to the dependencies
+            // If we didn't have bundle libraries then a placeholder was added
+            assert(CFGetTypeID(bundleLibrariesDictionaryRef) == CFDictionaryGetTypeID());
+
+            struct ApplyContext {
+                Diagnostics*                diagnostics;
+                std::vector<const char*>*   dependencies                                    = nullptr;
+                const char* (^getString)(Diagnostics& diags, CFStringRef symbolNameRef)     = nullptr;
+            };
+
+            CFDictionaryApplierFunction callback = [](const void *key, const void *value, void *context) {
+                CFStringRef keyStringRef = (CFStringRef)key;
+                assert(CFGetTypeID(keyStringRef) == CFStringGetTypeID());
+
+                ApplyContext* applyContext = (ApplyContext*)context;
+                const char* depString = applyContext->getString(*applyContext->diagnostics, keyStringRef);
+                if ( !depString )
+                    return;
+
+                applyContext->dependencies->push_back(depString);
+            };
+
+            ApplyContext applyContext = { &diags, &dependencies, getString };
+            CFDictionaryApplyFunction(bundleLibrariesDictionaryRef, callback, &applyContext);
+
+            if ( diags.hasError() )
+                return;
+        }
+        callback(bundleID, relativePath, dependencies);
+    }
+
+    CFRelease(plistRef);
+    CFRelease(readStreamRef);
+}
+
+} // namespace dyld3
diff --git a/dyld3/MachOAppCache.h b/dyld3/MachOAppCache.h
new file mode 100644 (file)
index 0000000..9d0f2ce
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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 MachOAppCache_h
+#define MachOAppCache_h
+
+#include "MachOAnalyzer.h"
+
+namespace dyld3 {
+
+struct MachOAppCache : public MachOAnalyzer {
+    // Taken from kmod.h
+    enum {
+        kmodMaxName = 64
+    };
+    #pragma pack(push, 4)
+    struct KModInfo64_v1 {
+        uint64_t            next_addr;
+        int32_t             info_version;
+        uint32_t            id;
+        uint8_t             name[kmodMaxName];
+        uint8_t             version[kmodMaxName];
+        int32_t             reference_count;
+        uint64_t            reference_list_addr;
+        uint64_t            address;
+        uint64_t            size;
+        uint64_t            hdr_size;
+        uint64_t            start_addr;
+        uint64_t            stop_addr;
+    };
+    #pragma pack(pop)
+
+    void forEachDylib(Diagnostics& diag, void (^callback)(const MachOAnalyzer* ma, const char* name, bool& stop)) const;
+
+    // Walk the __PRELINK_INFO dictionary and return each bundle and its libraries
+    void forEachPrelinkInfoLibrary(Diagnostics& diags,
+                                   void (^callback)(const char* bundleName, const char* relativePath,
+                                                    const std::vector<const char*>& deps)) const;
+};
+
+} // namespace dyld3
+
+#endif /* MachOAppCache_h */
index 7c26440518763475e9619bd849c07f5bd1a40613..026bfd149eb0db4925cef7fa3031d3cd219bdec8 100644 (file)
 #include <assert.h>
 #include <string.h>
 #include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <unistd.h>
 #include <TargetConditionals.h>
 #include <mach/host_info.h>
 #include <mach/mach.h>
 #include "MachOFile.h"
 #include "SupportedArchs.h"
 
+#if BUILDING_DYLD || BUILDING_LIBDYLD
+    // define away restrict until rdar://60166935 is fixed
+    #define restrict
+    #include <subsystem.h>
+#endif
 
 namespace dyld3 {
 
+////////////////////////////  posix wrappers ////////////////////////////////////////
+
+// <rdar://problem/10111032> wrap calls to stat() with check for EAGAIN
+int stat(const char* path, struct stat* buf)
+{
+    int result;
+    do {
+#if BUILDING_DYLD || BUILDING_LIBDYLD
+        result = ::stat_with_subsystem(path, buf);
+#else
+        result = ::stat(path, buf);
+#endif
+    } while ((result == -1) && ((errno == EAGAIN) || (errno == EINTR)));
+
+    return result;
+}
+
+// <rdar://problem/13805025> dyld should retry open() if it gets an EGAIN
+int open(const char* path, int flag, int other)
+{
+    int result;
+    do {
+#if BUILDING_DYLD || BUILDING_LIBDYLD
+        if (flag & O_CREAT)
+            result = ::open(path, flag, other);
+        else
+            result = ::open_with_subsystem(path, flag);
+#else
+        result = ::open(path, flag, other);
+#endif
+    } while ((result == -1) && ((errno == EAGAIN) || (errno == EINTR)));
+
+    return result;
+}
+
+
 ////////////////////////////  FatFile ////////////////////////////////////////
 
 const FatFile* FatFile::isFatFile(const void* fileStart)
@@ -130,7 +176,8 @@ void FatFile::forEachSlice(Diagnostics& diag, uint64_t fileLen, void (^callback)
     }
 }
 
-bool FatFile::isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const GradedArchs& archs, uint64_t& sliceOffset, uint64_t& sliceLen, bool& missingSlice) const
+bool FatFile::isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const GradedArchs& archs, bool isOSBinary,
+                                 uint64_t& sliceOffset, uint64_t& sliceLen, bool& missingSlice) const
 {
     missingSlice = false;
     if ( (this->magic != OSSwapBigToHostInt32(FAT_MAGIC)) && (this->magic != OSSwapBigToHostInt32(FAT_MAGIC_64)) )
@@ -138,7 +185,7 @@ bool FatFile::isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const Grad
 
     __block int bestGrade = 0;
     forEachSlice(diag, fileLen, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop) {
-        if (int sliceGrade = archs.grade(sliceCpuType, sliceCpuSubType)) {
+        if (int sliceGrade = archs.grade(sliceCpuType, sliceCpuSubType, isOSBinary)) {
             if ( sliceGrade > bestGrade ) {
                 sliceOffset = (char*)sliceStart - (char*)this;
                 sliceLen    = sliceSize;
@@ -158,26 +205,47 @@ bool FatFile::isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const Grad
 
 ////////////////////////////  GradedArchs ////////////////////////////////////////
 
-const GradedArchs GradedArchs::i386          = { {{CPU_TYPE_I386,     CPU_SUBTYPE_I386_ALL,    1}} };
-const GradedArchs GradedArchs::x86_64        = { {{CPU_TYPE_X86_64,   CPU_SUBTYPE_X86_64_ALL,  1}} };
-const GradedArchs GradedArchs::x86_64h       = { {{CPU_TYPE_X86_64,   CPU_SUBTYPE_X86_64_H,    2}, {CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL, 1}},     };
-const GradedArchs GradedArchs::arm64         = { {{CPU_TYPE_ARM64,    CPU_SUBTYPE_ARM64_ALL,   1}} };
+
+#define GRADE_i386        CPU_TYPE_I386,       CPU_SUBTYPE_I386_ALL,    false
+#define GRADE_x86_64      CPU_TYPE_X86_64,     CPU_SUBTYPE_X86_64_ALL,  false
+#define GRADE_x86_64h     CPU_TYPE_X86_64,     CPU_SUBTYPE_X86_64_H,    false
+#define GRADE_armv7       CPU_TYPE_ARM,        CPU_SUBTYPE_ARM64_ALL,   false
+#define GRADE_armv7s      CPU_TYPE_ARM,        CPU_SUBTYPE_ARM_V7,      false
+#define GRADE_armv7k      CPU_TYPE_ARM,        CPU_SUBTYPE_ARM_V7K,     false
+#define GRADE_arm64       CPU_TYPE_ARM64,      CPU_SUBTYPE_ARM64_ALL,   false
+#define GRADE_arm64e      CPU_TYPE_ARM64,      CPU_SUBTYPE_ARM64E,      false
+#define GRADE_arm64e_pb   CPU_TYPE_ARM64,      CPU_SUBTYPE_ARM64E,      true
+#define GRADE_arm64_32    CPU_TYPE_ARM64_32,   CPU_SUBTYPE_ARM64_32_V8, false
+
+const GradedArchs GradedArchs::i386              = { {{GRADE_i386,    1}} };
+const GradedArchs GradedArchs::x86_64            = { {{GRADE_x86_64,  1}} };
+const GradedArchs GradedArchs::x86_64h           = { {{GRADE_x86_64h, 2}, {GRADE_x86_64, 1}} };
+const GradedArchs GradedArchs::arm64             = { {{GRADE_arm64,   1}} };
 #if SUPPORT_ARCH_arm64e
-const GradedArchs GradedArchs::arm64e_compat = { {{CPU_TYPE_ARM64,    CPU_SUBTYPE_ARM64E,      2}, {CPU_TYPE_ARM64,  CPU_SUBTYPE_ARM64_ALL, 1}} };
-const GradedArchs GradedArchs::arm64e        = { {{CPU_TYPE_ARM64,    CPU_SUBTYPE_ARM64E,      1}} };
+const GradedArchs GradedArchs::arm64e_keysoff    = { {{GRADE_arm64e,    2}, {GRADE_arm64, 1}} };
+const GradedArchs GradedArchs::arm64e_keysoff_pb = { {{GRADE_arm64e_pb, 2}, {GRADE_arm64, 1}} };
+const GradedArchs GradedArchs::arm64e            = { {{GRADE_arm64e,    1}} };
+const GradedArchs GradedArchs::arm64e_pb         = { {{GRADE_arm64e_pb, 1}} };
 #endif
-const GradedArchs GradedArchs::armv7k        = { {{CPU_TYPE_ARM,      CPU_SUBTYPE_ARM_V7K,     1}} };
-const GradedArchs GradedArchs::armv7         = { {{CPU_TYPE_ARM,      CPU_SUBTYPE_ARM_V7,      1}} };
-const GradedArchs GradedArchs::armv7s        = { {{CPU_TYPE_ARM,      CPU_SUBTYPE_ARM_V7S,     2}, {CPU_TYPE_ARM,    CPU_SUBTYPE_ARM_V7, 1}} };
+const GradedArchs GradedArchs::armv7             = { {{GRADE_armv7,   1}} };
+const GradedArchs GradedArchs::armv7s            = { {{GRADE_armv7s,  2}, {GRADE_armv7, 1}} };
+const GradedArchs GradedArchs::armv7k            = { {{GRADE_armv7k,  1}} };
 #if SUPPORT_ARCH_arm64_32
-const GradedArchs GradedArchs::arm64_32      = { {{CPU_TYPE_ARM64_32, CPU_SUBTYPE_ARM64_32_V8, 1}} };
+const GradedArchs GradedArchs::arm64_32          = { {{GRADE_arm64_32, 1}} };
 #endif
 
-int GradedArchs::grade(uint32_t cputype, uint32_t cpusubtype) const
+int GradedArchs::grade(uint32_t cputype, uint32_t cpusubtype, bool isOSBinary) const
 {
     for (const CpuGrade* p = _orderedCpuTypes; p->type != 0; ++p) {
-        if ( (p->type == cputype) && (p->subtype == (cpusubtype & ~CPU_SUBTYPE_MASK)) )
-            return p->grade;
+        if ( (p->type == cputype) && (p->subtype == (cpusubtype & ~CPU_SUBTYPE_MASK)) ) {
+            if ( p->osBinary ) {
+                if ( isOSBinary )
+                    return p->grade;
+            }
+            else {
+                return p->grade;
+            }
+        }
     }
     return 0;
 }
@@ -206,13 +274,13 @@ static bool isHaswell()
 }
 #endif
 
-const GradedArchs& GradedArchs::forCurrentOS(const MachOFile* mainExecutable)
+const GradedArchs& GradedArchs::forCurrentOS(bool keysOff, bool osBinariesOnly)
 {
 #if __arm64e__
-    if ( mainExecutable->cpusubtype < CPU_SUBTYPE_ARM64E )
-        return arm64e_compat;
+    if ( osBinariesOnly )
+        return (keysOff ? arm64e_keysoff_pb : arm64e_pb);
     else
-        return arm64e;
+        return (keysOff ? arm64e_keysoff : arm64e);
 #elif __ARM64_ARCH_8_32__
     return arm64_32;
 #elif __arm64__
@@ -232,7 +300,7 @@ const GradedArchs& GradedArchs::forCurrentOS(const MachOFile* mainExecutable)
 #endif
 }
 
-const GradedArchs& GradedArchs::forName(const char* archName, bool forMainExecutable)
+const GradedArchs& GradedArchs::forName(const char* archName, bool keysOff)
 {
     if (strcmp(archName, "x86_64h") == 0 )
         return x86_64h;
@@ -240,7 +308,7 @@ const GradedArchs& GradedArchs::forName(const char* archName, bool forMainExecut
         return x86_64;
 #if SUPPORT_ARCH_arm64e
     else if (strcmp(archName, "arm64e") == 0 )
-        return forMainExecutable ? arm64e_compat : arm64e;
+        return keysOff ? arm64e_keysoff : arm64e;
 #endif
     else if (strcmp(archName, "arm64") == 0 )
         return arm64;
@@ -260,6 +328,7 @@ const GradedArchs& GradedArchs::forName(const char* archName, bool forMainExecut
 }
 
 
+
 ////////////////////////////  MachOFile ////////////////////////////////////////
 
 
@@ -285,7 +354,7 @@ const MachOFile::PlatformInfo MachOFile::_s_platformInfos[] = {
     { "tvOS",        Platform::tvOS,              LC_VERSION_MIN_TVOS     },
     { "watchOS",     Platform::watchOS,           LC_VERSION_MIN_WATCHOS  },
     { "bridgeOS",    Platform::bridgeOS,          LC_BUILD_VERSION        },
-    { "UIKitForMac", Platform::iOSMac,            LC_BUILD_VERSION        },
+    { "MacCatalyst", 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        },
@@ -303,6 +372,10 @@ size_t MachOFile::machHeaderSize() const
     return is64() ? sizeof(mach_header_64) : sizeof(mach_header);
 }
 
+uint32_t MachOFile::maskedCpuSubtype() const
+{
+    return (this->cpusubtype & ~CPU_SUBTYPE_MASK);
+}
 
 uint32_t MachOFile::pointerSize() const
 {
@@ -404,26 +477,25 @@ void MachOFile::packedVersionToString(uint32_t packedVersion, char versionString
     *s++ = '\0';
 }
 
-bool MachOFile::supportsPlatform(Platform reqPlatform) const
+bool MachOFile::builtForPlatform(Platform reqPlatform, bool onlyOnePlatform) const
 {
     __block bool foundRequestedPlatform = false;
-    __block bool foundOtherPlatform = false;
+    __block bool foundOtherPlatform     = false;
     forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
         if ( platform == reqPlatform )
             foundRequestedPlatform = true;
         else
             foundOtherPlatform = true;
     });
+    // if checking that this binary is built for exactly one platform, fail if more
+    if ( foundOtherPlatform && onlyOnePlatform )
+        return false;
     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  ( !foundOtherPlatform && (reqPlatform == Platform::macOS) ) {
         if ( this->cputype == CPU_TYPE_X86_64 )
             return true;
         if ( this->cputype == CPU_TYPE_I386 )
@@ -439,6 +511,52 @@ bool MachOFile::supportsPlatform(Platform reqPlatform) const
     return false;
 }
 
+bool MachOFile::loadableIntoProcess(Platform processPlatform, const char* path) const
+{
+    if ( this->builtForPlatform(processPlatform) )
+        return true;
+
+    // Some host macOS dylibs can be loaded into simulator processes
+    if ( MachOFile::isSimulatorPlatform(processPlatform) && this->builtForPlatform(Platform::macOS)) {
+        static const char* macOSHost[] = {
+            "/usr/lib/system/libsystem_kernel.dylib",
+            "/usr/lib/system/libsystem_platform.dylib",
+            "/usr/lib/system/libsystem_pthread.dylib",
+            "/usr/lib/system/libsystem_platform_debug.dylib",
+            "/usr/lib/system/libsystem_pthread_debug.dylib",
+            "/usr/lib/system/host/liblaunch_sim.dylib",
+        };
+        for (const char* libPath : macOSHost) {
+            if (strcmp(libPath, path) == 0)
+                return true;
+        }
+    }
+
+    // If this is being called on main executable where we expect a macOS program, Catalyst programs are also runnable
+    if ( (this->filetype == MH_EXECUTE) && (processPlatform == Platform::macOS) && this->builtForPlatform(Platform::iOSMac, true) )
+        return true;
+#if (TARGET_OS_OSX && TARGET_CPU_ARM64)
+    if ( (this->filetype == MH_EXECUTE) && (processPlatform == Platform::macOS) && this->builtForPlatform(Platform::iOS, true) )
+        return true;
+#endif
+
+    bool iOSonMac = (processPlatform == Platform::iOSMac);
+#if (TARGET_OS_OSX && TARGET_CPU_ARM64)
+    // allow iOS binaries in iOSApp
+    if ( processPlatform == Platform::iOS ) {
+        // can load Catalyst binaries into iOS process
+        if ( this->builtForPlatform(Platform::iOSMac) )
+            return true;
+        iOSonMac = true;
+    }
+#endif
+    // macOS dylibs can be loaded into iOSMac processes
+    if ( (iOSonMac) && this->builtForPlatform(Platform::macOS, true) )
+        return true;
+
+    return false;
+}
+
 bool MachOFile::isZippered() const
 {
     __block bool macOS = false;
@@ -475,8 +593,10 @@ Platform MachOFile::currentPlatform()
     return Platform::tvOS;
 #elif TARGET_OS_IOS
     return Platform::iOS;
-#elif TARGET_OS_MAC
+#elif TARGET_OS_OSX
     return Platform::macOS;
+#elif TARGET_OS_DRIVERKIT
+    return Platform::driverKit;
 #else
     #error unknown platform
 #endif
@@ -548,6 +668,16 @@ bool MachOFile::isStaticExecutable() const
     return !hasLoadCommand(LC_LOAD_DYLINKER);
 }
 
+bool MachOFile::isKextBundle() const
+{
+    return (this->filetype == MH_KEXT_BUNDLE);
+}
+
+bool MachOFile::isFileSet() const
+{
+    return (this->filetype == MH_FILESET);
+}
+
 bool MachOFile::isPIE() const
 {
     return (this->flags & MH_PIE);
@@ -665,6 +795,52 @@ void MachOFile::forEachLoadCommand(Diagnostics& diag, void (^callback)(const loa
     }
 }
 
+void MachOFile::removeLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& remove, bool& stop))
+{
+    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 if ( hasMachOBigEndianMagic() )
+        return;  // can't process big endian mach-o
+    else {
+        const uint32_t* h = (uint32_t*)this;
+        diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]);
+        return;  // not a mach-o file
+    }
+    const load_command* const cmdsEnd = (load_command*)((char*)startCmds + this->sizeofcmds);
+    auto cmd = (load_command*)startCmds;
+    const uint32_t origNcmds = this->ncmds;
+    unsigned bytesRemaining = this->sizeofcmds;
+    for (uint32_t i = 0; i < origNcmds; ++i) {
+        bool remove = false;
+        auto nextCmd = (load_command*)((char *)cmd + cmd->cmdsize);
+        if ( cmd->cmdsize < 8 ) {
+            diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, this->ncmds, cmd, this, cmd->cmdsize);
+            return;
+        }
+        if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
+            diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, this->ncmds, cmd, this, cmd->cmdsize, cmdsEnd);
+            return;
+        }
+        callback(cmd, remove, stop);
+        if ( remove ) {
+            this->sizeofcmds -= cmd->cmdsize;
+            ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining);
+            this->ncmds--;
+        } else {
+            bytesRemaining -= cmd->cmdsize;
+            cmd = nextCmd;
+        }
+        if ( stop )
+            break;
+    }
+    if ( cmd )
+     ::bzero(cmd, bytesRemaining);
+}
+
 const char* MachOFile::installName() const
 {
     const char*  name;
@@ -788,6 +964,48 @@ bool MachOFile::enforceCompatVersion() const
     return result;
 }
 
+const thread_command* MachOFile::unixThreadLoadCommand() const {
+    Diagnostics diag;
+    __block const thread_command* command = nullptr;
+    forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+        if ( cmd->cmd == LC_UNIXTHREAD ) {
+            command = (const thread_command*)cmd;
+            stop = true;
+        }
+    });
+    return command;
+}
+
+
+uint32_t MachOFile::entryAddrRegisterIndexForThreadCmd() const
+{
+    switch ( this->cputype ) {
+        case CPU_TYPE_I386:
+            return 10; // i386_thread_state_t.eip
+        case CPU_TYPE_X86_64:
+            return 16; // x86_thread_state64_t.rip
+        case CPU_TYPE_ARM:
+            return 15; // arm_thread_state_t.pc
+        case CPU_TYPE_ARM64:
+            return 32; // arm_thread_state64_t.__pc
+    }
+    return ~0U;
+}
+
+
+uint64_t MachOFile::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);
+
+    uint32_t index = entryAddrRegisterIndexForThreadCmd();
+    if (index == ~0U)
+        return 0;
+
+    return is64() ? regs64[index] : regs32[index];
+}
+
 
 void MachOFile::forEachSegment(void (^callback)(const SegmentInfo& info, bool& stop)) const
 {
@@ -987,8 +1205,94 @@ bool MachOFile::isSharedCacheEligiblePath(const char* dylibName) {
             || (strncmp(dylibName, "/Library/Apple/System/Library/", 30) == 0) );
 }
 
+static bool startsWith(const char* buffer, const char* valueToFind) {
+    return strncmp(buffer, valueToFind, strlen(valueToFind)) == 0;
+}
+
+static bool platformExcludesSharedCache_macOS(const char* installName) {
+    // Note: This function basically matches dontCache() from update dyld shared cache
+
+    if ( startsWith(installName, "/usr/lib/system/introspection/") )
+        return true;
+    if ( startsWith(installName, "/System/Library/QuickTime/") )
+        return true;
+    if ( startsWith(installName, "/System/Library/Tcl/") )
+        return true;
+    if ( startsWith(installName, "/System/Library/Perl/") )
+        return true;
+    if ( startsWith(installName, "/System/Library/MonitorPanels/") )
+        return true;
+    if ( startsWith(installName, "/System/Library/Accessibility/") )
+        return true;
+    if ( startsWith(installName, "/usr/local/") )
+        return true;
+    if ( startsWith(installName, "/usr/lib/pam/") )
+        return true;
+    // We no longer support ROSP, so skip all paths which start with the special prefix
+    if ( startsWith(installName, "/System/Library/Templates/Data/") )
+        return true;
+
+    // anything inside a .app bundle is specific to app, so should not be in shared cache
+    if ( strstr(installName, ".app/") != NULL )
+        return true;
+
+    return false;
+}
+
+static bool platformExcludesSharedCache_iOS(const char* installName) {
+    if ( strcmp(installName, "/System/Library/Caches/com.apple.xpc/sdk.dylib") == 0 )
+        return true;
+    if ( strcmp(installName, "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib") == 0 )
+        return true;
+    return false;
+}
+
+static bool platformExcludesSharedCache_tvOS(const char* installName) {
+    return platformExcludesSharedCache_iOS(installName);
+}
+
+static bool platformExcludesSharedCache_watchOS(const char* installName) {
+    return platformExcludesSharedCache_iOS(installName);
+}
+
+static bool platformExcludesSharedCache_bridgeOS(const char* installName) {
+    return platformExcludesSharedCache_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.
+static bool platformExcludesSharedCache(Platform platform, const char* installName) {
+    switch (platform) {
+        case dyld3::Platform::unknown:
+            return false;
+        case dyld3::Platform::macOS:
+            return platformExcludesSharedCache_macOS(installName);
+        case dyld3::Platform::iOS:
+            return platformExcludesSharedCache_iOS(installName);
+        case dyld3::Platform::tvOS:
+            return platformExcludesSharedCache_tvOS(installName);
+        case dyld3::Platform::watchOS:
+            return platformExcludesSharedCache_watchOS(installName);
+        case dyld3::Platform::bridgeOS:
+            return platformExcludesSharedCache_bridgeOS(installName);
+        case dyld3::Platform::iOSMac:
+            return platformExcludesSharedCache_macOS(installName);
+        case dyld3::Platform::iOS_simulator:
+            return false;
+        case dyld3::Platform::tvOS_simulator:
+            return false;
+        case dyld3::Platform::watchOS_simulator:
+            return false;
+        case dyld3::Platform::driverKit:
+            return false;
+    }
+}
+
+
+
 bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const
 {
+
     if ( !isSharedCacheEligiblePath(path) ) {
         // Dont spam the user with an error about paths when we know these are never eligible.
         return false;
@@ -1012,6 +1316,28 @@ bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(c
         failureReason("install path does not match install name");
         return false;
     }
+    else if ( strstr(dylibName, "//") != 0 ) {
+        failureReason("install name should not include //");
+        return false;
+    }
+    else if ( strstr(dylibName, "./") != 0 ) {
+        failureReason("install name should not include ./");
+        return false;
+    }
+
+    __block bool platformExcludedFile = false;
+    forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+        if ( platformExcludedFile )
+            return;
+        if ( platformExcludesSharedCache(platform, dylibName) ) {
+            platformExcludedFile = true;
+            return;
+        }
+    });
+    if ( platformExcludedFile ) {
+        failureReason("install name is not shared cache eligible on platform");
+        return false;
+    }
 
     bool retval = true;
 
@@ -1031,6 +1357,7 @@ bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(c
     __block bool hasExtraInfo = false;
     __block bool hasDyldInfo = false;
     __block bool hasExportTrie = false;
+    __block bool hasLazyLoad = false;
     Diagnostics diag;
     forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
         if ( cmd->cmd == LC_SEGMENT_SPLIT_INFO )
@@ -1039,6 +1366,8 @@ bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(c
             hasDyldInfo = true;
         if ( cmd->cmd == LC_DYLD_EXPORTS_TRIE )
             hasExportTrie = true;
+        if ( cmd->cmd == LC_LAZY_LOAD_DYLIB )
+            hasLazyLoad = true;
     });
     if ( !hasExtraInfo ) {
         retval = false;
@@ -1048,6 +1377,10 @@ bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(c
         retval = false;
         failureReason("Old binary, missing dyld info or export trie");
     }
+    if ( hasLazyLoad ) {
+        retval = false;
+        failureReason("Has lazy load");
+    }
 
     // dylib can only depend on other dylibs in the shared cache
     __block bool allDepPathsAreGood = true;
@@ -1062,6 +1395,103 @@ bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(c
         failureReason("Depends on dylibs ineligable for dyld cache");
     }
 
+    // dylibs with interposing info cannot be in cache
+    if ( hasInterposingTuples() ) {
+        retval = false;
+        failureReason("Has interposing tuples");
+    }
+
+    // Temporarily kick out swift binaries out of dyld cache on watchOS simulators as they have missing split seg
+    if ( (this->cputype == CPU_TYPE_I386) && builtForPlatform(Platform::watchOS_simulator) ) {
+        if ( strncmp(dylibName, "/usr/lib/swift/", 15) == 0 ) {
+            retval = false;
+            failureReason("i386 swift binary");
+        }
+    }
+
+    return retval;
+}
+
+#if BUILDING_APP_CACHE_UTIL
+bool MachOFile::canBePlacedInKernelCollection(const char* path, void (^failureReason)(const char*)) const
+{
+    // only dylibs and the kernel itself can go in cache
+    if ( this->filetype == MH_EXECUTE ) {
+        // xnu
+    } else if ( this->isKextBundle() ) {
+        // kext's
+    } else {
+        failureReason("Not MH_KEXT_BUNDLE");
+        return false;
+    }
+
+    if ( this->filetype == MH_EXECUTE ) {
+        // xnu
+
+        // two-level namespace binaries cannot go in cache
+        if ( (this->flags & MH_TWOLEVEL) != 0 ) {
+            failureReason("Built with two level namespaces");
+            return false;
+        }
+
+        // xnu kernel cannot have a page zero
+        __block bool foundPageZero = false;
+        forEachSegment(^(const SegmentInfo &segmentInfo, bool &stop) {
+            if ( strcmp(segmentInfo.segName, "__PAGEZERO") == 0 ) {
+                foundPageZero = true;
+                stop = true;
+            }
+        });
+        if (foundPageZero) {
+            failureReason("Has __PAGEZERO");
+            return false;
+        }
+
+        // xnu must have an LC_UNIXTHREAD to point to the entry point
+        __block bool foundMainLC = false;
+        __block bool foundUnixThreadLC = false;
+        Diagnostics diag;
+        forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) {
+            if ( cmd->cmd == LC_MAIN ) {
+                foundMainLC = true;
+                stop = true;
+            }
+            else if ( cmd->cmd == LC_UNIXTHREAD ) {
+                foundUnixThreadLC = true;
+            }
+        });
+        if (foundMainLC) {
+            failureReason("Found LC_MAIN");
+            return false;
+        }
+        if (!foundUnixThreadLC) {
+            failureReason("Expected LC_UNIXTHREAD");
+            return false;
+        }
+
+        if (diag.hasError()) {
+            failureReason("Error parsing load commands");
+            return false;
+        }
+
+        // The kernel should be a static executable, not a dynamic one
+        if ( !isStaticExecutable() ) {
+            failureReason("Expected static executable");
+            return false;
+        }
+
+        // The kernel must be built with -pie
+        if ( !isPIE() ) {
+            failureReason("Expected pie");
+            return false;
+        }
+    }
+
+    if ( isArch("arm64e") && isKextBundle() && !hasChainedFixups() ) {
+        failureReason("Missing fixup information");
+        return false;
+    }
+
     // dylibs with interposing info cannot be in cache
     __block bool hasInterposing = false;
     forEachSection(^(const SectionInfo& info, bool malformedSectionRange, bool &stop) {
@@ -1069,21 +1499,142 @@ bool MachOFile::canBePlacedInDyldCache(const char* path, void (^failureReason)(c
             hasInterposing = true;
     });
     if ( hasInterposing ) {
-        retval = false;
         failureReason("Has interposing tuples");
+        return false;
     }
 
-    // Temporarily kick out swift binaries on watchOS simulators as they have missing split seg
-    if ( supportsPlatform(Platform::watchOS_simulator) && isArch("i386") ) {
-        if ( strncmp(dylibName, "/usr/lib/swift/", 15) == 0 ) {
-            retval = false;
-            failureReason("i386 swift binary");
+    return true;
+}
+#endif
+
+static bool platformExcludesPrebuiltClosure_macOS(const char* path) {
+    // We no longer support ROSP, so skip all paths which start with the special prefix
+    if ( startsWith(path, "/System/Library/Templates/Data/") )
+        return true;
+
+    // anything inside a .app bundle is specific to app, so should not get a prebuilt closure
+    if ( strstr(path, ".app/") != NULL )
+        return true;
+
+    return false;
+}
+
+static bool platformExcludesPrebuiltClosure_iOS(const char* path) {
+    if ( strcmp(path, "/System/Library/Caches/com.apple.xpc/sdk.dylib") == 0 )
+        return true;
+    if ( strcmp(path, "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib") == 0 )
+        return true;
+    return false;
+}
+
+static bool platformExcludesPrebuiltClosure_tvOS(const char* path) {
+    return platformExcludesPrebuiltClosure_iOS(path);
+}
+
+static bool platformExcludesPrebuiltClosure_watchOS(const char* path) {
+    return platformExcludesPrebuiltClosure_iOS(path);
+}
+
+static bool platformExcludesPrebuiltClosure_bridgeOS(const char* path) {
+    return platformExcludesPrebuiltClosure_iOS(path);
+}
+
+// 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.
+static bool platformExcludesPrebuiltClosure(Platform platform, const char* path) {
+    switch (platform) {
+        case dyld3::Platform::unknown:
+            return false;
+        case dyld3::Platform::macOS:
+            return platformExcludesPrebuiltClosure_macOS(path);
+        case dyld3::Platform::iOS:
+            return platformExcludesPrebuiltClosure_iOS(path);
+        case dyld3::Platform::tvOS:
+            return platformExcludesPrebuiltClosure_tvOS(path);
+        case dyld3::Platform::watchOS:
+            return platformExcludesPrebuiltClosure_watchOS(path);
+        case dyld3::Platform::bridgeOS:
+            return platformExcludesPrebuiltClosure_bridgeOS(path);
+        case dyld3::Platform::iOSMac:
+            return platformExcludesPrebuiltClosure_macOS(path);
+        case dyld3::Platform::iOS_simulator:
+            return false;
+        case dyld3::Platform::tvOS_simulator:
+            return false;
+        case dyld3::Platform::watchOS_simulator:
+            return false;
+        case dyld3::Platform::driverKit:
+            return false;
+    }
+}
+
+bool MachOFile::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");
+    }
+
+    __block bool platformExcludedFile = false;
+    forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
+        if ( platformExcludedFile )
+            return;
+        if ( platformExcludesPrebuiltClosure(platform, path) ) {
+            platformExcludedFile = true;
+            return;
         }
+    });
+    if ( platformExcludedFile ) {
+        failureReason("file cannot get a prebuilt closure on this platform");
+        return false;
+    }
+
+    // dylibs with interposing info cannot have dlopen closure pre-computed
+    if ( hasInterposingTuples() ) {
+        retval = false;
+        failureReason("has interposing tuples");
+    }
+
+    // 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 MachOFile::hasInterposingTuples() const
+{
+    __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;
+    });
+    return hasInterposing;
+}
 
 bool MachOFile::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const
 {
@@ -1151,12 +1702,19 @@ bool MachOFile::hasChainedFixups() const
 {
 #if SUPPORT_ARCH_arm64e
     // arm64e always uses chained fixups
-    if ( (this->cputype == CPU_TYPE_ARM64) && (this->cpusubtype == CPU_SUBTYPE_ARM64E) )
-        return true;
+    if ( (this->cputype == CPU_TYPE_ARM64) && (this->maskedCpuSubtype() == CPU_SUBTYPE_ARM64E) ) {
+        // Not all binaries have fixups at all so check for the load commands
+        return hasLoadCommand(LC_DYLD_INFO_ONLY) || hasLoadCommand(LC_DYLD_CHAINED_FIXUPS);
+    }
 #endif
     return hasLoadCommand(LC_DYLD_CHAINED_FIXUPS);
 }
 
+bool MachOFile::hasChainedFixupsLoadCommand() const
+{
+    return hasLoadCommand(LC_DYLD_CHAINED_FIXUPS);
+}
+
 uint64_t MachOFile::read_uleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end)
 {
     uint64_t result = 0;
@@ -1197,7 +1755,7 @@ int64_t MachOFile::read_sleb128(Diagnostics& diag, const uint8_t*& p, const uint
         bit += 7;
     } while (byte & 0x80);
     // sign extend negative numbers
-    if ( (byte & 0x40) != 0 )
+    if ( ((byte & 0x40) != 0) && (bit < 64) )
         result |= (~0ULL) << bit;
     return result;
 }
index 6a78714c92b973dc0c7b5842d8539eaac90b1251..b9099e62d8a0da8ff2934f98a63f594e6cf6cf40 100644 (file)
   #define S_INIT_FUNC_OFFSETS       0x16
 #endif
 
+#ifndef MH_FILESET
+    #define MH_FILESET              0xc     /* set of mach-o's */
+#endif
+
 namespace dyld3 {
 
+// replacements for posix that handle EINTR
+int    stat(const char* path, struct stat* buf) VIS_HIDDEN;
+int    open(const char* path, int flag, int other) VIS_HIDDEN;
 
 
 /// Returns true if (addLHS + addRHS) > b, or if the add overflowed
@@ -99,7 +106,7 @@ enum class Platform {
     tvOS                = 3,    // PLATFORM_TVOS
     watchOS             = 4,    // PLATFORM_WATCHOS
     bridgeOS            = 5,    // PLATFORM_BRIDGEOS
-    iOSMac              = 6,    // PLATFORM_IOSMAC
+    iOSMac              = 6,    // PLATFORM_MACCATALYST
     iOS_simulator       = 7,    // PLATFORM_IOSSIMULATOR
     tvOS_simulator      = 8,    // PLATFORM_TVOSSIMULATOR
     watchOS_simulator   = 9,    // PLATFORM_WATCHOSSIMULATOR
@@ -115,10 +122,11 @@ public:
     GradedArchs()                   = delete;
     GradedArchs(const GradedArchs&) = delete;
 
-    static const GradedArchs&  forCurrentOS(const MachOFile* mainExecutable);
-    static const GradedArchs&  forName(const char* archName, bool forMainExecutable = false);
+    static const GradedArchs&  forCurrentOS(bool keysOff, bool platformBinariesOnly);
+    static const GradedArchs&  forName(const char* archName, bool keysOff = false);
+
 
-    int                     grade(uint32_t cputype, uint32_t cpusubtype) const;
+    int                     grade(uint32_t cputype, uint32_t cpusubtype, bool platformBinariesOnly) const;
     const char*             name() const;
 
     // pre-built lists for existing hardware
@@ -127,8 +135,10 @@ public:
     static const GradedArchs x86_64h;         // haswell Mac
     static const GradedArchs arm64;           // A11 or earlier iPhone or iPad
 #if SUPPORT_ARCH_arm64e
-    static const GradedArchs arm64e;          // A12 or later iPhone or iPad
-    static const GradedArchs arm64e_compat;   // A12 running arm64 main executable
+    static const GradedArchs arm64e;            // A12 or later iPhone or iPad
+    static const GradedArchs arm64e_keysoff;    // A12 running with signing keys disabled
+    static const GradedArchs arm64e_pb;         // macOS Apple Silicon running platform binary
+    static const GradedArchs arm64e_keysoff_pb; // macOS Apple Silicon running with signing keys disabled
 #endif
     static const GradedArchs armv7k;          // watch thru series 3
     static const GradedArchs armv7s;          // deprecated
@@ -139,7 +149,7 @@ public:
 
 // private:
 // should be private, but compiler won't statically initialize static members above
-    struct CpuGrade { uint32_t type; uint32_t subtype; uint32_t grade; };
+    struct CpuGrade { uint32_t type; uint32_t subtype; bool osBinary; uint16_t grade; };
     const CpuGrade     _orderedCpuTypes[3];  // zero terminated
 };
 
@@ -149,7 +159,7 @@ 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 GradedArchs& archs, uint64_t& sliceOffset, uint64_t& sliceLen, bool& missingSlice) const;
+    bool                   isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const GradedArchs& archs, bool osBinary, uint64_t& sliceOffset, uint64_t& sliceLen, bool& missingSlice) const;
 
 private:
     bool                   isValidSlice(Diagnostics& diag, uint64_t fileLen, uint32_t sliceIndex,
@@ -180,18 +190,21 @@ struct VIS_HIDDEN MachOFile : mach_header
     bool            isMainExecutable() const;
     bool            isDynamicExecutable() const;
     bool            isStaticExecutable() const;
+    bool            isKextBundle() const;
+    bool            isFileSet() const;
     bool            isPreload() const;
     bool            isPIE() const;
     bool            isArch(const char* archName) const;
     const char*     archName() const;
     bool            is64() const;
+    uint32_t        maskedCpuSubtype() const;
     size_t          machHeaderSize() const;
     uint32_t        pointerSize() const;
     bool            uses16KPages() const;
-    bool            supportsPlatform(Platform) const;
+    bool            builtForPlatform(Platform, bool onlyOnePlatform=false) const;
+    bool            loadableIntoProcess(Platform processPlatform, const char* path) const;
     bool            isZippered() const;
     bool            inDyldCache() const;
-    bool            isSimulatorBinary() const;
     bool            getUuid(uuid_t uuid) const;
     bool            hasWeakDefs() const;
     bool            hasThreadLocalVariables() const;
@@ -200,12 +213,22 @@ struct VIS_HIDDEN MachOFile : mach_header
     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;
+#if BUILDING_APP_CACHE_UTIL
+    bool            canBePlacedInKernelCollection(const char* path, void (^failureReason)(const char*)) const;
+#endif
+    bool            canHavePrecomputedDlopenClosure(const char* path, void (^failureReason)(const char*)) const;
     bool            canBeFairPlayEncrypted() const;
     bool            isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const;
     bool            allowsAlternatePlatform() const;
     bool            hasChainedFixups() const;
+    bool            hasChainedFixupsLoadCommand() const;
     void            forDyldEnv(void (^callback)(const char* envVar, bool& stop)) const;
     bool            enforceCompatVersion() const;
+    bool            hasInterposingTuples() const;
+
+    const thread_command*   unixThreadLoadCommand() const;
+    uint32_t                entryAddrRegisterIndexForThreadCmd() const;
+    uint64_t                entryAddrFromThreadCmd(const thread_command* cmd) const;
 
     struct SegmentInfo
     {
@@ -246,6 +269,7 @@ struct VIS_HIDDEN MachOFile : mach_header
 protected:
     bool            hasMachOBigEndianMagic() const;
     void            forEachLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& stop)) const;
+    void            removeLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& remove, bool& stop));
     bool            hasLoadCommand(uint32_t) const;
 
     const encryption_info_command* findFairPlayEncryptionLoadCommand() const;
index 35c61ac2046a64983ec74a8b814a5a5ab728733d..13bfca7f358b9392e4b0e638a61792bab2a9f7d6 100644 (file)
@@ -211,6 +211,7 @@ void MachOLoaded::getLayoutInfo(LayoutInfo& result) const
             result.linkeditFileSize     = (uint32_t)info.fileSize;
             result.linkeditSegIndex     = info.segIndex;
         }
+        result.lastSegIndex = info.segIndex;
     });
 }
 
@@ -707,9 +708,8 @@ bool MachOLoaded::intersectsRange(uintptr_t start, uintptr_t length) const
 
 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;
+    STACK_ALLOC_OVERFLOW_SAFE_ARRAY(uint32_t, visitedNodeOffsets, 128);
+    visitedNodeOffsets.push_back(0);
     const uint8_t* p = start;
     while ( p < end ) {
         uint64_t terminalSize = *p++;
@@ -778,17 +778,14 @@ const uint8_t* MachOLoaded::trieWalk(Diagnostics& diag, const uint8_t* start, co
                 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 ) {
+            // check for cycles
+            for (uint32_t aVisitedNodeOffset : visitedNodeOffsets) {
+                if ( aVisitedNodeOffset == 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;
-            }
+            visitedNodeOffsets.push_back((uint32_t)nodeOffset);
             p = &start[nodeOffset];
         }
         else
@@ -914,7 +911,7 @@ void MachOLoaded::forEachCodeDirectoryBlob(const void* codeSigStart, size_t code
 
     // Note: The kernel sometimes chooses sha1 on watchOS, and sometimes sha256.
     // Embed all of them so that we just need to match any of them
-    const bool isWatchOS = this->supportsPlatform(Platform::watchOS);
+    const bool isWatchOS = this->builtForPlatform(Platform::watchOS);
     const bool isMainExecutable = this->isMainExecutable();
     auto hashRankFn = isWatchOS ? &hash_rank_watchOS_dylibs : &hash_rank;
 
@@ -967,43 +964,58 @@ uint64_t MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::signExtendedAddend() co
 {
     assert(this->authBind.bind == 1);
     assert(this->authBind.auth == 0);
-    uint64_t addend19     = this->bind.addend;
+    uint64_t addend19 = this->bind.addend;
     if ( addend19 & 0x40000 )
         return addend19 | 0xFFFFFFFFFFFC0000ULL;
     else
         return addend19;
 }
 
-const char* MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::keyName() const
+const char* MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::keyName(uint8_t keyBits)
 {
     static const char* names[] = {
         "IA", "IB", "DA", "DB"
     };
-    assert(this->authBind.auth == 1);
-    uint8_t keyBits = this->authBind.key;
     assert(keyBits < 4);
     return names[keyBits];
 }
-
-uint64_t MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::signPointer(void* loc, uint64_t target) const
+const char* MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::keyName() const
 {
     assert(this->authBind.auth == 1);
+    return keyName(this->authBind.key);
+}
+
+uint64_t MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::signPointer(uint64_t unsignedAddr, void* loc, bool addrDiv, uint16_t diversity, uint8_t key)
+{
+    // don't sign NULL
+    if ( unsignedAddr == 0 )
+        return 0;
+        
 #if __has_feature(ptrauth_calls)
-    uint64_t discriminator = authBind.diversity;
-    if ( authBind.addrDiv )
-        discriminator = __builtin_ptrauth_blend_discriminator(loc, discriminator);
-    switch ( authBind.key ) {
+    uint64_t extendedDiscriminator = diversity;
+    if ( addrDiv )
+        extendedDiscriminator = __builtin_ptrauth_blend_discriminator(loc, extendedDiscriminator);
+    switch ( key ) {
         case 0: // IA
-            return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target, 0, discriminator);
+            return (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)unsignedAddr, 0, extendedDiscriminator);
         case 1: // IB
-            return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target, 1, discriminator);
+            return (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)unsignedAddr, 1, extendedDiscriminator);
         case 2: // DA
-            return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target, 2, discriminator);
+            return (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)unsignedAddr, 2, extendedDiscriminator);
         case 3: // DB
-            return (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)target, 3, discriminator);
+            return (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)unsignedAddr, 3, extendedDiscriminator);
     }
+    assert(0 && "invalid signing key");
+#else
+    assert(0 && "arm64e signing only arm64e");
 #endif
-    return target;
+}
+
+
+uint64_t MachOLoaded::ChainedFixupPointerOnDisk::Arm64e::signPointer(void* loc, uint64_t target) const
+{
+    assert(this->authBind.auth == 1);
+    return signPointer(target, loc, authBind.addrDiv, authBind.diversity, authBind.key);
 }
 
 uint64_t MachOLoaded::ChainedFixupPointerOnDisk::Generic64::unpackedTarget() const
@@ -1020,23 +1032,25 @@ uint64_t MachOLoaded::ChainedFixupPointerOnDisk::Generic64::signExtendedAddend()
     return newValue;
 }
 
+const char* MachOLoaded::ChainedFixupPointerOnDisk::Kernel64::keyName() const
+{
+    static const char* names[] = {
+        "IA", "IB", "DA", "DB"
+    };
+    assert(this->isAuth == 1);
+    uint8_t keyBits = this->key;
+    assert(keyBits < 4);
+    return names[keyBits];
+}
+
 bool MachOLoaded::ChainedFixupPointerOnDisk::isRebase(uint16_t pointerFormat, uint64_t preferedLoadAddress, uint64_t& targetRuntimeOffset) const
 {
     switch (pointerFormat) {
        case DYLD_CHAINED_PTR_ARM64E:
-            if ( this->arm64e.bind.bind )
-                return false;
-            if ( this->arm64e.authRebase.auth ) {
-                targetRuntimeOffset = this->arm64e.authRebase.target;
-                return true;
-            }
-            else {
-                targetRuntimeOffset = this->arm64e.unpackTarget() - preferedLoadAddress;
-                return true;
-            }
-            break;
-       case DYLD_CHAINED_PTR_ARM64E_OFFSET:
        case DYLD_CHAINED_PTR_ARM64E_USERLAND:
+       case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
+       case DYLD_CHAINED_PTR_ARM64E_KERNEL:
+       case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
             if ( this->arm64e.bind.bind )
                 return false;
             if ( this->arm64e.authRebase.auth ) {
@@ -1045,19 +1059,24 @@ bool MachOLoaded::ChainedFixupPointerOnDisk::isRebase(uint16_t pointerFormat, ui
             }
             else {
                 targetRuntimeOffset = this->arm64e.unpackTarget();
+                if ( (pointerFormat == DYLD_CHAINED_PTR_ARM64E) || (pointerFormat == DYLD_CHAINED_PTR_ARM64E_FIRMWARE) ) {
+                    targetRuntimeOffset -= preferedLoadAddress;
+                }
                 return true;
             }
             break;
         case DYLD_CHAINED_PTR_64:
-            if ( this->generic64.bind.bind )
-                return false;
-            targetRuntimeOffset = this->generic64.unpackedTarget() - preferedLoadAddress;
-            return true;
-            break;
         case DYLD_CHAINED_PTR_64_OFFSET:
             if ( this->generic64.bind.bind )
                 return false;
             targetRuntimeOffset = this->generic64.unpackedTarget();
+            if ( pointerFormat == DYLD_CHAINED_PTR_64 )
+                targetRuntimeOffset -= preferedLoadAddress;
+            return true;
+            break;
+        case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
+        case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
+            targetRuntimeOffset = this->kernel64.target;
             return true;
             break;
         case DYLD_CHAINED_PTR_32:
@@ -1066,26 +1085,40 @@ bool MachOLoaded::ChainedFixupPointerOnDisk::isRebase(uint16_t pointerFormat, ui
             targetRuntimeOffset = this->generic32.rebase.target - preferedLoadAddress;
             return true;
             break;
+        case DYLD_CHAINED_PTR_32_FIRMWARE:
+            targetRuntimeOffset = this->firmware32.target - preferedLoadAddress;
+            return true;
+            break;
         default:
             break;
     }
     assert(0 && "unsupported pointer chain format");
 }
 
-bool MachOLoaded::ChainedFixupPointerOnDisk::isBind(uint16_t pointerFormat, uint32_t& bindOrdinal) const
+bool MachOLoaded::ChainedFixupPointerOnDisk::isBind(uint16_t pointerFormat, uint32_t& bindOrdinal, int64_t& addend) const
 {
+    addend = 0;
     switch (pointerFormat) {
         case DYLD_CHAINED_PTR_ARM64E:
-        case DYLD_CHAINED_PTR_ARM64E_OFFSET:
         case DYLD_CHAINED_PTR_ARM64E_USERLAND:
+        case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
+        case DYLD_CHAINED_PTR_ARM64E_KERNEL:
+        case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
             if ( !this->arm64e.authBind.bind )
                 return false;
             if ( this->arm64e.authBind.auth ) {
-                bindOrdinal = this->arm64e.authBind.ordinal;
+                if ( pointerFormat == DYLD_CHAINED_PTR_ARM64E_USERLAND24 )
+                    bindOrdinal = this->arm64e.authBind24.ordinal;
+                else
+                    bindOrdinal = this->arm64e.authBind.ordinal;
                 return true;
             }
             else {
-                bindOrdinal = this->arm64e.bind.ordinal;
+                if ( pointerFormat == DYLD_CHAINED_PTR_ARM64E_USERLAND24 )
+                    bindOrdinal = this->arm64e.bind24.ordinal;
+                else
+                    bindOrdinal = this->arm64e.bind.ordinal;
+                addend = this->arm64e.signExtendedAddend();
                 return true;
             }
             break;
@@ -1094,20 +1127,47 @@ bool MachOLoaded::ChainedFixupPointerOnDisk::isBind(uint16_t pointerFormat, uint
             if ( !this->generic64.bind.bind )
                 return false;
             bindOrdinal = this->generic64.bind.ordinal;
+            addend = this->generic64.bind.addend;
             return true;
             break;
         case DYLD_CHAINED_PTR_32:
             if ( !this->generic32.bind.bind )
                 return false;
             bindOrdinal = this->generic32.bind.ordinal;
+            addend = this->generic32.bind.addend;
             return true;
             break;
+        case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
+        case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
+            return false;
         default:
             break;
     }
     assert(0 && "unsupported pointer chain format");
 }
 
+unsigned MachOLoaded::ChainedFixupPointerOnDisk::strideSize(uint16_t pointerFormat)
+{
+    switch (pointerFormat) {
+        case DYLD_CHAINED_PTR_ARM64E:
+        case DYLD_CHAINED_PTR_ARM64E_USERLAND:
+        case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
+            return 8;
+        case DYLD_CHAINED_PTR_ARM64E_KERNEL:
+        case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
+        case DYLD_CHAINED_PTR_32_FIRMWARE:
+        case DYLD_CHAINED_PTR_64:
+        case DYLD_CHAINED_PTR_64_OFFSET:
+        case DYLD_CHAINED_PTR_32:
+        case DYLD_CHAINED_PTR_32_CACHE:
+        case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
+            return 4;
+        case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
+            return 1;
+    }
+    assert(0 && "unsupported pointer chain format");
+}
+
 #if BUILDING_DYLD || BUILDING_LIBDYLD
 void MachOLoaded::fixupAllChainedFixups(Diagnostics& diag, const dyld_chained_starts_in_image* starts, uintptr_t slide,
                                         Array<const void*> bindTargets, void (^logFixup)(void* loc, void* newValue)) const
@@ -1118,16 +1178,20 @@ void MachOLoaded::fixupAllChainedFixups(Diagnostics& diag, const dyld_chained_st
 #if __LP64__
   #if  __has_feature(ptrauth_calls)
            case DYLD_CHAINED_PTR_ARM64E:
+           case DYLD_CHAINED_PTR_ARM64E_KERNEL:
+           case DYLD_CHAINED_PTR_ARM64E_USERLAND:
+           case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
                if ( fixupLoc->arm64e.authRebase.auth ) {
                     if ( fixupLoc->arm64e.authBind.bind ) {
-                        if ( fixupLoc->arm64e.authBind.ordinal >= bindTargets.count() ) {
-                            diag.error("out of range bind ordinal %d (max %lu)", fixupLoc->arm64e.authBind.ordinal, bindTargets.count());
+                        uint32_t bindOrdinal = (segInfo->pointer_format == DYLD_CHAINED_PTR_ARM64E_USERLAND24) ? fixupLoc->arm64e.authBind24.ordinal : fixupLoc->arm64e.authBind.ordinal;
+                        if ( bindOrdinal >= bindTargets.count() ) {
+                            diag.error("out of range bind ordinal %d (max %lu)", bindOrdinal, bindTargets.count());
                             stop = true;
                             break;
                         }
                         else {
                             // authenticated bind
-                            newValue = (void*)(bindTargets[fixupLoc->arm64e.bind.ordinal]);
+                            newValue = (void*)(bindTargets[bindOrdinal]);
                             if (newValue != 0)  // Don't sign missing weak imports
                                 newValue = (void*)fixupLoc->arm64e.signPointer(fixupLoc, (uintptr_t)newValue);
                         }
@@ -1139,86 +1203,31 @@ void MachOLoaded::fixupAllChainedFixups(Diagnostics& diag, const dyld_chained_st
                 }
                 else {
                     if ( fixupLoc->arm64e.bind.bind ) {
-                        if ( fixupLoc->arm64e.bind.ordinal >= bindTargets.count() ) {
-                            diag.error("out of range bind ordinal %d (max %lu)", fixupLoc->arm64e.bind.ordinal, bindTargets.count());
+                        uint32_t bindOrdinal = (segInfo->pointer_format == DYLD_CHAINED_PTR_ARM64E_USERLAND24) ? fixupLoc->arm64e.bind24.ordinal : fixupLoc->arm64e.bind.ordinal;
+                        if ( bindOrdinal >= bindTargets.count() ) {
+                            diag.error("out of range bind ordinal %d (max %lu)", bindOrdinal, bindTargets.count());
                             stop = true;
                             break;
                         }
                         else {
                             // plain bind
-                            newValue = (void*)((long)bindTargets[fixupLoc->arm64e.bind.ordinal] + fixupLoc->arm64e.signExtendedAddend());
+                            newValue = (void*)((long)bindTargets[bindOrdinal] + fixupLoc->arm64e.signExtendedAddend());
                         }
                     }
                     else {
-                        // plain rebase
-                        newValue = (void*)(fixupLoc->arm64e.unpackTarget()+slide);
+                        // plain rebase (old format target is vmaddr, new format target is offset)
+                        if ( segInfo->pointer_format == DYLD_CHAINED_PTR_ARM64E )
+                            newValue = (void*)(fixupLoc->arm64e.unpackTarget()+slide);
+                        else
+                            newValue = (void*)((uintptr_t)this + fixupLoc->arm64e.unpackTarget());
                    }
                 }
                 if ( logFixup )
                     logFixup(fixupLoc, newValue);
                 fixupLoc->raw64 = (uintptr_t)newValue;
                 break;
-            case DYLD_CHAINED_PTR_ARM64E_OFFSET:
-            case DYLD_CHAINED_PTR_ARM64E_USERLAND:
-                if ( fixupLoc->arm64e.authRebase.auth ) {
-                     if ( fixupLoc->arm64e.authBind.bind ) {
-                         if ( fixupLoc->arm64e.authBind.ordinal >= bindTargets.count() ) {
-                             diag.error("out of range bind ordinal %d (max %lu)", fixupLoc->arm64e.authBind.ordinal, bindTargets.count());
-                             stop = true;
-                             break;
-                         }
-                         else {
-                             // authenticated bind
-                             newValue = (void*)(bindTargets[fixupLoc->arm64e.bind.ordinal]);
-                             if (newValue != 0)  // Don't sign missing weak imports
-                                 newValue = (void*)fixupLoc->arm64e.signPointer(fixupLoc, (uintptr_t)newValue);
-                         }
-                     }
-                     else {
-                         // authenticated rebase
-                         newValue = (void*)fixupLoc->arm64e.signPointer(fixupLoc, (uintptr_t)this + fixupLoc->arm64e.authRebase.target);
-                     }
-                 }
-                 else {
-                     if ( fixupLoc->arm64e.bind.bind ) {
-                         if ( fixupLoc->arm64e.bind.ordinal >= bindTargets.count() ) {
-                             diag.error("out of range bind ordinal %d (max %lu)", fixupLoc->arm64e.bind.ordinal, bindTargets.count());
-                             stop = true;
-                             break;
-                         }
-                         else {
-                             // plain bind
-                             newValue = (void*)((long)bindTargets[fixupLoc->arm64e.bind.ordinal] + fixupLoc->arm64e.signExtendedAddend());
-                         }
-                     }
-                     else {
-                         // plain rebase
-                         newValue = (void*)((uintptr_t)this + fixupLoc->arm64e.unpackTarget());
-                    }
-                 }
-                 if ( logFixup )
-                     logFixup(fixupLoc, newValue);
-                 fixupLoc->raw64 = (uintptr_t)newValue;
-                 break;
   #endif
             case DYLD_CHAINED_PTR_64:
-                if ( fixupLoc->generic64.bind.bind ) {
-                    if ( fixupLoc->generic64.bind.ordinal >= bindTargets.count() ) {
-                        diag.error("out of range bind ordinal %d (max %lu)", fixupLoc->generic64.bind.ordinal, bindTargets.count());
-                        stop = true;
-                        break;
-                    }
-                    else {
-                        newValue = (void*)((long)bindTargets[fixupLoc->generic64.bind.ordinal] + fixupLoc->generic64.signExtendedAddend());
-                    }
-                }
-                else {
-                    newValue = (void*)(fixupLoc->generic64.unpackedTarget()+slide);
-                }
-                if ( logFixup )
-                    logFixup(fixupLoc, newValue);
-                fixupLoc->raw64 = (uintptr_t)newValue;
-               break;
             case DYLD_CHAINED_PTR_64_OFFSET:
                 if ( fixupLoc->generic64.bind.bind ) {
                     if ( fixupLoc->generic64.bind.ordinal >= bindTargets.count() ) {
@@ -1231,7 +1240,11 @@ void MachOLoaded::fixupAllChainedFixups(Diagnostics& diag, const dyld_chained_st
                     }
                 }
                 else {
-                    newValue = (void*)((uintptr_t)this + fixupLoc->generic64.unpackedTarget());
+                    // plain rebase (old format target is vmaddr, new format target is offset)
+                    if ( segInfo->pointer_format == DYLD_CHAINED_PTR_64 )
+                        newValue = (void*)(fixupLoc->generic64.unpackedTarget()+slide);
+                    else
+                        newValue = (void*)((uintptr_t)this + fixupLoc->generic64.unpackedTarget());
                 }
                 if ( logFixup )
                     logFixup(fixupLoc, newValue);
@@ -1273,29 +1286,31 @@ void MachOLoaded::fixupAllChainedFixups(Diagnostics& diag, const dyld_chained_st
 }
 #endif
 
-bool MachOLoaded::walkChain(Diagnostics& diag, const dyld_chained_starts_in_segment* segInfo, uint32_t pageIndex, uint16_t offsetInPage,
-                            bool notifyNonPointers, void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, const dyld_chained_starts_in_segment* segInfo, bool& stop)) const
+
+bool MachOLoaded::walkChain(Diagnostics& diag, ChainedFixupPointerOnDisk* chain, uint16_t pointer_format, bool notifyNonPointers, uint32_t max_valid_pointer,
+                            void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, bool& stop)) const
 {
-    bool                       stop = false;
-    uint8_t*                   pageContentStart = (uint8_t*)this + segInfo->segment_offset + (pageIndex * segInfo->page_size);
-    ChainedFixupPointerOnDisk* chain = (ChainedFixupPointerOnDisk*)(pageContentStart+offsetInPage);
-    bool                       chainEnd = false;
+    const unsigned stride = ChainedFixupPointerOnDisk::strideSize(pointer_format);
+    bool  stop = false;
+    bool  chainEnd = false;
     while (!stop && !chainEnd) {
         // copy chain content, in case handler modifies location to final value
         ChainedFixupPointerOnDisk chainContent = *chain;
-        handler(chain, segInfo, stop);
+        handler(chain, stop);
         if ( !stop ) {
-            switch (segInfo->pointer_format) {
+            switch (pointer_format) {
                 case DYLD_CHAINED_PTR_ARM64E:
+                case DYLD_CHAINED_PTR_ARM64E_KERNEL:
                 case DYLD_CHAINED_PTR_ARM64E_USERLAND:
+                case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
+                case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
                     if ( chainContent.arm64e.rebase.next == 0 )
                         chainEnd = true;
                     else
-                        chain = (ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.arm64e.rebase.next*8);
+                        chain = (ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.arm64e.rebase.next*stride);
                     break;
                 case DYLD_CHAINED_PTR_64:
                 case DYLD_CHAINED_PTR_64_OFFSET:
-                case DYLD_CHAINED_PTR_ARM64E_OFFSET:
                     if ( chainContent.generic64.rebase.next == 0 )
                         chainEnd = true;
                     else
@@ -1307,15 +1322,28 @@ bool MachOLoaded::walkChain(Diagnostics& diag, const dyld_chained_starts_in_segm
                     else {
                         chain = (ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.generic32.rebase.next*4);
                         if ( !notifyNonPointers ) {
-                            while ( (chain->generic32.rebase.bind == 0) && (chain->generic32.rebase.target > segInfo->max_valid_pointer) ) {
+                            while ( (chain->generic32.rebase.bind == 0) && (chain->generic32.rebase.target > max_valid_pointer) ) {
                                 // not a real pointer, but a non-pointer co-opted into chain
                                 chain = (ChainedFixupPointerOnDisk*)((uint8_t*)chain + chain->generic32.rebase.next*4);
                             }
                         }
                     }
                     break;
+                case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
+                case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
+                    if ( chainContent.kernel64.next == 0 )
+                        chainEnd = true;
+                    else
+                        chain = (ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.kernel64.next*stride);
+                    break;
+                case DYLD_CHAINED_PTR_32_FIRMWARE:
+                    if ( chainContent.firmware32.next == 0 )
+                        chainEnd = true;
+                    else
+                        chain = (ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.firmware32.next*4);
+                    break;
                 default:
-                    diag.error("unknown pointer format 0x%04X", segInfo->pointer_format);
+                    diag.error("unknown pointer format 0x%04X", pointer_format);
                     stop = true;
             }
         }
@@ -1323,35 +1351,70 @@ bool MachOLoaded::walkChain(Diagnostics& diag, const dyld_chained_starts_in_segm
     return stop;
 }
 
-void MachOLoaded::forEachFixupInAllChains(Diagnostics& diag, const dyld_chained_starts_in_image* starts, bool notifyNonPointers,
-                                          void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, const dyld_chained_starts_in_segment* segInfo, bool& stop)) const
+void MachOLoaded::forEachFixupChainSegment(Diagnostics& diag, const dyld_chained_starts_in_image* starts,
+                                           void (^handler)(const dyld_chained_starts_in_segment* segInfo, uint32_t segIndex, bool& stop)) const
 {
     bool stopped = false;
     for (uint32_t segIndex=0; segIndex < starts->seg_count && !stopped; ++segIndex) {
         if ( starts->seg_info_offset[segIndex] == 0 )
             continue;
         const dyld_chained_starts_in_segment* segInfo = (dyld_chained_starts_in_segment*)((uint8_t*)starts + starts->seg_info_offset[segIndex]);
-        for (uint32_t pageIndex=0; pageIndex < segInfo->page_count && !stopped; ++pageIndex) {
-            uint16_t offsetInPage = segInfo->page_start[pageIndex];
-            if ( offsetInPage == DYLD_CHAINED_PTR_START_NONE )
-                continue;
-            if ( offsetInPage & DYLD_CHAINED_PTR_START_MULTI ) {
-                // 32-bit chains which may need multiple starts per page
-                uint32_t overflowIndex = offsetInPage & ~DYLD_CHAINED_PTR_START_MULTI;
-                bool chainEnd = false;
-                while (!stopped && !chainEnd) {
-                    chainEnd = (segInfo->page_start[overflowIndex] & DYLD_CHAINED_PTR_START_LAST);
-                    offsetInPage = (segInfo->page_start[overflowIndex] & ~DYLD_CHAINED_PTR_START_LAST);
-                    if ( walkChain(diag, segInfo, pageIndex, offsetInPage, notifyNonPointers, handler) )
-                        stopped = true;
-                    ++overflowIndex;
-                }
-            }
-            else {
-                // one chain per page
-                walkChain(diag, segInfo, pageIndex, offsetInPage, notifyNonPointers, handler);
+        handler(segInfo, segIndex, stopped);
+    }
+}
+
+void MachOLoaded::forEachFixupInSegmentChains(Diagnostics& diag, const dyld_chained_starts_in_segment* segInfo, bool notifyNonPointers,
+                                              void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, const dyld_chained_starts_in_segment* segInfo, bool& stop)) const
+{
+    auto adaptor = ^(ChainedFixupPointerOnDisk* fixupLocation, bool& stop) {
+         handler(fixupLocation, segInfo, stop);
+    };
+    bool stopped = false;
+    for (uint32_t pageIndex=0; pageIndex < segInfo->page_count && !stopped; ++pageIndex) {
+        uint16_t offsetInPage = segInfo->page_start[pageIndex];
+        if ( offsetInPage == DYLD_CHAINED_PTR_START_NONE )
+            continue;
+        if ( offsetInPage & DYLD_CHAINED_PTR_START_MULTI ) {
+            // 32-bit chains which may need multiple starts per page
+            uint32_t overflowIndex = offsetInPage & ~DYLD_CHAINED_PTR_START_MULTI;
+            bool chainEnd = false;
+            while (!stopped && !chainEnd) {
+                chainEnd = (segInfo->page_start[overflowIndex] & DYLD_CHAINED_PTR_START_LAST);
+                offsetInPage = (segInfo->page_start[overflowIndex] & ~DYLD_CHAINED_PTR_START_LAST);
+                uint8_t* pageContentStart = (uint8_t*)this + segInfo->segment_offset + (pageIndex * segInfo->page_size);
+                ChainedFixupPointerOnDisk* chain = (ChainedFixupPointerOnDisk*)(pageContentStart+offsetInPage);
+                stopped = walkChain(diag, chain, segInfo->pointer_format, notifyNonPointers, segInfo->max_valid_pointer, adaptor);
+                ++overflowIndex;
             }
         }
+        else {
+            // one chain per page
+            uint8_t* pageContentStart = (uint8_t*)this + segInfo->segment_offset + (pageIndex * segInfo->page_size);
+            ChainedFixupPointerOnDisk* chain = (ChainedFixupPointerOnDisk*)(pageContentStart+offsetInPage);
+            stopped = walkChain(diag, chain, segInfo->pointer_format, notifyNonPointers, segInfo->max_valid_pointer, adaptor);
+        }
+    }
+}
+
+void MachOLoaded::forEachFixupInAllChains(Diagnostics& diag, const dyld_chained_starts_in_image* starts, bool notifyNonPointers,
+                                          void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, const dyld_chained_starts_in_segment* segInfo, bool& stop)) const
+{
+    bool stopped = false;
+    for (uint32_t segIndex=0; segIndex < starts->seg_count && !stopped; ++segIndex) {
+        if ( starts->seg_info_offset[segIndex] == 0 )
+            continue;
+        const dyld_chained_starts_in_segment* segInfo = (dyld_chained_starts_in_segment*)((uint8_t*)starts + starts->seg_info_offset[segIndex]);
+        forEachFixupInSegmentChains(diag, segInfo, notifyNonPointers, handler);
+    }
+}
+
+void MachOLoaded::forEachFixupInAllChains(Diagnostics& diag, uint16_t pointer_format, uint32_t starts_count, const uint32_t chain_starts[],
+                                          void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, bool& stop)) const
+{
+    for (uint32_t i=0; i < starts_count; ++i) {
+        ChainedFixupPointerOnDisk* chain = (ChainedFixupPointerOnDisk*)((uint8_t*)this + chain_starts[i]);
+        if ( walkChain(diag, chain, pointer_format, false, 0, handler) )
+            break;
     }
 }
 
index e23f890aee922bb023f9fe11b3d470d857af5e92..f53c22c7f4f39efe2e0b00c738e1eed58a505206 100644 (file)
@@ -79,6 +79,7 @@ struct VIS_HIDDEN MachOLoaded : public MachOFile
                                                   Array<const void*> bindTargets, void (^fixupLogger)(void* loc, void* newValue)) const;
 #endif
 
+    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;
 
 
     // For use with new rebase/bind scheme were each fixup location on disk contains info on what
@@ -90,11 +91,15 @@ struct VIS_HIDDEN MachOLoaded : public MachOFile
             dyld_chained_ptr_arm64e_auth_bind   authBind;
             dyld_chained_ptr_arm64e_rebase      rebase;
             dyld_chained_ptr_arm64e_bind        bind;
+            dyld_chained_ptr_arm64e_bind24      bind24;
+            dyld_chained_ptr_arm64e_auth_bind24 authBind24;
 
             uint64_t        signExtendedAddend() const;
             uint64_t        unpackTarget() const;
             const char*     keyName() const;
             uint64_t        signPointer(void* loc, uint64_t target) const;
+            static uint64_t    signPointer(uint64_t unsignedPtr, void* loc, bool addrDiv, uint16_t diversity, uint8_t key);
+            static const char* keyName(uint8_t keyBits);
         };
 
         union Generic64 {
@@ -112,19 +117,27 @@ struct VIS_HIDDEN MachOLoaded : public MachOFile
             uint64_t        signExtendedAddend() const;
         };
 
+        struct Kernel64 : dyld_chained_ptr_64_kernel_cache_rebase {
+            const char* keyName() const;
+        };
+
+        struct Firm32 : dyld_chained_ptr_32_firmware_rebase { };
+
         typedef dyld_chained_ptr_32_cache_rebase Cache32;
 
         uint64_t            raw64;
         Arm64e              arm64e;
         Generic64           generic64;
+        Kernel64            kernel64;
 
         uint32_t            raw32;
         Generic32           generic32;
         Cache32             cache32;
-
+        Firm32              firmware32;
 
         bool                isRebase(uint16_t pointerFormat, uint64_t preferedLoadAddress, uint64_t& targetRuntimeOffset) const;
-        bool                isBind(uint16_t pointerFormat, uint32_t& bindOrdinal) const;
+        bool                isBind(uint16_t pointerFormat, uint32_t& bindOrdinal, int64_t& addend) const;
+        static unsigned     strideSize(uint16_t pointerFormat);
     };
 
 
@@ -135,6 +148,7 @@ struct VIS_HIDDEN MachOLoaded : public MachOFile
         uint32_t     linkeditFileOffset;
         uint32_t     linkeditFileSize;
         uint32_t     linkeditSegIndex;
+        uint32_t     lastSegIndex;
     };
 
     struct LinkEditInfo
@@ -152,9 +166,19 @@ struct VIS_HIDDEN MachOLoaded : public MachOFile
     };
     void                    getLinkEditPointers(Diagnostics& diag, LinkEditInfo&) const;
 
-    // use by dyldinfo
+    void forEachFixupChainSegment(Diagnostics& diag, const dyld_chained_starts_in_image* starts,
+                                  void (^handler)(const dyld_chained_starts_in_segment* segInfo, uint32_t segIndex, bool& stop)) const;
+
+    void forEachFixupInSegmentChains(Diagnostics& diag, const dyld_chained_starts_in_segment* segInfo, bool notifyNonPointers,
+                                     void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, const dyld_chained_starts_in_segment* segInfo, bool& stop)) const;
+
+    // for dyld loaded images
     void                    forEachFixupInAllChains(Diagnostics& diag, const dyld_chained_starts_in_image* starts, bool notifyNonPointers,
                                                     void (^callback)(ChainedFixupPointerOnDisk* fixupLocation, const dyld_chained_starts_in_segment* segInfo, bool& stop)) const;
+    // for preload images
+    void                    forEachFixupInAllChains(Diagnostics& diag, uint16_t pointer_format, uint32_t starts_count, const uint32_t chain_starts[],
+                                                    void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, bool& stop)) const;
+
 protected:
     friend SharedCacheBuilder;
 
@@ -175,8 +199,8 @@ protected:
     void                    getLayoutInfo(LayoutInfo&) const;
     const uint8_t*          getLinkEditContent(const LayoutInfo& info, uint32_t fileOffset) const;
     const uint8_t*          getExportsTrie(const LinkEditInfo& info, uint64_t& trieSize) 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;
 
@@ -186,9 +210,8 @@ protected:
     // On all other platforms this always returns a single best cd hash (ranked to match the kernel).
     // Note the callback parameter is really a CS_CodeDirectory.
     void                    forEachCodeDirectoryBlob(const void* codeSigStart, size_t codeSignLen, void (^callback)(const void* cd)) const;
-    bool                    walkChain(Diagnostics& diag, const dyld_chained_starts_in_segment* segInfo, uint32_t pageIndex, uint16_t offsetInPage,
-                                      bool notifyNonPointers, void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, const dyld_chained_starts_in_segment* segInfo, bool& stop)) const;
-
+    bool                    walkChain(Diagnostics& diag, ChainedFixupPointerOnDisk* start, uint16_t pointer_format, bool notifyNonPointers, uint32_t max_valid_pointer,
+                                        void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, bool& stop)) const;
 
 };
 
index 0c219700e54e575ce289e96ba876652dd2a629ed..4949b63905a1b016f5ed7eebbafe93cc3bdd3c1d 100644 (file)
 #include <limits.h>
 #include <sys/errno.h>
 #include <unistd.h>
+#include <TargetConditionals.h>
 
 #include <initializer_list>
 
 #include "PathOverrides.h"
 
 
-
 namespace dyld3 {
 namespace closure {
 
@@ -94,7 +94,7 @@ PathOverrides::~PathOverrides()
 
 void PathOverrides::forEachInsertedDylib(void (^handler)(const char* dylibPath, bool &stop)) const
 {
-    if ( _insertedDylibs != nullptr ) {
+    if ( _insertedDylibs != nullptr && _insertedDylibs[0] != '\0' ) {
         forEachInColonList(_insertedDylibs, nullptr, ^(const char* path, bool &stop) {
             handler(path, stop);
         });
@@ -401,6 +401,25 @@ void PathOverrides::forEachPathVariant(const char* initialPath, bool pathIsInDyl
             return;
     }
 
+    // try rootpath
+    bool searchiOSSupport = (platform == Platform::iOSMac);
+#if (TARGET_OS_OSX && TARGET_CPU_ARM64)
+    if ( platform == Platform::iOS ) {
+        searchiOSSupport = true;
+        // <rdar://problem/58959974> some old Almond apps reference old WebKit location
+        if ( strcmp(initialPath, "/System/Library/PrivateFrameworks/WebKit.framework/WebKit") == 0 )
+            initialPath = "/System/Library/Frameworks/WebKit.framework/WebKit";
+    }
+#endif
+    if ( searchiOSSupport ) {
+        char rtpath[strlen("/System/iOSSupport")+strlen(initialPath)+8];
+        strcpy(rtpath, "/System/iOSSupport");
+        strcat(rtpath, initialPath);
+        forEachImageSuffix(rtpath, false, pathIsInDyldCacheWhichCannotBeOverridden, stop, handler);
+        if ( stop )
+            return;
+    }
+
     // try original path
     forEachImageSuffix(initialPath, false, pathIsInDyldCacheWhichCannotBeOverridden, stop, handler);
     if ( stop )
@@ -533,7 +552,7 @@ const char* PathPool::add(const char* path)
     return _next->add(path);
 }
 
-void PathPool::forEachPath(void (^handler)(const char* path))
+void PathPool::forEachPath(void (^handler)(const char* path)) const
 {
     for (const char* s = _buffer; s < _current; ++s) {
         handler(s);
@@ -544,6 +563,20 @@ void PathPool::forEachPath(void (^handler)(const char* path))
         _next->forEachPath(handler);
 }
 
+bool PathPool::contains(const char* path) const
+{
+    for (const char* s = _buffer; s < _current; ++s) {
+        if ( strcmp(s, path) == 0 )
+            return true;
+        s += strlen(s);
+    }
+
+    if ( _next != nullptr )
+        return _next->contains(path);
+
+    return false;
+}
+
 
 
 } // namespace closure
index 1478f3656eba4c915c5a369928ead9c410de60f6..81251abe033a65596d6c5e60769c4dd473d6a81c 100644 (file)
@@ -42,7 +42,8 @@ public:
     static PathPool*    allocate();
     static void         deallocate(PathPool* pool);
     const char*         add(const char* path);
-    void                forEachPath(void (^handler)(const char* path));
+    void                forEachPath(void (^handler)(const char* path)) const;
+    bool                contains(const char* path) const;
 
 private:
     enum { kAllocationSize = 32*1024 };
diff --git a/dyld3/RootsChecker.cpp b/dyld3/RootsChecker.cpp
new file mode 100644 (file)
index 0000000..765b45d
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+* Copyright (c) 2020 Apple Inc. All rights reserved.
+*
+* @APPLE_LICENSE_HEADER_START@
+*
+* This file contains Original Code and/or Modifications of Original Code
+* as defined in and that are subject to the Apple Public Source License
+* Version 2.0 (the 'License'). You may not use this file except in
+* compliance with the License. Please obtain a copy of the License at
+* http://www.opensource.apple.com/apsl/ and read it before using this
+* file.
+*
+* The Original Code and all software distributed under the License are
+* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+* Please see the License for the specific language governing rights and
+* limitations under the License.
+*
+* @APPLE_LICENSE_HEADER_END@
+*/
+
+#include <TargetConditionals.h>
+
+#include "ClosureFileSystemPhysical.h"
+#include "DyldSharedCache.h"
+#include "MachOAnalyzer.h"
+#include "RootsChecker.h"
+
+#if DYLD_SIMULATOR_ROOTS_SUPPORT
+#include "SharedCacheRuntime.h"
+#endif
+
+namespace dyld3 {
+
+#if DYLD_SIMULATOR_ROOTS_SUPPORT
+static bool imageUUIDMatchesSharedCache(const char* path, const closure::FileSystem* fileSystem,
+                                       const DyldSharedCache* cache, const closure::Image* image) {
+    // We can only use the cache if the file UUID matches the shared cache
+    bool uuidMatchesCache = false;
+
+    // The simulator can't pass in a file system as its not hooked up to the vector.
+    // They can just pass in nullptr where needed and we'll assume its the physical file system
+    closure::FileSystemPhysical fileSystemPhysical;
+    if ( fileSystem == nullptr )
+        fileSystem = &fileSystemPhysical;
+
+    Diagnostics diag;
+    Platform platform = cache->platform();
+    const GradedArchs& archs = GradedArchs::forName(cache->archName(), true);
+    char realerPath[MAXPATHLEN];
+    dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, *fileSystem, path, archs, platform, realerPath);
+    const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
+    if ( ma != nullptr ) {
+        uuid_t uuid;
+        uuid_t image_uuid;
+        if ( !ma->getUuid(uuid) )
+            memset(&uuid, 0, sizeof(uuid_t));
+        if ( !image->getUuid(image_uuid) )
+            memset(&image_uuid, 0, sizeof(uuid_t));
+        uuidMatchesCache = ( memcmp(uuid, image_uuid, sizeof(uuid_t)) == 0 );
+        fileSystem->unloadFile(loadedFileInfo);
+    }
+    return uuidMatchesCache;
+}
+
+bool RootsChecker::uuidMatchesSharedCache(const char* path, const closure::FileSystem* fileSystem,
+                                          const DyldSharedCache* cache) {
+    const dyld3::closure::ImageArray* images = cache->cachedDylibsImageArray();
+    const closure::Image* image = nullptr;
+    uint32_t imageIndex;
+    if ( cache->hasImagePath(path, imageIndex) ) {
+        image = images->imageForNum(imageIndex+1);
+    }
+    return imageUUIDMatchesSharedCache(path, fileSystem, cache, image);
+}
+#endif
+
+bool RootsChecker::onDiskFileIsRoot(const char* path, const DyldSharedCache* cache,
+                                    const closure::Image* image,
+                                    const closure::FileSystem* fileSystem,
+                                    uint64_t inode, uint64_t modtime) const {
+
+    // Upon entry, we know the dylib exists and has a mod time and inode.  Now
+    // we need to see if its a root or not
+
+    // Note we don't check if dylibs are expected on disk here.  We assume that an
+    // image only has a mod time and inode if its expected on disk
+    uint64_t expectedINode;
+    uint64_t expectedMtime;
+    if ( image->hasFileModTimeAndInode(expectedINode, expectedMtime) ) {
+        if ( (expectedMtime == modtime) && (expectedINode == inode) )
+            return false;
+        // mod time or inode don't match, so this is a root
+        return true;
+    }
+
+#if DYLD_SIMULATOR_ROOTS_SUPPORT
+    if ( strcmp(path, "/usr/lib/system/libsystem_kernel.dylib") == 0 ) {
+        // If this was a root when launchd checked, then assume we are a root now
+        if ( libsystemKernelIsRoot )
+            return true;
+
+        // If the file system is read-only, then this cannot be a root now
+        if ( !fileSystemCanBeModified )
+            return false;
+
+        // Possibly a root.  Open the file and check
+        return !imageUUIDMatchesSharedCache(path, fileSystem, cache, image);
+    } else if ( strcmp(path, "/usr/lib/system/libsystem_platform.dylib") == 0 ) {
+        // If this was a root when launchd checked, then assume we are a root now
+        if ( libsystemPlatformIsRoot )
+            return true;
+
+        // If the file system is read-only, then this cannot be a root now
+        if ( !fileSystemCanBeModified )
+            return false;
+
+        // Possibly a root.  Open the file and check
+        return !imageUUIDMatchesSharedCache(path, fileSystem, cache, image);
+    } else if ( strcmp(path, "/usr/lib/system/libsystem_pthread.dylib") == 0 ) {
+        // If this was a root when launchd checked, then assume we are a root now
+        if ( libsystemPThreadIsRoot )
+            return true;
+
+        // If the file system is read-only, then this cannot be a root now
+        if ( !fileSystemCanBeModified )
+            return false;
+
+        // Possibly a root.  Open the file and check
+        return !imageUUIDMatchesSharedCache(path, fileSystem, cache, image);
+    }
+#endif
+
+    // If we aren't a special simulator dylib, then we must be a root
+    return true;
+}
+
+} // namespace dyld3
diff --git a/dyld3/RootsChecker.h b/dyld3/RootsChecker.h
new file mode 100644 (file)
index 0000000..87e7195
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2020 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, 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_ROOTS_CHECKER_H__
+#define __DYLD_ROOTS_CHECKER_H__
+
+#include <TargetConditionals.h>
+
+#if TARGET_OS_OSX && (BUILDING_DYLD || BUILDING_CLOSURE_UTIL) && !TARGET_OS_SIMULATOR
+#define DYLD_SIMULATOR_ROOTS_SUPPORT 1
+#else
+#define DYLD_SIMULATOR_ROOTS_SUPPORT 0
+#endif
+
+class DyldSharedCache;
+
+namespace dyld3 {
+
+namespace closure {
+class FileSystem;
+struct Image;
+}
+
+#define VIS_HIDDEN __attribute__((visibility("hidden")))
+
+class VIS_HIDDEN RootsChecker
+{
+public:
+    RootsChecker() = default;
+
+    // Does a on-disk binary represent a root.
+    // If the cache expects dylibs on disk, then so long as the dylib matches
+    // the mod time and inode in the cache, it is not a root.
+    // For the macOS simulator support dylibs, which are on disk, whether they
+    // are roots depends on the state we are tracking in the comm page.
+    // For all other dylibs and all other platforms, on-disk dylibs are always roots
+    bool onDiskFileIsRoot(const char* path, const DyldSharedCache* cache, const closure::Image* image,
+                          const closure::FileSystem* fileSystem,
+                          uint64_t inode, uint64_t modtime) const;
+
+#if DYLD_SIMULATOR_ROOTS_SUPPORT
+    static bool uuidMatchesSharedCache(const char* path, const closure::FileSystem* fileSystem,
+                                       const DyldSharedCache* cache);
+
+    void setLibsystemKernelIsRoot(bool v) {
+        libsystemKernelIsRoot = v;
+    }
+    void setLibsystemPlatformIsRoot(bool v) {
+        libsystemPlatformIsRoot = v;
+    }
+    void setLibsystemPThreadIsRoot(bool v) {
+        libsystemPThreadIsRoot = v;
+    }
+    void setFileSystemCanBeModified(bool v) {
+        fileSystemCanBeModified = v;
+    }
+#endif
+
+private:
+    // By default, assume nothing is a root, but that the file system is writable.
+    // This should be conservatively correct.
+#if DYLD_SIMULATOR_ROOTS_SUPPORT
+    bool libsystemKernelIsRoot      = false;
+    bool libsystemPlatformIsRoot    = false;
+    bool libsystemPThreadIsRoot     = false;
+    bool fileSystemCanBeModified    = true;
+#endif
+};
+
+} // namespace dyld3
+
+#endif // __DYLD_ROOTS_CHECKER_H__
index 3adaf482b1bae0b04189ed1c599b7037139b55e1..8c03f04114890296585477184ad0854e68cabba6 100644 (file)
@@ -26,6 +26,7 @@
 #include <stdint.h>
 #include <string.h>
 #include <unistd.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <sys/param.h>
 #include <sys/types.h>
 // should be in mach/shared_region.h
 extern "C" int __shared_region_check_np(uint64_t* startaddress);
 extern "C" int __shared_region_map_and_slide_np(int fd, uint32_t count, const shared_file_mapping_np mappings[], long slide, const dyld_cache_slide_info2* slideInfo, size_t slideInfoSize);
+extern "C" int __shared_region_map_and_slide_2_np(uint32_t files_count, const shared_file_np files[], uint32_t mappings_count, const shared_file_mapping_slide_np mappings[]);
 
+#ifndef VM_PROT_NOAUTH
+#define VM_PROT_NOAUTH  0x40  /* must not interfere with normal prot assignments */
+#endif
 
 namespace dyld {
-    extern int  my_stat(const char* path, struct stat* buf);
-    extern int  my_open(const char* path, int flag, int other);
     extern void log(const char*, ...);
+    extern void logToConsole(const char* format, ...);
+#if defined(__x86_64__) && !TARGET_OS_SIMULATOR
+    bool isTranslated();
+#endif
 }
 
 
@@ -67,13 +74,13 @@ namespace dyld3 {
 
 struct CacheInfo
 {
-    int                     fd;
-    shared_file_mapping_np  mappings[3];
-    uint64_t                slideInfoAddressUnslid;
-    size_t                  slideInfoSize;
-    uint64_t                sharedRegionStart;
-    uint64_t                sharedRegionSize;
-    uint64_t                maxSlide;
+    shared_file_mapping_slide_np            mappings[DyldSharedCache::MaxMappings];
+    uint32_t                                mappingsCount;
+    // All mappings come from the same file
+    int                                     fd               = 0;
+    uint64_t                                sharedRegionStart;
+    uint64_t                                sharedRegionSize;
+    uint64_t                                maxSlide;
 };
 
 
@@ -169,57 +176,123 @@ static void rebaseChainV4(uint8_t* pageContent, uint16_t startOffset, uintptr_t
 }
 #endif
 
-static void getCachePath(const SharedCacheOptions& options, size_t pathBufferSize, char pathBuffer[])
-{
+#if TARGET_OS_OSX
+bool getMacOSCachePath(char pathBuffer[], size_t pathBufferSize,
+                       const char* cacheDir, bool useHaswell) {
+    // Clear old attempts at finding a cache, if any
+    pathBuffer[0] = '\0';
+
     // set cache dir
-    if ( options.cacheDirOverride != nullptr ) {
-        strlcpy(pathBuffer, options.cacheDirOverride, pathBufferSize);
-    }
-    else {
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
-        strlcpy(pathBuffer, IPHONE_DYLD_SHARED_CACHE_DIR, sizeof(IPHONE_DYLD_SHARED_CACHE_DIR));
-#else
-        strlcpy(pathBuffer, MACOSX_DYLD_SHARED_CACHE_DIR, sizeof(MACOSX_DYLD_SHARED_CACHE_DIR));
-#endif
-    }
+    strlcpy(pathBuffer, cacheDir, pathBufferSize);
 
     // append file component of cache file
     if ( pathBuffer[strlen(pathBuffer)-1] != '/' )
         strlcat(pathBuffer, "/", pathBufferSize);
-#if __x86_64__ && !__IPHONE_OS_VERSION_MIN_REQUIRED
-    if ( options.useHaswell ) {
+
+#if __x86_64__
+    if ( useHaswell ) {
         size_t len = strlen(pathBuffer);
         struct stat haswellStatBuf;
         strlcat(pathBuffer, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME_H, pathBufferSize);
-        if ( dyld::my_stat(pathBuffer, &haswellStatBuf) == 0 )
-            return;
+        if ( dyld3::stat(pathBuffer, &haswellStatBuf) == 0 )
+            return true;
         // no haswell cache file, use regular x86_64 cache
         pathBuffer[len] = '\0';
     }
 #endif
 
+    struct stat statBuf;
+    strlcat(pathBuffer, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, pathBufferSize);
+    if ( dyld3::stat(pathBuffer, &statBuf) == 0 )
+        return true;
+
+    return false;
+}
+#endif // TARGET_OS_OSX
+
+static void getCachePath(const SharedCacheOptions& options, size_t pathBufferSize, char pathBuffer[])
+{
+#if TARGET_OS_OSX
+
+    if ( options.cacheDirOverride != nullptr ) {
+        getMacOSCachePath(pathBuffer, pathBufferSize, options.cacheDirOverride, options.useHaswell);
+    } else {
+        getMacOSCachePath(pathBuffer, pathBufferSize, MACOSX_MRM_DYLD_SHARED_CACHE_DIR, options.useHaswell);
+    }
+
+#else // TARGET_OS_OSX
+
+    // Non-macOS path
+    if ( options.cacheDirOverride != nullptr ) {
+        strlcpy(pathBuffer, options.cacheDirOverride, pathBufferSize);
+    } else {
+        strlcpy(pathBuffer, IPHONE_DYLD_SHARED_CACHE_DIR, sizeof(IPHONE_DYLD_SHARED_CACHE_DIR));
+    }
+
+    // append file component of cache file
+    if ( pathBuffer[strlen(pathBuffer)-1] != '/' )
+        strlcat(pathBuffer, "/", pathBufferSize);
+
     strlcat(pathBuffer, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, pathBufferSize);
 
-#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR
+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
     // use .development cache if it exists
-    struct stat enableStatBuf;
+    if ( BootArgs::forceCustomerCache() ) {
+        // The boot-arg always wins.  Use the customer cache if we are told to
+        return;
+    }
+    if ( !dyld3::internalInstall() ) {
+        // We can't use the development cache on customer installs
+        return;
+    }
+    if ( BootArgs::forceDevelopmentCache() ) {
+        // The boot-arg always wins.  Use the development cache if we are told to
+        strlcat(pathBuffer, DYLD_SHARED_CACHE_DEVELOPMENT_EXT, pathBufferSize);
+        return;
+    }
+
+    // If only one or the other caches exists, then use the one we have
     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 ( !BootArgs::forceCustomerCache() && developmentDevice && ((enableFileExists && (enableStatBuf.st_size < ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE) && devCacheExists) || !optCacheExists) )
+    bool devCacheExists = (dyld3::stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME DYLD_SHARED_CACHE_DEVELOPMENT_EXT, &devCacheStatBuf) == 0);
+    bool optCacheExists = (dyld3::stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &optCacheStatBuf) == 0);
+    if ( !devCacheExists ) {
+        // If the dev cache doesn't exist, then use the customer cache
+        return;
+    }
+    if ( !optCacheExists ) {
+        // If the customer cache doesn't exist, then use the development cache
         strlcat(pathBuffer, DYLD_SHARED_CACHE_DEVELOPMENT_EXT, pathBufferSize);
+        return;
+    }
+
+    // Finally, check for the sentinels
+    struct stat enableStatBuf;
+    //struct stat sentinelStatBuf;
+    bool enableFileExists = (dyld3::stat(IPHONE_DYLD_SHARED_CACHE_DIR "enable-dylibs-to-override-cache", &enableStatBuf) == 0);
+    // FIXME: rdar://problem/59813537 Re-enable once automation is updated to use boot-arg
+    bool sentinelFileExists = false;
+    //bool sentinelFileExists = (dyld3::stat(MACOSX_MRM_DYLD_SHARED_CACHE_DIR "enable_development_mode", &sentinelStatBuf) == 0);
+    if ( enableFileExists && (enableStatBuf.st_size < ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE) ) {
+        // if the old enable file exists, use the development cache
+        strlcat(pathBuffer, DYLD_SHARED_CACHE_DEVELOPMENT_EXT, pathBufferSize);
+        return;
+    }
+    if ( sentinelFileExists ) {
+        // If the new sentinel exists, then use the development cache
+        strlcat(pathBuffer, DYLD_SHARED_CACHE_DEVELOPMENT_EXT, pathBufferSize);
+        return;
+    }
 #endif
 
+#endif //!TARGET_OS_OSX
 }
 
 
 int openSharedCacheFile(const SharedCacheOptions& options, SharedCacheLoadInfo* results)
 {
     getCachePath(options, sizeof(results->path), results->path);
-    return dyld::my_open(results->path, O_RDONLY, 0);
+    return dyld3::open(results->path, O_RDONLY, 0);
 }
 
 static bool validMagic(const SharedCacheOptions& options, const DyldSharedCache* cache)
@@ -258,15 +331,29 @@ static bool validPlatform(const SharedCacheOptions& options, const DyldSharedCac
 }
 
 #if !TARGET_OS_SIMULATOR
-static void verboseSharedCacheMappings(const shared_file_mapping_np mappings[3])
+static void verboseSharedCacheMappings(const shared_file_mapping_slide_np mappings[DyldSharedCache::MaxMappings],
+                                       uint32_t mappingsCount)
 {
-    for (int i=0; i < 3; ++i) {
-        dyld::log("        0x%08llX->0x%08llX init=%x, max=%x %s%s%s\n",
-            mappings[i].sfm_address, mappings[i].sfm_address+mappings[i].sfm_size-1,
-            mappings[i].sfm_init_prot, mappings[i].sfm_init_prot,
-            ((mappings[i].sfm_init_prot & VM_PROT_READ) ? "read " : ""),
-            ((mappings[i].sfm_init_prot & VM_PROT_WRITE) ? "write " : ""),
-            ((mappings[i].sfm_init_prot & VM_PROT_EXECUTE) ? "execute " : ""));
+    for (int i=0; i < mappingsCount; ++i) {
+        const char* mappingName = "";
+        if ( mappings[i].sms_init_prot & VM_PROT_WRITE ) {
+            if ( mappings[i].sms_init_prot & VM_PROT_NOAUTH ) {
+                // __DATA*
+                mappingName = "data";
+            } else {
+                // __AUTH*
+                mappingName = "auth";
+            }
+        }
+        uint32_t init_prot = mappings[i].sms_init_prot & (VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE);
+        uint32_t max_prot = mappings[i].sms_max_prot & (VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE);
+        dyld::log("        0x%08llX->0x%08llX init=%x, max=%x %s%s%s%s\n",
+            mappings[i].sms_address, mappings[i].sms_address+mappings[i].sms_size-1,
+            init_prot, max_prot,
+            ((mappings[i].sms_init_prot & VM_PROT_READ) ? "read " : ""),
+            ((mappings[i].sms_init_prot & VM_PROT_WRITE) ? "write " : ""),
+            ((mappings[i].sms_init_prot & VM_PROT_EXECUTE) ? "execute " : ""),
+            mappingName);
     }
 }
 #endif
@@ -282,7 +369,7 @@ static bool preflightCacheFile(const SharedCacheOptions& options, SharedCacheLoa
     }
 
     struct stat cacheStatBuf;
-    if ( dyld::my_stat(results->path, &cacheStatBuf) != 0 ) {
+    if ( dyld3::stat(results->path, &cacheStatBuf) != 0 ) {
         results->errorMessage = "shared cache file stat() failed";
         ::close(fd);
         return false;
@@ -307,39 +394,41 @@ static bool preflightCacheFile(const SharedCacheOptions& options, SharedCacheLoa
         ::close(fd);
         return false;
     }
-    if ( (cache->header.mappingCount != 3) || (cache->header.mappingOffset > 0x148) ) {
+    if ( (cache->header.mappingCount < 3) || (cache->header.mappingCount > DyldSharedCache::MaxMappings) || (cache->header.mappingOffset > 0x168) ) {
         results->errorMessage = "shared cache file mappings are invalid";
         ::close(fd);
         return false;
     }
     const dyld_cache_mapping_info* const fileMappings = (dyld_cache_mapping_info*)&firstPage[cache->header.mappingOffset];
-    if (  (fileMappings[0].fileOffset != 0)
-      || ((fileMappings[0].address + fileMappings[0].size) > fileMappings[1].address)
-      || ((fileMappings[1].address + fileMappings[1].size) > fileMappings[2].address)
-      || ((fileMappings[0].fileOffset + fileMappings[0].size) != fileMappings[1].fileOffset)
-      || ((fileMappings[1].fileOffset + fileMappings[1].size) != fileMappings[2].fileOffset)
+    const dyld_cache_mapping_info* textMapping = &fileMappings[0];
+    const dyld_cache_mapping_info* firstDataMapping = &fileMappings[1];
+    const dyld_cache_mapping_info* linkeditMapping = &fileMappings[cache->header.mappingCount - 1];
+    if (  (textMapping->fileOffset != 0)
+      || ((fileMappings[0].address + fileMappings[0].size) > firstDataMapping->address)
+      || ((fileMappings[0].fileOffset + fileMappings[0].size) != firstDataMapping->fileOffset)
       || ((cache->header.codeSignatureOffset + cache->header.codeSignatureSize) != cacheFileLength)
-      || (fileMappings[0].maxProt != (VM_PROT_READ|VM_PROT_EXECUTE))
-      || (fileMappings[1].maxProt != (VM_PROT_READ|VM_PROT_WRITE))
-      || (fileMappings[2].maxProt != VM_PROT_READ) ) {
-        results->errorMessage = "shared cache file mappings are invalid";
+      || (textMapping->maxProt != (VM_PROT_READ|VM_PROT_EXECUTE))
+      || (linkeditMapping->maxProt != VM_PROT_READ) ) {
+        results->errorMessage = "shared cache text/linkedit mappings are invalid";
         ::close(fd);
         return false;
     }
 
-    if ( cache->header.mappingOffset >= 0xF8 ) {
-        if ( (fileMappings[0].address != cache->header.sharedRegionStart) || ((fileMappings[2].address + fileMappings[2].size) > (cache->header.sharedRegionStart+cache->header.sharedRegionSize)) ) {
-            results->errorMessage = "shared cache file mapping addressses invalid";
+    // Check the __DATA mappings
+    for (unsigned i = 1; i != (cache->header.mappingCount - 1); ++i) {
+        if ( ((fileMappings[i].address + fileMappings[i].size) > fileMappings[i + 1].address)
+          || ((fileMappings[i].fileOffset + fileMappings[i].size) != fileMappings[i + 1].fileOffset)
+          || (fileMappings[i].maxProt != (VM_PROT_READ|VM_PROT_WRITE)) ) {
+            results->errorMessage = "shared cache data mappings are invalid";
             ::close(fd);
             return false;
         }
     }
-    else {
-        if ( (fileMappings[0].address != SHARED_REGION_BASE) || ((fileMappings[2].address + fileMappings[2].size) > (SHARED_REGION_BASE+SHARED_REGION_SIZE)) ) {
-            results->errorMessage = "shared cache file mapping addressses invalid";
-            ::close(fd);
-            return false;
-        }
+
+    if ( (textMapping->address != cache->header.sharedRegionStart) || ((linkeditMapping->address + linkeditMapping->size) > (cache->header.sharedRegionStart+cache->header.sharedRegionSize)) ) {
+        results->errorMessage = "shared cache file mapping addressses invalid";
+        ::close(fd);
+        return false;
     }
 
     // register code signature of cache file
@@ -375,28 +464,52 @@ static bool preflightCacheFile(const SharedCacheOptions& options, SharedCacheLoa
     ::munmap(mappedData, sizeof(firstPage));
 
     // fill out results
-    info->fd = fd;
-    for (int i=0; i < 3; ++i) {
-        info->mappings[i].sfm_address       = fileMappings[i].address;
-        info->mappings[i].sfm_size          = fileMappings[i].size;
-        info->mappings[i].sfm_file_offset   = fileMappings[i].fileOffset;
-        info->mappings[i].sfm_max_prot      = fileMappings[i].maxProt;
-        info->mappings[i].sfm_init_prot     = fileMappings[i].initProt;
-    }
-    info->mappings[1].sfm_max_prot  |= VM_PROT_SLIDE;
-    info->mappings[1].sfm_init_prot |= VM_PROT_SLIDE;
-    info->slideInfoAddressUnslid  = fileMappings[2].address + cache->header.slideInfoOffset - fileMappings[2].fileOffset;
-    info->slideInfoSize           = (long)cache->header.slideInfoSize;
-    if ( cache->header.mappingOffset >= 0xf8 ) {
-        info->sharedRegionStart = cache->header.sharedRegionStart;
-        info->sharedRegionSize  = cache->header.sharedRegionSize;
-        info->maxSlide          = cache->header.maxSlide;
-    }
-    else {
-        info->sharedRegionStart = SHARED_REGION_BASE;
-        info->sharedRegionSize  = SHARED_REGION_SIZE;
-        info->maxSlide          = SHARED_REGION_SIZE - (fileMappings[2].address + fileMappings[2].size - fileMappings[0].address);
+    info->mappingsCount = cache->header.mappingCount;
+    // We have to emit the mapping for the __LINKEDIT before the slid mappings
+    // This is so that the kernel has already mapped __LINKEDIT in to its address space
+    // for when it copies the slid info for each __DATA mapping
+    for (int i=0; i < cache->header.mappingCount; ++i) {
+        uint64_t    slideInfoFileOffset = 0;
+        uint64_t    slideInfoFileSize   = 0;
+        vm_prot_t   authProt            = 0;
+        if ( cache->header.mappingOffset <= __offsetof(dyld_cache_header, mappingWithSlideOffset) ) {
+            // Old cache without the new slid mappings
+            if ( i == 1 ) {
+                // Add slide info to the __DATA mapping
+                slideInfoFileOffset = cache->header.slideInfoOffsetUnused;
+                slideInfoFileSize   = cache->header.slideInfoSizeUnused;
+                // Don't set auth prot to anything interseting on the old mapppings
+                authProt = 0;
+            }
+        } else {
+            // New cache where each mapping has a corresponding slid mapping
+            const dyld_cache_mapping_and_slide_info* slidableMappings = (const dyld_cache_mapping_and_slide_info*)&firstPage[cache->header.mappingWithSlideOffset];
+            slideInfoFileOffset = slidableMappings[i].slideInfoFileOffset;
+            slideInfoFileSize   = slidableMappings[i].slideInfoFileSize;
+            if ( (slidableMappings[i].flags & DYLD_CACHE_MAPPING_AUTH_DATA) == 0 )
+                authProt = VM_PROT_NOAUTH;
+        }
+
+        // Add a file for each mapping
+        info->fd                        = fd;
+        info->mappings[i].sms_address               = fileMappings[i].address;
+        info->mappings[i].sms_size                  = fileMappings[i].size;
+        info->mappings[i].sms_file_offset           = fileMappings[i].fileOffset;
+        info->mappings[i].sms_slide_size            = 0;
+        info->mappings[i].sms_slide_start           = 0;
+        info->mappings[i].sms_max_prot              = fileMappings[i].maxProt;
+        info->mappings[i].sms_init_prot             = fileMappings[i].initProt;
+        if ( slideInfoFileSize != 0 ) {
+            uint64_t offsetInLinkEditRegion = (slideInfoFileOffset - linkeditMapping->fileOffset);
+            info->mappings[i].sms_slide_start   = (user_addr_t)(linkeditMapping->address + offsetInLinkEditRegion);
+            info->mappings[i].sms_slide_size    = (user_addr_t)slideInfoFileSize;
+            info->mappings[i].sms_init_prot    |= (VM_PROT_SLIDE | authProt);
+            info->mappings[i].sms_max_prot     |= (VM_PROT_SLIDE | authProt);
+        }
     }
+    info->sharedRegionStart = cache->header.sharedRegionStart;
+    info->sharedRegionSize  = cache->header.sharedRegionSize;
+    info->maxSlide          = cache->header.maxSlide;
     return true;
 }
 
@@ -404,14 +517,10 @@ static bool preflightCacheFile(const SharedCacheOptions& options, SharedCacheLoa
 #if !TARGET_OS_SIMULATOR
 
 // update all __DATA pages with slide info
-static bool rebaseDataPages(bool isVerbose, CacheInfo& info, SharedCacheLoadInfo* results)
+static bool rebaseDataPages(bool isVerbose, const dyld_cache_slide_info* slideInfo, const uint8_t *dataPagesStart,
+                            uint64_t sharedRegionStart, 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;
+    const dyld_cache_slide_info* slideInfoHeader = slideInfo;
     if ( slideInfoHeader != nullptr ) {
         if ( slideInfoHeader->version == 2 ) {
             const dyld_cache_slide_info2* slideHeader = (dyld_cache_slide_info2*)slideInfo;
@@ -450,6 +559,7 @@ static bool rebaseDataPages(bool isVerbose, CacheInfo& info, SharedCacheLoadInfo
              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];
+                 //dyld::log("page[%d]: page_starts[i]=0x%04X\n", i, delta);
                  if ( delta == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE )
                      continue;
                  delta = delta/sizeof(uint64_t); // initial offset is byte based
@@ -459,7 +569,7 @@ static bool rebaseDataPages(bool isVerbose, CacheInfo& info, SharedCacheLoadInfo
                      delta = loc->plain.offsetToNextPointer;
                      if ( loc->auth.authenticated ) {
 #if __has_feature(ptrauth_calls)
-                        uint64_t target = info.sharedRegionStart + loc->auth.offsetFromSharedCacheBase + results->slide;
+                        uint64_t target = sharedRegionStart + loc->auth.offsetFromSharedCacheBase + results->slide;
                         MachOLoaded::ChainedFixupPointerOnDisk ptr;
                         ptr.raw64 = *((uint64_t*)loc);
                         loc->raw = ptr.arm64e.signPointer(loc, target);
@@ -534,12 +644,26 @@ static bool reuseExistingCache(const SharedCacheOptions& options, SharedCacheLoa
             if ( options.verbose ) {
                 const shared_file_mapping_np* const mappings = (shared_file_mapping_np*)(cacheBaseAddress + existingCache->header.mappingOffset);
                 dyld::log("re-using existing shared cache (%s):\n", results->path);
-                shared_file_mapping_np slidMappings[3];
-                for (int i=0; i < 3; ++i) {
-                    slidMappings[i] = mappings[i];
-                    slidMappings[i].sfm_address += results->slide;
+                shared_file_mapping_slide_np slidMappings[DyldSharedCache::MaxMappings];
+                for (int i=0; i < DyldSharedCache::MaxMappings; ++i) {
+                    slidMappings[i].sms_address = mappings[i].sfm_address;
+                    slidMappings[i].sms_size = mappings[i].sfm_size;
+                    slidMappings[i].sms_file_offset = mappings[i].sfm_file_offset;
+                    slidMappings[i].sms_max_prot = mappings[i].sfm_max_prot;
+                    slidMappings[i].sms_init_prot = mappings[i].sfm_init_prot;
+
+                    slidMappings[i].sms_address += results->slide;
+                    if ( existingCache->header.mappingOffset > __offsetof(dyld_cache_header, mappingWithSlideOffset) ) {
+                        // New caches have slide info on each new mapping
+                        const dyld_cache_mapping_and_slide_info* const slidableMappings = (dyld_cache_mapping_and_slide_info*)(cacheBaseAddress + existingCache->header.mappingWithSlideOffset);
+                        assert(existingCache->header.mappingWithSlideCount <= DyldSharedCache::MaxMappings);
+                        if ( !(slidableMappings[i].flags & DYLD_CACHE_MAPPING_AUTH_DATA) ) {
+                            slidMappings[i].sms_max_prot  |= VM_PROT_NOAUTH;
+                            slidMappings[i].sms_init_prot |= VM_PROT_NOAUTH;
+                        }
+                    }
                 }
-                verboseSharedCacheMappings(slidMappings);
+                verboseSharedCacheMappings(slidMappings, existingCache->header.mappingCount);
             }
         }
         else {
@@ -550,24 +674,28 @@ static bool reuseExistingCache(const SharedCacheOptions& options, SharedCacheLoa
     return false;
 }
 
-static long pickCacheASLR(CacheInfo& info)
+static long pickCacheASLRSlide(CacheInfo& info)
 {
     // choose new random slide
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
+#if TARGET_OS_IPHONE || (TARGET_OS_OSX && TARGET_CPU_ARM64)
     // <rdar://problem/20848977> change shared cache slide for 32-bit arm to always be 16k aligned
-    long slide = ((arc4random() % info.maxSlide) & (-16384));
+    long slide;
+    if (info.maxSlide == 0)
+        slide = 0;
+    else
+        slide = ((arc4random() % info.maxSlide) & (-16384));
 #else
-    long slide = ((arc4random() % info.maxSlide) & (-4096));
-#endif
-
-    // <rdar://problem/32031197> respect -disable_aslr boot-arg
-    if ( BootArgs::contains("-disable_aslr") )
+    long slide;
+    if (info.maxSlide == 0)
         slide = 0;
-
-    // update mappings
-    for (uint32_t i=0; i < 3; ++i) {
-        info.mappings[i].sfm_address += slide;
+    else
+        slide = ((arc4random() % info.maxSlide) & (-4096));
+#if defined(__x86_64__) && !TARGET_OS_SIMULATOR
+    if (dyld::isTranslated()) {
+        slide &= (-16384);
     }
+#endif
+#endif
     
     return slide;
 }
@@ -578,16 +706,56 @@ static bool mapCacheSystemWide(const SharedCacheOptions& options, SharedCacheLoa
     if ( !preflightCacheFile(options, results, &info) )
         return false;
 
-    const dyld_cache_slide_info2* slideInfo = nullptr;
-    if ( info.slideInfoSize != 0 ) {
-        results->slide = pickCacheASLR(info);
-        slideInfo = (dyld_cache_slide_info2*)(info.slideInfoAddressUnslid + results->slide);
+    int result = 0;
+    if ( info.mappingsCount != 3 ) {
+        uint32_t maxSlide = options.disableASLR ? 0 : (uint32_t)info.maxSlide;
+
+        shared_file_np file;
+        file.sf_fd = info.fd;
+        file.sf_mappings_count = info.mappingsCount;
+        // For the new syscall, this is actually the max slide.  The kernel now owns the actual slide
+        file.sf_slide = maxSlide;
+        result = __shared_region_map_and_slide_2_np(1, &file, info.mappingsCount, info.mappings);
+    } else {
+        // With the old syscall, dyld has to choose the slide
+        results->slide = options.disableASLR ? 0 : pickCacheASLRSlide(info);
+
+        // update mappings based on the slide we choose
+        for (uint32_t i=0; i < info.mappingsCount; ++i) {
+            info.mappings[i].sms_address += results->slide;
+            if ( info.mappings[i].sms_slide_size != 0 )
+                info.mappings[i].sms_slide_start += (uint32_t)results->slide;
+        }
+
+        // If we get here then we don't have the new kernel function, so use the old one
+        const dyld_cache_slide_info2*   slideInfo       = nullptr;
+        size_t                          slideInfoSize   = 0;
+        shared_file_mapping_np mappings[3];
+        for (unsigned i = 0; i != 3; ++i) {
+            mappings[i].sfm_address         = info.mappings[i].sms_address;
+            mappings[i].sfm_size            = info.mappings[i].sms_size;
+            mappings[i].sfm_file_offset     = info.mappings[i].sms_file_offset;
+            mappings[i].sfm_max_prot        = info.mappings[i].sms_max_prot;
+            mappings[i].sfm_init_prot       = info.mappings[i].sms_init_prot;
+            if ( info.mappings[i].sms_slide_size != 0 ) {
+                slideInfo       = (dyld_cache_slide_info2*)info.mappings[i].sms_slide_start;
+                slideInfoSize   = (size_t)info.mappings[i].sms_slide_size;
+            }
+        }
+        result = __shared_region_map_and_slide_np(info.fd, 3, mappings, results->slide, slideInfo, slideInfoSize);
     }
 
-    int result = __shared_region_map_and_slide_np(info.fd, 3, info.mappings, results->slide, slideInfo, info.slideInfoSize);
     ::close(info.fd);
     if ( result == 0 ) {
-        results->loadAddress = (const DyldSharedCache*)(info.mappings[0].sfm_address);
+        results->loadAddress = (const DyldSharedCache*)(info.mappings[0].sms_address);
+        if ( info.mappingsCount != 3 ) {
+            // We don't know our own slide any more as the kernel owns it, so ask for it again now
+            if ( reuseExistingCache(options, results) )
+                return true;
+            // Uh oh, we mapped the kernel, but we didn't find the slide
+            dyld::logToConsole("dyld: error finding shared cache slide for system wide mapping\n");
+            return false;
+        }
     }
     else {
         // could be another process beat us to it
@@ -601,7 +769,7 @@ static bool mapCacheSystemWide(const SharedCacheOptions& options, SharedCacheLoa
 
     if ( options.verbose ) {
         dyld::log("mapped dyld cache file system wide: %s\n", results->path);
-        verboseSharedCacheMappings(info.mappings);
+        verboseSharedCacheMappings(info.mappings, info.mappingsCount);
     }
     return true;
 }
@@ -616,12 +784,18 @@ static bool mapCachePrivate(const SharedCacheOptions& options, SharedCacheLoadIn
 
     // compute ALSR slide
     results->slide = 0;
-#if !TARGET_OS_SIMULATOR // simulator caches do not support sliding
-    if ( info.slideInfoSize != 0 ) {
-        results->slide = pickCacheASLR(info);
-    }
+#if !TARGET_OS_SIMULATOR
+    results->slide = options.disableASLR ? 0 : pickCacheASLRSlide(info);
 #endif
-    results->loadAddress = (const DyldSharedCache*)(info.mappings[0].sfm_address);
+
+    // update mappings
+    for (uint32_t i=0; i < info.mappingsCount; ++i) {
+        info.mappings[i].sms_address += (uint32_t)results->slide;
+        if ( info.mappings[i].sms_slide_size != 0 )
+            info.mappings[i].sms_slide_start += (uint32_t)results->slide;
+    }
+
+    results->loadAddress = (const DyldSharedCache*)(info.mappings[0].sms_address);
 
     // deallocate any existing system wide shared cache
     deallocateExistingSharedCache();
@@ -634,18 +808,18 @@ static bool mapCachePrivate(const SharedCacheOptions& options, SharedCacheLoadIn
 #endif
 
     // map cache just for this process with mmap()
-    for (int i=0; i < 3; ++i) {
-        void* mmapAddress = (void*)(uintptr_t)(info.mappings[i].sfm_address);
-        size_t size = (size_t)(info.mappings[i].sfm_size);
+    for (int i=0; i < info.mappingsCount; ++i) {
+        void* mmapAddress = (void*)(uintptr_t)(info.mappings[i].sms_address);
+        size_t size = (size_t)(info.mappings[i].sms_size);
         //dyld::log("dyld: mapping address %p with size 0x%08lX\n", mmapAddress, size);
         int protection = 0;
-        if ( info.mappings[i].sfm_init_prot & VM_PROT_EXECUTE )
+        if ( info.mappings[i].sms_init_prot & VM_PROT_EXECUTE )
             protection   |= PROT_EXEC;
-        if ( info.mappings[i].sfm_init_prot & VM_PROT_READ )
+        if ( info.mappings[i].sms_init_prot & VM_PROT_READ )
             protection   |= PROT_READ;
-        if ( info.mappings[i].sfm_init_prot & VM_PROT_WRITE )
+        if ( info.mappings[i].sms_init_prot & VM_PROT_WRITE )
             protection   |= PROT_WRITE;
-        off_t offset = info.mappings[i].sfm_file_offset;
+        off_t offset = info.mappings[i].sms_file_offset;
         if ( ::mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, info.fd, offset) != mmapAddress ) {
             // failed to map some chunk of this shared cache file
             // clear shared region
@@ -662,11 +836,18 @@ static bool mapCachePrivate(const SharedCacheOptions& options, SharedCacheLoadIn
 #if TARGET_OS_SIMULATOR // simulator caches do not support sliding
     return true;
 #else
-    bool success = rebaseDataPages(options.verbose, info, results);
+    __block bool success = true;
+    for (int i=0; i < info.mappingsCount; ++i) {
+        if ( info.mappings[i].sms_slide_size == 0 )
+            continue;
+        const dyld_cache_slide_info* slideInfoHeader = (const dyld_cache_slide_info*)info.mappings[i].sms_slide_start;
+        const uint8_t* mappingPagesStart = (const uint8_t*)info.mappings[i].sms_address;
+        success &= rebaseDataPages(options.verbose, slideInfoHeader, mappingPagesStart, info.sharedRegionStart, results);
+    }
 
     if ( options.verbose ) {
         dyld::log("mapped dyld cache file private to process (%s):\n", results->path);
-        verboseSharedCacheMappings(info.mappings);
+        verboseSharedCacheMappings(info.mappings, info.mappingsCount);
     }
     return success;
 #endif
@@ -710,7 +891,7 @@ bool findInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dyl
 
     if ( loadInfo.loadAddress->header.formatVersion != dyld3::closure::kFormatVersion ) {
         // support for older cache with a different Image* format
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
+#if TARGET_OS_IPHONE
         uint64_t hash = 0;
         for (const char* s=dylibPathToFind; *s != '\0'; ++s)
                 hash += hash*4 + *s;
@@ -718,7 +899,7 @@ bool findInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dyl
         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
+#if TARGET_OS_IPHONE
             // on iOS, inode is used to hold hash of path
             if ( (p->modTime == 0) && (p->inode != hash) )
                 continue;
@@ -741,34 +922,7 @@ bool findInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dyl
     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 ( results->image == nullptr )
         return false;
 
@@ -793,7 +947,7 @@ void deallocateExistingSharedCache()
 #if TARGET_OS_SIMULATOR
     // dyld deallocated macOS shared cache before jumping into dyld_sim
 #else
-    // <rdar://problem/5077374> remove the shared region sub-map
+    // <rdar://problem/50773474> remove the shared region sub-map
     uint64_t existingCacheAddress = 0;
     if ( __shared_region_check_np(&existingCacheAddress) == 0 ) {
         ::mmap((void*)((long)SHARED_REGION_BASE), SHARED_REGION_SIZE, PROT_NONE, MAP_FIXED | MAP_PRIVATE| MAP_ANON, 0, 0);
index 7c06e6d4b24fed5d1c3f1c44e294a1e537deb901..fdbd5fe750b475e23c295c99991de37c2728cce4 100644 (file)
@@ -38,6 +38,7 @@ struct SharedCacheOptions {
     bool            forcePrivate;
     bool            useHaswell;
     bool            verbose;
+    bool            disableASLR;
 };
 
 struct SharedCacheLoadInfo {
index 425c23f57a2dbe7fcd15aa2383b28f77dfdfcc9c..ef3d5e146bae9ad42feff03b7a0eb5b857dd3c7f 100644 (file)
@@ -25,6 +25,8 @@
 #ifndef _DYLD_SUPPORTED_ARCHS_H_
 #define _DYLD_SUPPORTED_ARCHS_H_
 
+#include <TargetConditionals.h>
+
 
 
 #endif // _DYLD_SUPPORTED_ARCHS_H_
diff --git a/dyld3/dyld_app_cache_util.cpp b/dyld3/dyld_app_cache_util.cpp
new file mode 100644 (file)
index 0000000..0fe8495
--- /dev/null
@@ -0,0 +1,1801 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <list>
+#include <map>
+#include <vector>
+
+#include <variant>
+
+#include <CoreFoundation/CFBundle.h>
+#include <CoreFoundation/CFPropertyList.h>
+#include <CoreFoundation/CFStream.h>
+
+#include "kernel_collection_builder.h"
+#include "ClosureFileSystemPhysical.h"
+#include "FileUtils.h"
+#include "JSONWriter.h"
+#include "MachOAppCache.h"
+
+using namespace dyld3::json;
+
+using dyld3::closure::FileSystemPhysical;
+using dyld3::closure::LoadedFileInfo;
+using dyld3::GradedArchs;
+using dyld3::MachOAnalyzer;
+using dyld3::MachOAppCache;
+using dyld3::Platform;
+using dyld3::json::Node;
+
+__attribute__((noreturn))
+static void exit_usage(const char* missingOption = nullptr) {
+    if ( missingOption != nullptr ) {
+        fprintf(stderr, "Missing option '%s'\n", missingOption);
+        fprintf(stderr, "\n");
+    }
+
+    fprintf(stderr, "Usage: dyld_app_cache_util [-layout] [-entrypoint] [-fixups] [-symbols] [-kmod] [-uuid] [-fips] -arch arch -platform platform -app-cache app-cache-path\n");
+    fprintf(stderr, "  -layout          print the layout of an existing app cache\n");
+    fprintf(stderr, "  -entrypoint      print the entrypoint of an existing app cache\n");
+    fprintf(stderr, "  -fixups          print the fixups of an existing app cache\n");
+    fprintf(stderr, "  -symbols         print the symbols of an existing app cache\n");
+    fprintf(stderr, "  -kmod            print the kmod_info of an existing app cache\n");
+    fprintf(stderr, "  -uuid            print the UUID of an existing app cache\n");
+    fprintf(stderr, "  -fips            print the FIPS section of an existing app cache\n");
+    fprintf(stderr, "\n");
+
+    fprintf(stderr, "Usage: dyld_app_cache_util -validate file-path -arch arch -platform platform\n");
+    fprintf(stderr, "  -validate        the path to check is valid for inserting in to an app cache\n");
+    fprintf(stderr, "\n");
+
+    fprintf(stderr, "Usage: dyld_app_cache_util -list-bundles directory-path\n");
+    fprintf(stderr, "  -list-bundles    the directory to index for bundles\n");
+    fprintf(stderr, "\n");
+
+    fprintf(stderr, "Usage: dyld_app_cache_util -create-kernel-collection kernel-collection -kernel kernel-path [-extensions path-to-extensions] [-bundle-id bundle-id]*\n");
+    fprintf(stderr, "  -create-kernel-collection    create a kernel collection and write to the given path\n");
+    fprintf(stderr, "  -kernel                      path to the kernel static executable\n");
+    fprintf(stderr, "  -extensions                  path to the kernel extensions directory\n");
+    fprintf(stderr, "  -bundle-id                   zero or more bundle-ids to link in to the kernel collection\n");
+    fprintf(stderr, "  -sectcreate                  segment name, section name, and payload file path for more data to embed in the kernel\n");
+    fprintf(stderr, "\n");
+
+    fprintf(stderr, "Usage: dyld_app_cache_util -create-pageable-kernel-collection aux-kernel-collection -kernel-collection kernel-collection-path [-extensions path-to-extensions] [-bundle-id bundle-id]*\n");
+    fprintf(stderr, "  -create-pageable-kernel-collection   create a pageable kernel collection and write to the given path\n");
+    fprintf(stderr, "  -kernel-collection                   path to the kernel collection collection\n");
+    fprintf(stderr, "  -extensions                          path to the kernel extensions directory\n");
+    fprintf(stderr, "  -bundle-id                           zero or more bundle-ids to link in to the kernel collection\n");
+    fprintf(stderr, "\n");
+
+    fprintf(stderr, "Usage: dyld_app_cache_util -create-aux-kernel-collection aux-kernel-collection -kernel-collection kernel-collection-path [-extensions path-to-extensions] [-bundle-id bundle-id]*\n");
+    fprintf(stderr, "  -create-aux-kernel-collection    create an aux kernel collection and write to the given path\n");
+    fprintf(stderr, "  -kernel-collection               path to the kernel collection\n");
+    fprintf(stderr, "  -pageable-collection             path to the pageable collection\n");
+    fprintf(stderr, "  -extensions                      path to the kernel extensions directory\n");
+    fprintf(stderr, "  -bundle-id                       zero or more bundle-ids to link in to the kernel collection\n");
+    fprintf(stderr, "\n");
+
+    fprintf(stderr, "Common options:\n");
+    fprintf(stderr, "  -arch xxx        the arch to use to create the app cache\n");
+    fprintf(stderr, "  -platform xxx    the platform to use to create the app cache\n");
+
+    exit(1);
+}
+
+struct DumpOptions {
+    bool printLayout            = false;
+    bool printEntryPoint        = false;
+    bool printFixups            = false;
+    bool printSymbols           = false;
+    bool printUUID              = false;
+    bool printKModInfo          = false;
+    bool printFIPS              = false;
+};
+
+struct ValidateOptions {
+    const char* filePath    = nullptr;
+};
+
+struct ListBundlesOptions {
+    const char* directoryPath    = nullptr;
+};
+
+// The payload of -sectcreate
+struct SectionData {
+    const char* segmentName         = nullptr;
+    const char* sectionName         = nullptr;
+    const char* payloadFilePath     = nullptr;
+};
+
+struct CreateKernelCollectionOptions {
+    const char*                             outputCachePath         = nullptr;
+    const char*                             kernelPath              = nullptr;
+    const char*                             kernelCollectionPath    = nullptr;
+    const char*                             pageableCollectionPath  = nullptr;
+    const char*                             extensionsPath          = nullptr;
+    const char*                             volumeRoot              = "";
+    std::vector<const char*>                bundleIDs;
+    bool                                    verbose                 = false;
+    bool                                    printJSONErrors         = false;
+    CollectionKind                          collectionKind          = unknownKC;
+    StripMode                               stripMode               = unknownStripMode;
+    std::vector<SectionData>                sections;
+    const char*                             prelinkInfoExtraData    = nullptr;
+};
+
+typedef std::variant<std::monostate, DumpOptions, ValidateOptions, ListBundlesOptions, CreateKernelCollectionOptions> OptionsVariants;
+
+struct CommonOptions {
+    const char*                 appCachePath        = nullptr;
+    std::vector<const char*>    archs;
+    const char*                 platform            = nullptr;
+};
+
+CommonOptions gOpts;
+
+template<typename T>
+static T& exitOrGetState(OptionsVariants& options, const char* argv) {
+    if (std::holds_alternative<std::monostate>(options)) {
+        return options.emplace<T>();
+    }
+    if (std::holds_alternative<T>(options))
+        return std::get<T>(options);
+    exit_usage();
+}
+
+static bool parseArgs(int argc, const char* argv[], OptionsVariants& options) {
+    for (int i = 1; i < argc; ++i) {
+        const char* arg = argv[i];
+        if (arg[0] != '-') {
+            fprintf(stderr, "unknown option: %s\n", arg);
+            exit_usage();
+        }
+
+        // Common options
+        if (strcmp(arg, "-app-cache") == 0) {
+            if (gOpts.appCachePath != nullptr)
+                exit_usage();
+            gOpts.appCachePath = argv[++i];
+            continue;
+        }
+        if (strcmp(arg, "-arch") == 0) {
+            gOpts.archs.push_back(argv[++i]);
+            continue;
+        }
+        if (strcmp(arg, "-platform") == 0) {
+            if (gOpts.platform != nullptr)
+                exit_usage();
+            gOpts.platform = argv[++i];
+            continue;
+        }
+
+        // DumpOptions
+        if (strcmp(arg, "-layout") == 0) {
+            exitOrGetState<DumpOptions>(options, arg).printLayout = true;
+            continue;
+        }
+        if (strcmp(arg, "-entrypoint") == 0) {
+            exitOrGetState<DumpOptions>(options, arg).printEntryPoint = true;
+            continue;
+        }
+        if (strcmp(arg, "-fixups") == 0) {
+            exitOrGetState<DumpOptions>(options, arg).printFixups = true;
+            continue;
+        }
+        if (strcmp(arg, "-symbols") == 0) {
+            exitOrGetState<DumpOptions>(options, arg).printSymbols = true;
+            continue;
+        }
+        if (strcmp(arg, "-uuid") == 0) {
+            exitOrGetState<DumpOptions>(options, arg).printUUID = true;
+            continue;
+        }
+        if (strcmp(arg, "-kmod") == 0) {
+            exitOrGetState<DumpOptions>(options, arg).printKModInfo = true;
+            continue;
+        }
+        if (strcmp(arg, "-fips") == 0) {
+            exitOrGetState<DumpOptions>(options, arg).printFIPS = true;
+            continue;
+        }
+
+        // ValidateOptions
+        if (strcmp(arg, "-validate") == 0) {
+            exitOrGetState<ValidateOptions>(options, arg).filePath = argv[++i];
+            continue;
+        }
+
+        // ListBundlesOptions
+        if (strcmp(arg, "-list-bundles") == 0) {
+            exitOrGetState<ListBundlesOptions>(options, arg).directoryPath = argv[++i];
+            continue;
+        }
+
+        // CreateKernelCollectionOptions
+        if (strcmp(arg, "-create-kernel-collection") == 0) {
+            exitOrGetState<CreateKernelCollectionOptions>(options, arg).outputCachePath = argv[++i];
+            exitOrGetState<CreateKernelCollectionOptions>(options, arg).collectionKind = baseKC;
+            continue;
+        }
+        if (strcmp(arg, "-kernel") == 0) {
+            exitOrGetState<CreateKernelCollectionOptions>(options, arg).kernelPath = argv[++i];
+            continue;
+        }
+        if (strcmp(arg, "-create-pageable-kernel-collection") == 0) {
+            exitOrGetState<CreateKernelCollectionOptions>(options, arg).outputCachePath = argv[++i];
+            exitOrGetState<CreateKernelCollectionOptions>(options, arg).collectionKind = pageableKC;
+            continue;
+        }
+        if (strcmp(arg, "-create-aux-kernel-collection") == 0) {
+            exitOrGetState<CreateKernelCollectionOptions>(options, arg).outputCachePath = argv[++i];
+            exitOrGetState<CreateKernelCollectionOptions>(options, arg).collectionKind = auxKC;
+            continue;
+        }
+        if (strcmp(arg, "-kernel-collection") == 0) {
+            exitOrGetState<CreateKernelCollectionOptions>(options, arg).kernelCollectionPath = argv[++i];
+            continue;
+        }
+        if (strcmp(arg, "-pageable-collection") == 0) {
+            exitOrGetState<CreateKernelCollectionOptions>(options, arg).pageableCollectionPath = argv[++i];
+            continue;
+        }
+        if (strcmp(arg, "-extensions") == 0) {
+            exitOrGetState<CreateKernelCollectionOptions>(options, arg).extensionsPath = argv[++i];
+            continue;
+        }
+        if (strcmp(arg, "-volume-root") == 0) {
+            exitOrGetState<CreateKernelCollectionOptions>(options, arg).volumeRoot = argv[++i];
+            continue;
+        }
+        if (strcmp(arg, "-bundle-id") == 0) {
+            exitOrGetState<CreateKernelCollectionOptions>(options, arg).bundleIDs.push_back(argv[++i]);
+            continue;
+        }
+        if (strcmp(arg, "-verbose") == 0) {
+            exitOrGetState<CreateKernelCollectionOptions>(options, arg).verbose = true;
+            continue;
+        }
+        if (strcmp(arg, "-json-errors") == 0) {
+            exitOrGetState<CreateKernelCollectionOptions>(options, arg).printJSONErrors = true;
+            continue;
+        }
+        if (strcmp(arg, "-strip-all") == 0) {
+            exitOrGetState<CreateKernelCollectionOptions>(options, arg).stripMode = stripAll;
+            continue;
+        }
+        if (strcmp(arg, "-strip-all-kexts") == 0) {
+            exitOrGetState<CreateKernelCollectionOptions>(options, arg).stripMode = stripAllKexts;
+            continue;
+        }
+        if (strcmp(arg, "-sectcreate") == 0) {
+            const char* segmentName = argv[++i];
+            const char* sectionName = argv[++i];
+            const char* payloadFilePath = argv[++i];
+            SectionData sectData = { segmentName, sectionName, payloadFilePath };
+                exitOrGetState<CreateKernelCollectionOptions>(options, arg).sections.push_back(sectData);
+            continue;
+        }
+        if (strcmp(arg, "-prelink-info-extra") == 0) {
+            const char* payloadFilePath = argv[++i];
+            exitOrGetState<CreateKernelCollectionOptions>(options, arg).prelinkInfoExtraData = payloadFilePath;
+            continue;
+        }
+
+
+        fprintf(stderr, "unknown option: %s\n", arg);
+        exit_usage();
+    }
+
+    return true;
+}
+
+static Platform stringToPlatform(const std::string& str) {
+    if (str == "unknown")
+        return Platform::unknown;
+    if (str == "macOS")
+        return Platform::macOS;
+    if (str == "iOS")
+        return Platform::iOS;
+    if (str == "tvOS")
+        return Platform::tvOS;
+    if (str == "watchOS")
+        return Platform::watchOS;
+    if (str == "bridgeOS")
+        return Platform::bridgeOS;
+    if (str == "iOSMac")
+        return Platform::iOSMac;
+    if (str == "UIKitForMac")
+        return Platform::iOSMac;
+    if (str == "iOS_simulator")
+        return Platform::iOS_simulator;
+    if (str == "tvOS_simulator")
+        return Platform::tvOS_simulator;
+    if (str == "watchOS_simulator")
+        return Platform::watchOS_simulator;
+    return Platform::unknown;
+}
+
+static int dumpAppCache(const DumpOptions& options) {
+    // Verify any required options
+    if (gOpts.archs.size() != 1)
+        exit_usage("-arch");
+    if (gOpts.platform == nullptr)
+        exit_usage("-platform");
+
+    if (gOpts.appCachePath == nullptr)
+        exit_usage();
+
+    FileSystemPhysical fileSystem;
+    if (!fileSystem.fileExists(gOpts.appCachePath)) {
+        fprintf(stderr, "App-cache path does not exist: %s\n", gOpts.appCachePath);
+        return 1;
+    }
+
+    const GradedArchs& archs = GradedArchs::forName(gOpts.archs[0]);
+    Platform platform = Platform::unknown;
+    bool isKernelCollection = false;
+
+    // HACK: Pass a real option for building a kernel app cache
+    if (!strcmp(gOpts.platform, "kernel")) {
+        isKernelCollection = true;
+    } else {
+        platform = stringToPlatform(gOpts.platform);
+        if (platform == Platform::unknown) {
+            fprintf(stderr, "Could not create app cache because: unknown platform '%s'\n", gOpts.platform);
+            return 1;
+        }
+    }
+
+    __block Diagnostics diag;
+    char appCacheRealPath[MAXPATHLEN];
+    LoadedFileInfo loadedFileInfo = MachOAnalyzer::load(diag, fileSystem, gOpts.appCachePath, archs, platform, appCacheRealPath);
+    if (diag.hasError()) {
+        fprintf(stderr, "Could not load app cache because: %s\n", diag.errorMessage().c_str());
+        return 1;
+    }
+
+    MachOAppCache* appCacheMA = (MachOAppCache*)loadedFileInfo.fileContent;
+    if (appCacheMA == nullptr) {
+        fprintf(stderr, "Could not load app cache: %s\n", gOpts.appCachePath);
+        return 1;
+    }
+
+    if (options.printLayout) {
+        __block Node topNode;
+
+        // Add the segments for the app cache
+        __block Node segmentsNode;
+        __block bool hasError = false;
+        appCacheMA->forEachSegment(^(const dyld3::MachOFile::SegmentInfo &info, bool &stop) {
+            Node segmentNode;
+            segmentNode.map["name"] = makeNode(info.segName);
+            segmentNode.map["vmAddr"] = makeNode(hex(info.vmAddr));
+            segmentNode.map["vmSize"] = makeNode(hex(info.vmSize));
+            segmentNode.map["vmEnd"] = makeNode(hex(info.vmAddr + info.vmSize));
+            switch (info.protections) {
+                case VM_PROT_READ:
+                    segmentNode.map["permissions"] = makeNode("r--");
+                    break;
+                case VM_PROT_WRITE:
+                    segmentNode.map["permissions"] = makeNode("-w-");
+                    break;
+                case VM_PROT_EXECUTE:
+                    segmentNode.map["permissions"] = makeNode("--x");
+                    break;
+                case VM_PROT_READ | VM_PROT_WRITE:
+                    segmentNode.map["permissions"] = makeNode("rw-");
+                    break;
+                case VM_PROT_READ | VM_PROT_EXECUTE:
+                    segmentNode.map["permissions"] = makeNode("r-x");
+                    break;
+                case VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE:
+                    segmentNode.map["permissions"] = makeNode("rwx");
+                    break;
+                default:
+                    fprintf(stderr, "Unknown permissions on segment '%s'\n", info.segName);
+                    hasError = true;
+                    stop = true;
+            }
+
+            __block Node sectionsNode;
+            appCacheMA->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo &sectInfo, bool malformedSectionRange, bool &stop) {
+                if ( strncmp(sectInfo.segInfo.segName, info.segName, 16) != 0 )
+                    return;
+
+                Node sectionNode;
+                sectionNode.map["name"] = makeNode(sectInfo.sectName);
+                sectionNode.map["vmAddr"] = makeNode(hex(sectInfo.sectAddr));
+                sectionNode.map["vmSize"] = makeNode(hex(sectInfo.sectSize));
+                sectionNode.map["vmEnd"] = makeNode(hex(sectInfo.sectAddr + sectInfo.sectSize));
+
+                sectionsNode.array.push_back(sectionNode);
+            });
+
+            if ( !sectionsNode.array.empty() ) {
+                segmentNode.map["sections"] = sectionsNode;
+            }
+
+            segmentsNode.array.push_back(segmentNode);
+        });
+
+        if (hasError)
+            return 1;
+
+        topNode.map["cache-segments"] = segmentsNode;
+
+        // Map from name to relative path
+        __block std::unordered_map<std::string, std::string> relativePaths;
+        appCacheMA->forEachPrelinkInfoLibrary(diag, ^(const char *bundleName, const char* relativePath,
+                                                      const std::vector<const char *> &deps) {
+            if ( relativePath != nullptr )
+                relativePaths[bundleName] = relativePath;
+        });
+
+        __block Node dylibsNode;
+        appCacheMA->forEachDylib(diag, ^(const MachOAnalyzer *ma, const char *name, bool &stop) {
+            __block Node segmentsNode;
+            ma->forEachSegment(^(const dyld3::MachOFile::SegmentInfo &info, bool &stop) {
+                Node segmentNode;
+                segmentNode.map["name"] = makeNode(info.segName);
+                segmentNode.map["vmAddr"] = makeNode(hex(info.vmAddr));
+                segmentNode.map["vmSize"] = makeNode(hex(info.vmSize));
+                segmentNode.map["vmEnd"] = makeNode(hex(info.vmAddr + info.vmSize));
+
+                switch (info.protections) {
+                    case VM_PROT_READ:
+                        segmentNode.map["permissions"] = makeNode("r--");
+                        break;
+                    case VM_PROT_WRITE:
+                        segmentNode.map["permissions"] = makeNode("-w-");
+                        break;
+                    case VM_PROT_EXECUTE:
+                        segmentNode.map["permissions"] = makeNode("--x");
+                        break;
+                    case VM_PROT_READ | VM_PROT_WRITE:
+                        segmentNode.map["permissions"] = makeNode("rw-");
+                        break;
+                    case VM_PROT_READ | VM_PROT_EXECUTE:
+                        segmentNode.map["permissions"] = makeNode("r-x");
+                        break;
+                    default:
+                        fprintf(stderr, "Unknown permissions on segment '%s'\n", info.segName);
+                        hasError = true;
+                        stop = true;
+                }
+
+                __block Node sectionsNode;
+                ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo &sectInfo, bool malformedSectionRange, bool &stop) {
+                    if ( strncmp(sectInfo.segInfo.segName, info.segName, 16) != 0 )
+                        return;
+
+                    Node sectionNode;
+                    sectionNode.map["name"] = makeNode(sectInfo.sectName);
+                    sectionNode.map["vmAddr"] = makeNode(hex(sectInfo.sectAddr));
+                    sectionNode.map["vmSize"] = makeNode(hex(sectInfo.sectSize));
+                    sectionNode.map["vmEnd"] = makeNode(hex(sectInfo.sectAddr + sectInfo.sectSize));
+
+                    sectionsNode.array.push_back(sectionNode);
+                });
+
+                if ( !sectionsNode.array.empty() ) {
+                    segmentNode.map["sections"] = sectionsNode;
+                }
+
+                segmentsNode.array.push_back(segmentNode);
+            });
+
+            Node dylibNode;
+            dylibNode.map["name"] = makeNode(name);
+            dylibNode.map["segments"] = segmentsNode;
+
+            auto relativePathIt = relativePaths.find(name);
+            if ( relativePathIt != relativePaths.end() )
+                dylibNode.map["relativePath"] = makeNode(relativePathIt->second);
+
+            dylibsNode.array.push_back(dylibNode);
+        });
+
+        topNode.map["dylibs"] = dylibsNode;
+
+        printJSON(topNode, 0, std::cout);
+    }
+
+    if (options.printEntryPoint) {
+        __block Node topNode;
+
+        // add entry
+        uint64_t    entryOffset;
+        bool        usesCRT;
+        Node        entryPointNode;
+        if ( appCacheMA->getEntry(entryOffset, usesCRT) ) {
+            entryPointNode.value = hex(appCacheMA->preferredLoadAddress() + entryOffset);
+        }
+
+        topNode.map["entrypoint"] = entryPointNode;
+
+        printJSON(topNode, 0, std::cout);
+    }
+
+    if (options.printFixups) {
+        __block Node topNode;
+
+        __block uint64_t baseAddress = ~0ULL;
+        appCacheMA->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) {
+            baseAddress = std::min(baseAddress, info.vmAddr);
+        });
+        uint64_t cacheBaseAddress = baseAddress;
+        uint64_t textSegVMAddr = appCacheMA->preferredLoadAddress();
+
+        auto getFixupsNode = [cacheBaseAddress, textSegVMAddr](const dyld3::MachOAnalyzer* ma) {
+            __block Node fixupsNode;
+
+            if (!ma->hasChainedFixups()) {
+                return makeNode("none");
+            }
+
+            // Keep track of the fixups seen by chained fixups.  The remainder might be
+            // classic relocs if we are the x86_64 kernel collection
+            __block std::set<uint64_t> seenFixupVMOffsets;
+
+            __block Diagnostics diag;
+            ma->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) {
+                ma->forEachFixupInAllChains(diag, starts, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
+                    uint64_t vmOffset = (uint8_t*)fixupLoc - (uint8_t*)ma;
+                    seenFixupVMOffsets.insert(vmOffset);
+
+                    // Correct for __DATA being before __TEXT, in which case the offset
+                    // is from __DATA, not a mach header offset
+                    vmOffset += (textSegVMAddr - cacheBaseAddress);
+
+                    fixupsNode.map[hex(vmOffset)] = makeNode("fixup");
+                    switch (segInfo->pointer_format) {
+                        case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
+                        case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: {
+                            uint64_t targetVMOffset = fixupLoc->kernel64.target;
+                            uint64_t targetVMAddr = targetVMOffset + cacheBaseAddress;
+                            std::string level = "kc(" + decimal(fixupLoc->kernel64.cacheLevel) + ")";
+                            std::string fixup = level + " + " + hex(targetVMAddr);
+                            if (fixupLoc->kernel64.isAuth) {
+                                fixup += " auth(";
+                                fixup += fixupLoc->kernel64.keyName();
+                                fixup += " ";
+                                fixup += fixupLoc->kernel64.addrDiv ? "addr" : "!addr";
+                                fixup += " ";
+                                fixup += decimal(fixupLoc->kernel64.diversity);
+                                fixup += ")";
+                            }
+                            fixupsNode.map[hex(vmOffset)] = makeNode(fixup);
+                            break;
+                        }
+                        default:
+                            diag.error("unknown pointer type %d", segInfo->pointer_format);
+                            break;
+                    }
+                 });
+            });
+            diag.assertNoError();
+
+            ma->forEachRebase(diag, ^(const char *opcodeName, const dyld3::MachOAnalyzer::LinkEditInfo &leInfo,
+                                      const dyld3::MachOAnalyzer::SegmentInfo *segments,
+                                      bool segIndexSet, uint32_t pointerSize, uint8_t segmentIndex,
+                                      uint64_t segmentOffset, dyld3::MachOAnalyzer::Rebase kind, bool &stop) {
+                uint64_t rebaseVmAddr  = segments[segmentIndex].vmAddr + segmentOffset;
+                uint64_t runtimeOffset = rebaseVmAddr - textSegVMAddr;
+                const uint8_t* fixupLoc = (const uint8_t*)ma + runtimeOffset;
+
+                // Correct for __DATA being before __TEXT, in which case the offset
+                // is from __DATA, not a mach header offset
+                runtimeOffset += (textSegVMAddr - cacheBaseAddress);
+
+                std::string fixup = "kc(0) + ";
+                switch ( kind ) {
+                    case dyld3::MachOAnalyzer::Rebase::unknown:
+                        fixup += " : unhandled";
+                        break;
+                    case dyld3::MachOAnalyzer::Rebase::pointer32: {
+                        uint32_t value = *(uint32_t*)(fixupLoc);
+                        fixup += hex(value) + " : pointer32";
+                        break;
+                    }
+                    case dyld3::MachOAnalyzer::Rebase::pointer64: {
+                        uint64_t value = *(uint64_t*)(fixupLoc);
+                        fixup += hex(value) + " : pointer64";
+                        break;
+                    }
+                    case dyld3::MachOAnalyzer::Rebase::textPCrel32:
+                        fixup += " : pcrel32";
+                        break;
+                    case dyld3::MachOAnalyzer::Rebase::textAbsolute32:
+                        fixup += " : absolute32";
+                        break;
+                }
+                fixupsNode.map[hex(runtimeOffset)] = makeNode(fixup);
+            });
+            diag.assertNoError();
+
+            return fixupsNode;
+        };
+
+        topNode.map["fixups"] = getFixupsNode(appCacheMA);
+
+        __block Node dylibsNode;
+        appCacheMA->forEachDylib(diag, ^(const MachOAnalyzer *ma, const char *name, bool &stop) {
+            Node dylibNode;
+            dylibNode.map["name"] = makeNode(name);
+            dylibNode.map["fixups"] = getFixupsNode(ma);
+
+            dylibsNode.array.push_back(dylibNode);
+        });
+
+        topNode.map["dylibs"] = dylibsNode;
+
+        printJSON(topNode, 0, std::cout);
+    }
+
+    if (options.printSymbols) {
+        __block Node topNode;
+
+        __block Node dylibsNode;
+        appCacheMA->forEachDylib(diag, ^(const MachOAnalyzer *ma, const char *name, bool &stop) {
+            __block Node globalSymbolsNode;
+            ma->forEachGlobalSymbol(diag, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) {
+                Node symbolNode;
+                symbolNode.map["name"] = makeNode(symbolName);
+                symbolNode.map["vmAddr"] = makeNode(hex(n_value));
+
+                globalSymbolsNode.array.push_back(symbolNode);
+            });
+
+            __block Node localSymbolsNode;
+            ma->forEachLocalSymbol(diag, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) {
+                Node symbolNode;
+                symbolNode.map["name"] = makeNode(symbolName);
+                symbolNode.map["vmAddr"] = makeNode(hex(n_value));
+
+                localSymbolsNode.array.push_back(symbolNode);
+            });
+
+            if (globalSymbolsNode.array.empty())
+                globalSymbolsNode = makeNode("none");
+            if (localSymbolsNode.array.empty())
+                localSymbolsNode = makeNode("none");
+
+            Node dylibNode;
+            dylibNode.map["name"] = makeNode(name);
+            dylibNode.map["global-symbols"] = globalSymbolsNode;
+            dylibNode.map["local-symbols"] = localSymbolsNode;
+
+            dylibsNode.array.push_back(dylibNode);
+        });
+
+        topNode.map["dylibs"] = dylibsNode;
+
+        printJSON(topNode, 0, std::cout);
+    }
+
+    if (options.printUUID) {
+        __block Node topNode;
+
+        // add uuid
+        Node uuidNode;
+        uuid_t uuid = {};
+        if ( appCacheMA->getUuid(uuid) ) {
+            uuid_string_t uuidString;
+            uuid_unparse_upper(uuid, uuidString);
+            uuidNode.value = uuidString;
+        }
+
+        topNode.map["uuid"] = uuidNode;
+
+        auto getPlistUUID = ^(const char* jsonNodeName, CFStringRef keyName) {
+            uuid_t uuid = {};
+            const uint8_t* prelinkInfoBuffer = nullptr;
+            uint64_t prelinkInfoBufferSize = 0;
+            prelinkInfoBuffer = (const uint8_t*)appCacheMA->findSectionContent("__PRELINK_INFO", "__info", prelinkInfoBufferSize);
+            if ( prelinkInfoBuffer != nullptr ) {
+                CFReadStreamRef readStreamRef = CFReadStreamCreateWithBytesNoCopy(kCFAllocatorDefault, prelinkInfoBuffer, prelinkInfoBufferSize, kCFAllocatorNull);
+                if ( !CFReadStreamOpen(readStreamRef) ) {
+                    fprintf(stderr, "Could not open plist stream\n");
+                    exit(1);
+                }
+                CFErrorRef errorRef = nullptr;
+                CFPropertyListRef plistRef = CFPropertyListCreateWithStream(kCFAllocatorDefault, readStreamRef, prelinkInfoBufferSize, kCFPropertyListImmutable, nullptr, &errorRef);
+                if ( errorRef != nullptr ) {
+                    CFStringRef stringRef = CFErrorCopyFailureReason(errorRef);
+                    fprintf(stderr, "Could not read plist because: %s\n", CFStringGetCStringPtr(stringRef, kCFStringEncodingASCII));
+                    CFRelease(stringRef);
+                    exit(1);
+                }
+                assert(CFGetTypeID(plistRef) == CFDictionaryGetTypeID());
+                CFDataRef uuidDataRef = (CFDataRef)CFDictionaryGetValue((CFDictionaryRef)plistRef, keyName);
+                if ( uuidDataRef != nullptr ) {
+                    CFDataGetBytes(uuidDataRef, CFRangeMake(0, CFDataGetLength(uuidDataRef)), uuid);
+
+                    Node uuidNode;
+                    uuid_string_t uuidString;
+                    uuid_unparse_upper(uuid, uuidString);
+                    uuidNode.value = uuidString;
+                    topNode.map[jsonNodeName] = uuidNode;
+                }
+                CFRelease(plistRef);
+                CFRelease(readStreamRef);
+            }
+        };
+
+        getPlistUUID("prelink-info-uuid", CFSTR("_PrelinkKCID"));
+
+        // If we are an auxKC, then we should also have a reference to the baseKC UUID
+        getPlistUUID("prelink-info-base-uuid", CFSTR("_BootKCID"));
+
+        // If we are an pageableKC, then we should also have a reference to the pageableKC UUID
+        getPlistUUID("prelink-info-pageable-uuid", CFSTR("_PageableKCID"));
+
+        printJSON(topNode, 0, std::cout);
+    }
+
+    if (options.printKModInfo) {
+        __block Node topNode;
+
+        appCacheMA->forEachDylib(diag, ^(const MachOAnalyzer *ma, const char *name, bool &stop) {
+            Node dylibNode;
+            dylibNode.map["name"] = makeNode(name);
+
+            // Check for a global first
+            __block uint64_t kmodInfoVMOffset = 0;
+            __block bool found = false;
+            {
+                dyld3::MachOAnalyzer::FoundSymbol foundInfo;
+                found = ma->findExportedSymbol(diag, "_kmod_info", true, foundInfo, nullptr);
+                if ( found ) {
+                    kmodInfoVMOffset = foundInfo.value;
+                }
+            }
+            // And fall back to a local if we need to
+            if ( !found ) {
+                ma->forEachLocalSymbol(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, "_kmod_info") == 0 ) {
+                        kmodInfoVMOffset = n_value - ma->preferredLoadAddress();
+                        found = true;
+                        stop = true;
+                    }
+                });
+            }
+
+            if ( found ) {
+                dyld3::MachOAppCache::KModInfo64_v1* kmodInfo = (dyld3::MachOAppCache::KModInfo64_v1*)((uint8_t*)ma + kmodInfoVMOffset);
+                Node kmodInfoNode;
+                kmodInfoNode.map["info-version"] = makeNode(decimal(kmodInfo->info_version));
+                kmodInfoNode.map["name"] = makeNode((const char*)&kmodInfo->name[0]);
+                kmodInfoNode.map["version"] = makeNode((const char*)&kmodInfo->version[0]);
+                kmodInfoNode.map["address"] = makeNode(hex(kmodInfo->address));
+                kmodInfoNode.map["size"] = makeNode(hex(kmodInfo->size));
+
+                dylibNode.map["kmod_info"] = kmodInfoNode;
+            } else {
+                dylibNode.map["kmod_info"] = makeNode("none");
+            }
+
+            topNode.array.push_back(dylibNode);
+        });
+
+        printJSON(topNode, 0, std::cout);
+    }
+
+    if (options.printFIPS) {
+        __block Node topNode;
+
+        appCacheMA->forEachDylib(diag, ^(const MachOAnalyzer *ma, const char *name, bool &stop) {
+            if ( strcmp(name, "com.apple.kec.corecrypto") != 0 )
+                return;
+
+            uint64_t hashStoreSize;
+            const void* hashStoreLocation = ma->findSectionContent("__TEXT", "__fips_hmacs", hashStoreSize);
+            assert(hashStoreLocation != nullptr);
+
+            const uint8_t* hashBuffer = (const uint8_t*)hashStoreLocation;
+            std::string hashString;
+
+            for (int i = 0; i < hashStoreSize; ++i) {
+                uint8_t byte = hashBuffer[i];
+                uint8_t nibbleL = byte & 0x0F;
+                uint8_t nibbleH = byte >> 4;
+                if ( nibbleH < 10 ) {
+                    hashString += '0' + nibbleH;
+                } else {
+                    hashString += 'a' + (nibbleH-10);
+                }
+                if ( nibbleL < 10 ) {
+                    hashString += '0' + nibbleL;
+                } else {
+                    hashString += 'a' + (nibbleL-10);
+                }
+            }
+
+            stop = true;
+
+            topNode.map["fips"] = makeNode(hashString);
+        });
+
+        printJSON(topNode, 0, std::cout);
+    }
+
+    return 0;
+}
+
+static int validateFile(const ValidateOptions& options) {
+    // Verify any required options
+    if (gOpts.archs.size() != 1)
+        exit_usage("-arch");
+    if (gOpts.platform == nullptr)
+        exit_usage("-platform");
+    if (options.filePath == nullptr)
+        exit_usage();
+
+    const GradedArchs& archs = GradedArchs::forName(gOpts.archs[0]);
+    Platform platform = Platform::unknown;
+
+    // HACK: Pass a real option for building a kernel app cache
+    if (strcmp(gOpts.platform, "kernel")) {
+        fprintf(stderr, "Could not create app cache because: unknown platform '%s'\n", gOpts.platform);
+        return 1;
+    }
+
+    __block Diagnostics diag;
+
+    std::string file = options.filePath;
+    {
+        FileSystemPhysical fileSystem;
+        char fileRealPath[MAXPATHLEN];
+        LoadedFileInfo loadedFileInfo = MachOAnalyzer::load(diag, fileSystem, file.c_str(), archs, platform, fileRealPath);
+        if (diag.hasError()) {
+            fprintf(stderr, "Could not load file '%s' because: %s\n", file.c_str(), diag.errorMessage().c_str());
+            diag.clearError();
+            return 1;
+        }
+
+        MachOAnalyzer* ma = (MachOAnalyzer*)loadedFileInfo.fileContent;
+        if (ma == nullptr) {
+            fprintf(stderr, "Could not load file: %s\n", file.c_str());
+            return 1;
+        }
+
+        auto errorHandler = ^(const char* msg) {
+            diag.warning("File '%s' cannot be placed in kernel collection because: %s", file.c_str(), msg);
+        };
+        if (ma->canBePlacedInKernelCollection(file.c_str(), errorHandler)) {
+            return 0;
+        } else {
+            fileSystem.unloadFile(loadedFileInfo);
+        }
+    }
+
+    {
+        // Since we found no files, print warnings for the ones we tried
+        if (diag.warnings().empty()) {
+            fprintf(stderr, "File '%s' was not valid for app-cache\n", file.c_str());
+        } else {
+            for (const std::string& msg : diag.warnings()) {
+                fprintf(stderr, "  %s\n", msg.c_str());
+            }
+        }
+        return 1;
+    }
+
+    return 0;
+}
+
+static void forEachBundle(const char* bundlesDirectoryPath,
+                          void (^callback)(CFBundleRef bundleRef, const char* bundleName)) {
+    CFStringRef sourcePath = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, bundlesDirectoryPath,
+                                                             kCFStringEncodingASCII, kCFAllocatorNull);
+    CFURLRef sourceURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, sourcePath,
+                                                       kCFURLPOSIXPathStyle, true);
+    CFArrayRef bundles = CFBundleCreateBundlesFromDirectory(kCFAllocatorDefault, sourceURL, nullptr);
+
+    for (CFIndex i = 0, e = CFArrayGetCount(bundles); i != e; ++i) {
+        CFBundleRef bundleRef = (CFBundleRef)CFArrayGetValueAtIndex(bundles, i);
+        CFStringRef bundleID = CFBundleGetIdentifier(bundleRef);
+        if (!bundleID)
+            continue;
+        const char* bundleName = CFStringGetCStringPtr(bundleID, kCFStringEncodingASCII);
+        callback(bundleRef, bundleName);
+    }
+
+    CFRelease(sourcePath);
+    CFRelease(sourceURL);
+    CFRelease(bundles);
+}
+
+static int listBundles(const ListBundlesOptions& options) {
+    // Verify any required options
+    if (options.directoryPath == nullptr)
+        exit_usage();
+
+    forEachBundle(options.directoryPath, ^(CFBundleRef bundleRef, const char* bundleName){
+        printf("Bundle: %s\n", bundleName);
+    });
+
+    return 0;
+}
+
+static CFDataRef
+createKernelCollectionForArch(const CreateKernelCollectionOptions& options, const char* arch,
+                              Diagnostics& diag) {
+    const GradedArchs& archs = GradedArchs::forName(arch);
+    Platform platform = Platform::unknown;
+
+
+    KernelCollectionBuilder* kcb = nullptr;
+    {
+        CFStringRef archStringRef = CFStringCreateWithCString(kCFAllocatorDefault, arch, kCFStringEncodingASCII);
+        BuildOptions_v1 buildOptions = { 1, options.collectionKind, options.stripMode, archStringRef, options.verbose };
+        kcb = createKernelCollectionBuilder(&buildOptions);
+        CFRelease(archStringRef);
+    }
+
+    FileSystemPhysical fileSystem;
+    LoadedFileInfo kernelCollectionLoadedFileInfo;
+
+    auto loadKernelCollection = ^(const char* kernelCollectionPath, CollectionKind collectionKind) {
+        if (!fileSystem.fileExists(kernelCollectionPath)) {
+            fprintf(stderr, "kernel collection path does not exist: %s\n", options.kernelPath);
+            return false;
+        }
+        LoadedFileInfo info;
+        char realerPath[MAXPATHLEN];
+        bool loadedFile = fileSystem.loadFile(kernelCollectionPath, info, realerPath, ^(const char *format, ...) {
+            va_list list;
+            va_start(list, format);
+            diag.error(format, list);
+            va_end(list);
+        });
+        if ( !loadedFile )
+            return false;
+        CFStringRef pathStringRef = CFStringCreateWithCString(kCFAllocatorDefault, kernelCollectionPath, kCFStringEncodingASCII);
+        CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)info.fileContent, info.fileContentLen, kCFAllocatorNull);
+        if ( !addCollectionFile(kcb, pathStringRef, dataRef, collectionKind) ) {
+            diag.error("Could not load kernel collection file");
+            return false;
+        }
+        CFRelease(dataRef);
+        CFRelease(pathStringRef);
+        return true;
+    };
+
+    switch (options.collectionKind) {
+        case unknownKC:
+            fprintf(stderr, "Invalid kernel collection kind\n");
+            exit(1);
+        case baseKC: {
+            if (!fileSystem.fileExists(options.kernelPath)) {
+                fprintf(stderr, "Kernel path does not exist: %s\n", options.kernelPath);
+                return {};
+            }
+            LoadedFileInfo info;
+            char realerPath[MAXPATHLEN];
+            bool loadedFile = fileSystem.loadFile(options.kernelPath, info, realerPath, ^(const char *format, ...) {
+                va_list list;
+                va_start(list, format);
+                diag.error(format, list);
+                va_end(list);
+            });
+            if ( !loadedFile )
+                return {};
+            CFStringRef pathStringRef = CFStringCreateWithCString(kCFAllocatorDefault, options.kernelPath, kCFStringEncodingASCII);
+            CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)info.fileContent, info.fileContentLen, kCFAllocatorNull);
+            if ( !addKernelFile(kcb, pathStringRef, dataRef) ) {
+                uint64_t errorCount = 0;
+                const char* const* errors = getErrors(kcb, &errorCount);
+                for (uint64_t i = 0; i != errorCount; ++i)
+                    diag.error("Could not load kernel file because: '%s'", errors[i]);
+                return {};
+            }
+            CFRelease(dataRef);
+            CFRelease(pathStringRef);
+            break;
+        }
+        case pageableKC:
+            if ( !loadKernelCollection(options.kernelCollectionPath, baseKC) )
+                return {};
+            break;
+        case auxKC:
+            if ( !loadKernelCollection(options.kernelCollectionPath, baseKC) )
+                return {};
+            // Pageable is optional
+            if ( options.pageableCollectionPath != nullptr ) {
+                if ( !loadKernelCollection(options.pageableCollectionPath, pageableKC) )
+                    return {};
+            }
+            break;
+    }
+
+    if ( !options.bundleIDs.empty() ) {
+        struct BundleData {
+            std::string                 executablePath;
+            std::string                 bundlePath;
+            std::vector<std::string>    dependencies;
+            CFDictionaryRef             infoPlist       = nullptr;
+        };
+        __block std::map<std::string, BundleData> foundBundles;
+
+        // Look for bundles in the extensions directory and any PlugIns directories its kext's contain
+        __block std::list<std::pair<std::string, bool>> kextDirectoriesToProcess;
+        kextDirectoriesToProcess.push_back({ options.extensionsPath, true });
+        while ( !kextDirectoriesToProcess.empty() ) {
+            std::string kextDir = kextDirectoriesToProcess.front().first;
+            bool lookForPlugins = kextDirectoriesToProcess.front().second;
+            kextDirectoriesToProcess.pop_front();
+
+            __block bool foundError = false;
+            forEachBundle(kextDir.c_str(), ^(CFBundleRef bundleRef, const char* bundleName) {
+
+                if (foundError)
+                    return;
+
+                // If the directory contains a PlugIns directory, then add it to the list to seach for kexts
+                if (lookForPlugins) {
+                    CFURLRef pluginsRelativeURL = CFBundleCopyBuiltInPlugInsURL(bundleRef);
+                    if ( pluginsRelativeURL != nullptr ) {
+                        CFURLRef pluginsAbsoluteURL = CFURLCopyAbsoluteURL(pluginsRelativeURL);
+                        CFStringRef pluginString = CFURLCopyFileSystemPath(pluginsAbsoluteURL, kCFURLPOSIXPathStyle);
+                        const char* pluginPath = CFStringGetCStringPtr(pluginString, kCFStringEncodingASCII);
+                        kextDirectoriesToProcess.push_back({ pluginPath, false });
+
+                        CFRelease(pluginString);
+                        CFRelease(pluginsAbsoluteURL);
+                        CFRelease(pluginsRelativeURL);
+                    }
+                }
+
+#if 0
+                // For now always load bundles as we don't require every bundle to be listed on the command line
+                // but can instead bring them in on demand.
+
+                // Once we've looked for plugins, if we don't want this bundle then we can skip validating it.
+                if ( foundBundles.count(bundleName) == 0 )
+                    return;
+#endif
+
+                BundleData bundleData;
+                bundleData.infoPlist = CFBundleGetInfoDictionary(bundleRef);
+
+                CFURLRef bundleExecutableRelativeURL = CFBundleCopyExecutableURL(bundleRef);
+
+                // Its ok to be missing an executable.  We'll just skip this bundle
+                if ( bundleExecutableRelativeURL == nullptr ) {
+                    // FIXME: Its possibly not ok to be missing the executable if its actually listed
+                    // as a CFBundleExecutable path in the plist
+                    foundBundles[bundleName] = bundleData;
+                    return;
+                }
+
+                CFURLRef bundleExecutableAbsoluteURL = CFURLCopyAbsoluteURL(bundleExecutableRelativeURL);
+                CFStringRef bundleExecutableString = CFURLCopyFileSystemPath(bundleExecutableAbsoluteURL, kCFURLPOSIXPathStyle);
+                const char* bundleExecutablePath = CFStringGetCStringPtr(bundleExecutableString, kCFStringEncodingASCII);
+
+                // Check for an arch specific dependency list first
+                std::string archBundleLibraries = std::string("OSBundleLibraries") + "_" + arch;
+                CFStringRef archBundleLibrariesStringRef = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, archBundleLibraries.c_str(),
+                                                                                           kCFStringEncodingASCII, kCFAllocatorNull);
+                CFTypeRef depsRef = CFBundleGetValueForInfoDictionaryKey(bundleRef, archBundleLibrariesStringRef);
+                if ( depsRef == nullptr ) {
+                    // No arch specific deps, so try the defaults
+                    depsRef = CFBundleGetValueForInfoDictionaryKey(bundleRef, CFSTR("OSBundleLibraries"));
+                }
+                if (depsRef != nullptr) {
+                    if (CFGetTypeID(depsRef) != CFDictionaryGetTypeID()) {
+                        fprintf(stderr, "Bad bundle '%s' (\"OSBundleLibraries\" is not a dictionary)\n", bundleName);
+                        foundError = true;
+                        return;
+                    }
+
+                    CFDictionaryRef dictRef = (CFDictionaryRef)depsRef;
+                    CFDictionaryApplyFunction(dictRef, [](const void *key, const void *value, void *context) {
+                        BundleData* bundleData = (BundleData*)context;
+                        CFStringRef keyRef = (CFStringRef)key;
+                        //CFStringRef valueRef = (CFStringRef)value;
+                        bundleData->dependencies.push_back(CFStringGetCStringPtr(keyRef, kCFStringEncodingASCII));
+                    }, &bundleData);
+                }
+
+                // Make sure no-one tries to link the kernel directly.  They must do so via symbol sets
+                if ( !bundleData.dependencies.empty() ) {
+                    for (const std::string& dep : bundleData.dependencies) {
+                        if (dep == "com.apple.kernel") {
+                            fprintf(stderr, "Rejecting bundle '%s' as it is trying to link directly to the kernel\n",
+                                    bundleName);
+                            foundError = true;
+                            return;
+                        }
+                    }
+                }
+
+                bundleData.executablePath = bundleExecutablePath;
+
+                CFURLRef bundleURLRef = CFBundleCopyBundleURL(bundleRef);
+                CFStringRef bundleURLString = CFURLCopyFileSystemPath(bundleURLRef, kCFURLPOSIXPathStyle);
+                const char* bundleURLPath = CFStringGetCStringPtr(bundleURLString, kCFStringEncodingASCII);
+                if (strncmp(bundleURLPath, options.extensionsPath, strlen(options.extensionsPath)) != 0) {
+                    fprintf(stderr, "Bundle path '%s' is not within extensions directory '%s'\n",
+                            bundleURLPath, options.extensionsPath);
+                }
+                // Don't remove the whole extensions prefix, but instead the volume root, if we have one
+                bundleData.bundlePath = bundleURLPath + strlen(options.volumeRoot);
+                foundBundles[bundleName] = bundleData;
+
+                CFRelease(bundleExecutableString);
+                CFRelease(bundleExecutableAbsoluteURL);
+                CFRelease(bundleExecutableRelativeURL);
+            });
+
+            if (foundError)
+                return {};
+        }
+
+        __block std::set<std::string> existingBundles;
+        auto addSymbolSetsBundleIDs = ^(const dyld3::MachOAnalyzer* kernelMA) {
+            assert(kernelMA != nullptr);
+
+            __block std::list<std::string> nonASCIIStrings;
+            auto getString = ^(Diagnostics& diags, CFStringRef symbolNameRef) {
+                const char* symbolName = CFStringGetCStringPtr(symbolNameRef, kCFStringEncodingUTF8);
+                if ( symbolName != nullptr )
+                    return symbolName;
+
+                CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(symbolNameRef), kCFStringEncodingUTF8);
+                char buffer[len + 1];
+                if ( !CFStringGetCString(symbolNameRef, buffer, len, kCFStringEncodingUTF8) ) {
+                    diags.error("Could not convert string to ASCII");
+                    return (const char*)nullptr;
+                }
+                buffer[len] = '\0';
+                nonASCIIStrings.push_back(buffer);
+                return nonASCIIStrings.back().c_str();
+            };
+
+            uint64_t symbolSetsSize = 0;
+            const void* symbolSetsContent = kernelMA->findSectionContent("__LINKINFO", "__symbolsets", symbolSetsSize);
+            if ( symbolSetsContent != nullptr ) {
+
+                // A helper to automatically call CFRelease when we go out of scope
+                struct AutoReleaseTypeRef {
+                    AutoReleaseTypeRef() = default;
+                    ~AutoReleaseTypeRef() {
+                        if ( ref != nullptr ) {
+                            CFRelease(ref);
+                        }
+                    }
+                    void setRef(CFTypeRef typeRef) {
+                        assert(ref == nullptr);
+                        ref = typeRef;
+                    }
+
+                    CFTypeRef ref = nullptr;
+                };
+
+                AutoReleaseTypeRef dataRefReleaser;
+                AutoReleaseTypeRef plistRefReleaser;
+
+                CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)symbolSetsContent, symbolSetsSize, kCFAllocatorNull);
+                if ( dataRef == nullptr ) {
+                    diag.error("Could not create data ref for symbol sets");
+                    return false;
+                }
+                dataRefReleaser.setRef(dataRef);
+
+                CFErrorRef errorRef = nullptr;
+                CFPropertyListRef plistRef = CFPropertyListCreateWithData(kCFAllocatorDefault, dataRef, kCFPropertyListImmutable, nullptr, &errorRef);
+                if (errorRef != nullptr) {
+                    CFStringRef errorString = CFErrorCopyDescription(errorRef);
+                    diag.error("Could not load plist because :%s",
+                               CFStringGetCStringPtr(errorString, kCFStringEncodingASCII));
+                    CFRelease(errorRef);
+                    return false;
+                }
+                if ( plistRef == nullptr ) {
+                    diag.error("Could not create plist ref for symbol sets");
+                    return false;
+                }
+                plistRefReleaser.setRef(plistRef);
+
+                if ( CFGetTypeID(plistRef) != CFDictionaryGetTypeID() ) {
+                    diag.error("Symbol set plist should be a dictionary");
+                    return false;
+                }
+                CFDictionaryRef symbolSetsDictRef = (CFDictionaryRef)plistRef;
+                CFArrayRef symbolSetArrayRef = (CFArrayRef)CFDictionaryGetValue(symbolSetsDictRef, CFSTR("SymbolsSets"));
+                if ( symbolSetArrayRef != nullptr ) {
+                    if ( CFGetTypeID(symbolSetArrayRef) != CFArrayGetTypeID() ) {
+                        diag.error("SymbolsSets value should be an array");
+                        return false;
+                    }
+                    for (CFIndex symbolSetIndex = 0; symbolSetIndex != CFArrayGetCount(symbolSetArrayRef); ++symbolSetIndex) {
+                        CFDictionaryRef symbolSetDictRef = (CFDictionaryRef)CFArrayGetValueAtIndex(symbolSetArrayRef, symbolSetIndex);
+                        if ( CFGetTypeID(symbolSetDictRef) != CFDictionaryGetTypeID() ) {
+                            diag.error("Symbol set element should be a dictionary");
+                            return false;
+                        }
+
+                        // CFBundleIdentifier
+                        CFStringRef bundleIDRef = (CFStringRef)CFDictionaryGetValue(symbolSetDictRef, CFSTR("CFBundleIdentifier"));
+                        if ( (bundleIDRef == nullptr) || (CFGetTypeID(bundleIDRef) != CFStringGetTypeID()) ) {
+                            diag.error("Symbol set bundle ID should be a string");
+                            return false;
+                        }
+
+                        const char* dylibID = getString(diag, bundleIDRef);
+                        if ( dylibID == nullptr )
+                            return false;
+                        existingBundles.insert(dylibID);
+                    }
+                }
+            }
+            return true;
+        };
+
+        auto addExistingBundleIDs = ^(const char* path, bool isBaseKC) {
+            char fileRealPath[MAXPATHLEN];
+            auto kernelCollectionLoadedFileInfo = MachOAnalyzer::load(diag, fileSystem, path, archs, platform, fileRealPath);
+            if ( diag.hasError() ) {
+                fprintf(stderr, "Could not load file '%s' because: %s\n", path, diag.errorMessage().c_str());
+                return false;
+            }
+            const MachOAppCache* appCacheMA = (const MachOAppCache*)kernelCollectionLoadedFileInfo.fileContent;
+            if (appCacheMA == nullptr) {
+                fprintf(stderr, "Could not load file: %s\n", path);
+                return false;
+            }
+            if ( !appCacheMA->isFileSet() ) {
+                fprintf(stderr, "kernel collection is not a cache file: %s\n", path);
+                return false;
+            }
+            __block const dyld3::MachOAnalyzer* kernelMA = nullptr;
+            appCacheMA->forEachDylib(diag, ^(const MachOAnalyzer *ma, const char *name, bool &stop) {
+                if ( ma->isStaticExecutable() ) {
+                    kernelMA = ma;
+                }
+                existingBundles.insert(name);
+            });
+
+            if ( isBaseKC ) {
+                if ( !addSymbolSetsBundleIDs(kernelMA) )
+                    return false;
+            }
+            fileSystem.unloadFile(kernelCollectionLoadedFileInfo);
+            return true;
+        };
+        if ( options.collectionKind == baseKC ) {
+            char fileRealPath[MAXPATHLEN];
+            auto kernelLoadedFileInfo = MachOAnalyzer::load(diag, fileSystem, options.kernelPath, archs, platform, fileRealPath);
+            if ( diag.hasError() ) {
+                fprintf(stderr, "Could not load file '%s' because: %s\n", options.kernelPath, diag.errorMessage().c_str());
+                return {};
+            }
+            const MachOAppCache* kernelMA = (const MachOAppCache*)kernelLoadedFileInfo.fileContent;
+            if (kernelMA == nullptr) {
+                fprintf(stderr, "Could not load file: %s\n", options.kernelPath);
+                return {};
+            }
+            if ( !kernelMA->isStaticExecutable() ) {
+                fprintf(stderr, "kernel is not a static executable: %s\n", options.kernelPath);
+                return {};
+            }
+
+            if ( !addSymbolSetsBundleIDs(kernelMA) )
+                return {};
+            fileSystem.unloadFile(kernelLoadedFileInfo);
+        }
+        if ( (options.collectionKind == auxKC) || (options.collectionKind == pageableKC) ) {
+            // Work out which bundle-ids are already in the base KC
+            if ( !addExistingBundleIDs(options.kernelCollectionPath, true) )
+                return {};
+        }
+        if ( options.pageableCollectionPath != nullptr ) {
+            // Work out which bundle-ids are already in the pageable KC
+            if ( !addExistingBundleIDs(options.pageableCollectionPath, false) )
+                return {};
+        }
+
+        std::set<std::string> processedBundleIDs;
+        std::list<const char*> bundleIDsToLoad;
+        bundleIDsToLoad.insert(bundleIDsToLoad.end(), options.bundleIDs.begin(), options.bundleIDs.end());
+        while (!bundleIDsToLoad.empty()) {
+            std::string bundleID = bundleIDsToLoad.front();
+            bundleIDsToLoad.pop_front();
+
+            std::string stripModeString;
+            if ( const char* colonPos = strchr(bundleID.c_str(), ':') ) {
+                stripModeString = colonPos + 1;
+                bundleID.erase(colonPos - bundleID.data());
+            }
+
+            // If we've seen this one already then skip it
+            if (!processedBundleIDs.insert(bundleID).second)
+                continue;
+
+            // Find the bundle for this ID
+            auto it = foundBundles.find(bundleID);
+            if (it == foundBundles.end()) {
+                fprintf(stderr, "[WARNING]: Could not find bundle with ID '%s'\n", bundleID.c_str());
+                continue;
+            }
+
+            BundleData& bundleData = it->second;
+
+            LoadedFileInfo info;
+
+            // Codeless kexts don't have an executable path, but we still want to put their
+            // plist in the prelink info
+            bool isCodeless = bundleData.executablePath.empty();
+            if ( !isCodeless ) {
+                char realerPath[MAXPATHLEN];
+                bool loadedFile = fileSystem.loadFile(bundleData.executablePath.c_str(), info, realerPath,
+                                                      ^(const char *format, ...) {
+                    va_list list;
+                    va_start(list, format);
+                    diag.error(format, list);
+                    va_end(list);
+                });
+                if ( !loadedFile )
+                    return {};
+            }
+
+            std::vector<const char*> deps;
+            for (const std::string& dependency : bundleData.dependencies)
+                deps.push_back(dependency.c_str());
+
+            CFStringRef         kextPathStringRef   = nullptr;
+            CFDataRef           kextDataRef         = nullptr;
+            if ( !isCodeless) {
+                kextPathStringRef = CFStringCreateWithCString(kCFAllocatorDefault, bundleData.executablePath.c_str(), kCFStringEncodingASCII);
+                kextDataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)info.fileContent, info.fileContentLen, kCFAllocatorNull);
+            }
+
+            CFMutableArrayRef   kextDepsArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, bundleData.dependencies.size(), &kCFTypeArrayCallBacks);
+            for (const std::string& dependency : bundleData.dependencies) {
+                CFStringRef depStringRef = CFStringCreateWithCString(kCFAllocatorDefault, dependency.c_str(), kCFStringEncodingASCII);
+                CFArrayAppendValue(kextDepsArrayRef, depStringRef);
+                CFRelease(depStringRef);
+            }
+            CFStringRef         kextBundleIDStringRef = CFStringCreateWithCString(kCFAllocatorDefault, bundleID.c_str(), kCFStringEncodingASCII);
+            CFStringRef         kextBundlePathStringRef = CFStringCreateWithCString(kCFAllocatorDefault, bundleData.bundlePath.c_str(), kCFStringEncodingASCII);
+
+            BinaryStripMode stripMode = binaryStripNone;
+            if ( !stripModeString.empty() ) {
+                if ( stripModeString == "locals" ) {
+                    stripMode = binaryStripLocals;
+                } else if ( stripModeString == "exports" ) {
+                    stripMode = binaryStripExports;
+                } else if ( stripModeString == "all" ) {
+                    stripMode = binaryStripAll;
+                } else {
+                    diag.error("Unknown strip mode: '%s'", stripModeString.c_str());
+                    return {};
+                }
+            }
+
+            KextFileData_v1 fileData = { 1, kextPathStringRef, kextDataRef,
+                                         kextDepsArrayRef, kextBundleIDStringRef, kextBundlePathStringRef,
+                                         bundleData.infoPlist, stripMode };
+
+            if ( !addKextDataFile(kcb, &fileData) ) {
+                uint64_t errorCount = 0;
+                const char* const* errors = getErrors(kcb, &errorCount);
+                for (uint64_t i = 0; i != errorCount; ++i)
+                    diag.error("Could not load kext file because: '%s'", errors[i]);
+                return {};
+            }
+
+            // Walk the dependencies and add any new ones to the list
+            for (const std::string& dependency : bundleData.dependencies) {
+                if ( existingBundles.find(dependency) == existingBundles.end() )
+                    bundleIDsToLoad.push_back(dependency.c_str());
+            }
+        }
+
+        // Filter dependencies to kext's with binaries
+#if 0
+        const std::map<std::string, BundleData>* foundBundlesPtr = &foundBundles;
+        for (AppCacheBuilder::InputDylib& file : loadedFiles) {
+            file.dylibDeps.erase(std::remove_if(file.dylibDeps.begin(), file.dylibDeps.end(),
+                                                [&](const std::string& depName) {
+                auto it = foundBundlesPtr->find(depName);
+                assert(it != foundBundlesPtr->end());
+                return it->second.executablePath.empty();
+            }),file.dylibDeps.end());
+        }
+#endif
+    }
+
+#if 0
+    for (AppCacheBuilder::InputDylib& file : loadedFiles) {
+        char fileRealPath[MAXPATHLEN];
+        const char* path = file.dylib.loadedFileInfo.path;
+        LoadedFileInfo loadedFileInfo = MachOAnalyzer::load(diag, fileSystem, path, archs, platform, fileRealPath);
+        if ( diag.hasError() ) {
+            fprintf(stderr, "Could not load file '%s' because: %s\n", path, diag.errorMessage().c_str());
+            return {};
+        }
+
+        MachOAnalyzer* ma = (MachOAnalyzer*)loadedFileInfo.fileContent;
+        if (ma == nullptr) {
+            fprintf(stderr, "Could not load file: %s\n", path);
+            return {};
+        }
+
+        auto errorHandler = ^(const char* msg) {
+            diag.error("Binary located at '%s' cannot be placed in kernel collection because: %s", path, msg);
+        };
+        if (ma->canBePlacedInKernelCollection(path, errorHandler)) {
+            DyldSharedCache::MappedMachO mappedFile(path, ma, loadedFileInfo.sliceLen, false, false,
+                                                    loadedFileInfo.sliceOffset, loadedFileInfo.mtime,
+                                                    loadedFileInfo.inode);
+            CacheBuilder::LoadedMachO loadedMachO = { mappedFile, loadedFileInfo, nullptr };
+            file.dylib = loadedMachO;
+        } else {
+            fileSystem.unloadFile(loadedFileInfo);
+        }
+        if ( diag.hasError() ) {
+            fprintf(stderr, "%s\n", diag.errorMessage().c_str());
+            return {};
+        }
+    }
+#endif
+
+#if 0
+    if (loadedFiles.empty()) {
+        fprintf(stderr, "Could not find any valid files to create kernel collection\n");
+
+        // Since we found no files, print warnings for the ones we tried
+        if (!diag.warnings().empty()) {
+            fprintf(stderr, "Failed to use the following files:\n");
+            for (const std::string& msg : diag.warnings()) {
+                fprintf(stderr, "  %s\n", msg.c_str());
+            }
+        }
+        return {};
+    }
+
+    if (options.verbose) {
+        for (const AppCacheBuilder::InputDylib& loadedFile : loadedFiles)
+            fprintf(stderr, "Building cache with file: %s\n", loadedFile.dylib.loadedFileInfo.path);
+    }
+#endif
+
+    for (const SectionData& sectData : options.sections) {
+        CFStringRef segmentName   = CFStringCreateWithCString(kCFAllocatorDefault, sectData.segmentName, kCFStringEncodingASCII);
+        CFStringRef sectionName   = nullptr;
+        if ( sectData.sectionName != nullptr )
+            sectionName = CFStringCreateWithCString(kCFAllocatorDefault, sectData.sectionName, kCFStringEncodingASCII);
+
+        CFDataRef sectionData = nullptr;
+        {
+            struct stat stat_buf;
+            int fd = ::open(sectData.payloadFilePath, O_RDONLY, 0);
+            if (fd == -1) {
+                diag.error("can't open file '%s', errno=%d\n", sectData.payloadFilePath, errno);
+                return {};
+            }
+
+            if (fstat(fd, &stat_buf) == -1) {
+                diag.error("can't stat open file '%s', errno=%d\n", sectData.payloadFilePath, 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) {
+                diag.error("mmap() for file at %s failed, errno=%d\n", sectData.payloadFilePath, errno);
+                ::close(fd);
+                return {};
+            }
+            ::close(fd);
+
+            sectionData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)buffer, stat_buf.st_size, kCFAllocatorNull);
+        }
+
+        if ( !addSegmentData(kcb, segmentName, sectionName, sectionData) ) {
+            uint64_t errorCount = 0;
+            const char* const* errors = getErrors(kcb, &errorCount);
+            for (uint64_t i = 0; i != errorCount; ++i)
+                diag.error("Could not load section data file because: '%s'", errors[i]);
+            return {};
+        }
+    }
+
+    if ( options.prelinkInfoExtraData != nullptr ) {
+        struct stat stat_buf;
+        int fd = ::open(options.prelinkInfoExtraData, O_RDONLY, 0);
+        if (fd == -1) {
+            diag.error("can't open file '%s', errno=%d\n", options.prelinkInfoExtraData, errno);
+            return {};
+        }
+
+        if (fstat(fd, &stat_buf) == -1) {
+            diag.error("can't stat open file '%s', errno=%d\n", options.prelinkInfoExtraData, 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) {
+            diag.error("mmap() for file at %s failed, errno=%d\n", options.prelinkInfoExtraData, errno);
+            ::close(fd);
+            return {};
+        }
+        ::close(fd);
+
+        CFDataRef prelinkInfoData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)buffer, stat_buf.st_size, kCFAllocatorNull);
+
+        CFErrorRef errorRef = nullptr;
+        CFPropertyListRef plistRef = CFPropertyListCreateWithData(kCFAllocatorDefault, prelinkInfoData, kCFPropertyListImmutable, nullptr, &errorRef);
+        if (errorRef != nullptr) {
+            CFStringRef errorString = CFErrorCopyDescription(errorRef);
+            diag.error("Could not load prelink info plist because :%s",
+                       CFStringGetCStringPtr(errorString, kCFStringEncodingASCII));
+            CFRelease(errorRef);
+            return {};
+        }
+        if ( plistRef == nullptr ) {
+            diag.error("Could not create plist ref for prelink info");
+            return {};
+        }
+        if ( CFGetTypeID(plistRef) != CFDictionaryGetTypeID() ) {
+            diag.error("Prelink info plist should be a dictionary");
+            return {};
+        }
+
+        if ( !addPrelinkInfo(kcb, (CFDictionaryRef)plistRef) ) {
+            uint64_t errorCount = 0;
+            const char* const* errors = getErrors(kcb, &errorCount);
+            for (uint64_t i = 0; i != errorCount; ++i)
+                diag.error("Could not prelink data file because: '%s'", errors[i]);
+            return {};
+        }
+    }
+
+    bool success = runKernelCollectionBuilder(kcb);
+    uint64_t errorCount = 0;
+    const char* const* errors = getErrors(kcb, &errorCount);
+    if ( errors != nullptr ) {
+        if ( !options.printJSONErrors ) {
+            for (uint64_t i = 0; i != errorCount; ++i) {
+                fprintf(stderr, "Could not build kernel collection because '%s'\n", errors[i]);
+            }
+        }
+        CFDictionaryRef errorDictRef = getKextErrors(kcb);
+        if ( errorDictRef != nullptr ) {
+            Node rootNode;
+
+            CFDictionaryApplyFunction(errorDictRef, [](const void *key, const void *value, void *context) {
+                Node* rootNode = (Node*)context;
+                CFStringRef keyRef = (CFStringRef)key;
+                CFArrayRef valueRef = (CFArrayRef)value;
+
+                Node bundleNode;
+                bundleNode.map["id"] = Node(CFStringGetCStringPtr(keyRef, kCFStringEncodingASCII));
+
+                Node errorsNode;
+                CFArrayApplyFunction(valueRef, CFRangeMake(0, CFArrayGetCount(valueRef)), [](const void *value, void *context) {
+                    Node* errorsNode = (Node*)context;
+                    CFStringRef valueRef = (CFStringRef)value;
+
+                    errorsNode->array.push_back(Node(CFStringGetCStringPtr(valueRef, kCFStringEncodingASCII)));
+                }, &errorsNode);
+
+                bundleNode.map["errors"] = errorsNode;
+
+                rootNode->array.push_back(bundleNode);
+            }, &rootNode);
+
+            // sort the nodes so that the output is reproducible
+            std::sort(rootNode.array.begin(), rootNode.array.end(),
+                      [](const Node& a, const Node&b) {
+                return a.map.find("id")->second.value < b.map.find("id")->second.value;
+            });
+
+            printJSON(rootNode);
+        } else {
+            Node rootNode;
+            for (uint64_t i = 0; i != errorCount; ++i) {
+                rootNode.array.push_back(Node(errors[i]));
+            }
+            printJSON(rootNode);
+        }
+        return {};
+    }
+
+    if ( !success )
+        return {};
+
+    uint64_t fileResultCount = 0;
+    const auto* fileResults = getCollectionFileResults(kcb, &fileResultCount);
+    if ( fileResults == nullptr ) {
+        fprintf(stderr, "Could not get file results\n");
+        return {};
+    }
+    if ( fileResultCount != 1 ) {
+        fprintf(stderr, "Unsupported file result count: %lld\n", fileResultCount);
+        return {};
+    }
+
+    CFDataRef dataRef = fileResults[0]->data;
+    CFRetain(dataRef);
+
+    destroyKernelCollectionBuilder(kcb);
+
+    return dataRef;
+}
+
+static int createKernelCollection(const CreateKernelCollectionOptions& options) {
+    // Verify any required options
+    if (gOpts.archs.empty()) {
+        exit_usage("-arch");
+    } else {
+        std::set<std::string_view> archs(gOpts.archs.begin(), gOpts.archs.end());
+        if (archs.size() != gOpts.archs.size()) {
+            fprintf(stderr, "Duplicate -arch specified\n");
+            exit(1);
+        }
+    }
+    if (options.outputCachePath == nullptr)
+        exit_usage();
+
+    switch (options.stripMode) {
+        case unknownStripMode:
+        case stripNone:
+            break;
+        case stripAll:
+        case stripAllKexts:
+            if ( options.collectionKind != baseKC ) {
+                fprintf(stderr, "Cannot use -strip-all-kexts with auxKC.  Use strip-all instead\n");
+                exit(1);
+            }
+            break;
+    }
+
+    switch (options.collectionKind) {
+        case unknownKC:
+            fprintf(stderr, "Invalid kernel collection kind\n");
+            exit(1);
+        case baseKC:
+            if (options.kernelPath == nullptr)
+                exit_usage("-kernel");
+            break;
+        case pageableKC:
+        case auxKC:
+            if (options.kernelCollectionPath == nullptr)
+                exit_usage("-kernel-collection");
+            break;
+    }
+
+    if ( !options.bundleIDs.empty() ) {
+        if (options.extensionsPath == nullptr)
+            exit_usage("-extensions");
+    }
+
+    // Volume root should be a prefix of extensions path
+    if ( options.extensionsPath != nullptr ) {
+        if ( strncmp(options.extensionsPath, options.volumeRoot, strlen(options.volumeRoot)) != 0 ) {
+            fprintf(stderr, "Volume root '%s' is not a prefix of extensions path '%s'\n",
+                    options.volumeRoot, options.extensionsPath);
+        }
+    }
+
+    std::vector<CFDataRef> buffers;
+    for (const char* arch : gOpts.archs) {
+        Diagnostics diag;
+        CFDataRef bufferRef = createKernelCollectionForArch(options, arch, diag);
+        if ( diag.hasError() ) {
+            fprintf(stderr, "%s\n", diag.errorMessage().c_str());
+            return 1;
+        }
+        if ( bufferRef == nullptr ) {
+            // If we  want errors then return 0
+            if ( options.printJSONErrors )
+                return 0;
+            return 1;
+        }
+        buffers.push_back(bufferRef);
+    }
+
+    if (buffers.size() == 1) {
+        // Single arch.  Just write the file directly
+        CFDataRef bufferRef = buffers.front();
+        if ( !safeSave(CFDataGetBytePtr(bufferRef), CFDataGetLength(bufferRef), options.outputCachePath) ) {
+            fprintf(stderr, "Could not write app cache\n");
+            return 1;
+        }
+        CFRelease(bufferRef);
+    } else {
+        // Multiple buffers.  Create a FAT file
+        std::vector<uint8_t> fatBuffer;
+
+        // Add the FAT header to the start of the buffer
+        fatBuffer.resize(0x4000, 0);
+        fat_header* header = (fat_header*)&fatBuffer.front();
+        header->magic       = OSSwapHostToBigInt32(FAT_MAGIC);
+        header->nfat_arch   = OSSwapHostToBigInt32((uint32_t)buffers.size());
+
+        for (uint32_t i = 0; i != buffers.size(); ++i) {
+            CFDataRef bufferRef = buffers[i];
+            mach_header* mh = (mach_header*)CFDataGetBytePtr(bufferRef);
+
+            uint32_t offsetInBuffer = (uint32_t)fatBuffer.size();
+
+            fat_arch* archBuffer = (fat_arch*)(&fatBuffer.front() + sizeof(fat_header));
+            archBuffer[i].cputype       = OSSwapHostToBigInt32(mh->cputype);
+            archBuffer[i].cpusubtype    = OSSwapHostToBigInt32(mh->cpusubtype);
+            archBuffer[i].offset        = OSSwapHostToBigInt32(offsetInBuffer);
+            archBuffer[i].size          = OSSwapHostToBigInt32((uint32_t)CFDataGetLength(bufferRef));
+            archBuffer[i].align         = OSSwapHostToBigInt32(14);
+
+            auto align = [](uint64_t addr, uint8_t p2) {
+                uint64_t mask = (1 << p2);
+                return (addr + mask - 1) & (-mask);
+            };
+
+            uint32_t alignedSize = (uint32_t)align((uint32_t)CFDataGetLength(bufferRef), 14);
+            fatBuffer.resize(fatBuffer.size() + alignedSize, 0);
+            memcpy(&fatBuffer.front() + offsetInBuffer, CFDataGetBytePtr(bufferRef), CFDataGetLength(bufferRef));
+        }
+
+        if ( !safeSave(&fatBuffer.front(), fatBuffer.size(), options.outputCachePath) ) {
+            fprintf(stderr, "Could not write app cache\n");
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+int main(int argc, const char* argv[]) {
+    OptionsVariants options;
+    if (!parseArgs(argc, argv, options))
+        return 1;
+
+    if (std::holds_alternative<DumpOptions>(options)) {
+        return dumpAppCache(std::get<DumpOptions>(options));
+    }
+
+    if (std::holds_alternative<ValidateOptions>(options)) {
+        return validateFile(std::get<ValidateOptions>(options));
+    }
+
+    if (std::holds_alternative<ListBundlesOptions>(options)) {
+        return listBundles(std::get<ListBundlesOptions>(options));
+    }
+
+    if (std::holds_alternative<CreateKernelCollectionOptions>(options)) {
+        return createKernelCollection(std::get<CreateKernelCollectionOptions>(options));
+    }
+
+    assert(std::holds_alternative<std::monostate>(options));
+
+    exit_usage();
+}
index a35ffdd646f0b2f0117f8f6c47201d6ffa8ae400..f9c1663ea098930df7617dfbfd7b64b9f98af493 100644 (file)
@@ -57,7 +57,7 @@ static const char* leafName(const char* argv0)
         return argv0;
 }
 
-static void entry_setVars(const mach_header* mainMH, int argc, const char* argv[], const char* envp[], const char* apple[])
+static void entry_setVars(const mach_header* mainMH, int argc, const char* argv[], const char* envp[], const char* apple[], bool keysOff, bool platformBinariesOnly)
 {
     NXArgc       = argc;
     NXArgv       = argv;
@@ -70,7 +70,7 @@ static void entry_setVars(const mach_header* mainMH, int argc, const char* argv[
     sVars.NXArgvPtr     = &NXArgv;
     sVars.environPtr    = (const char***)&environ;
     sVars.__prognamePtr = &__progname;
-    gAllImages.setProgramVars(&sVars);
+    gAllImages.setProgramVars(&sVars, keysOff, platformBinariesOnly);
 
     gUseDyld3 = true;
 
@@ -150,9 +150,21 @@ static void entry_setHasCacheOverrides(bool someCacheImageOverriden)
 
 static void entry_setProgramVars(ProgramVars* progVars)
 {
-    gAllImages.setProgramVars((AllImages::ProgramVars*)progVars);
+    // this entry only called when running crt1.o based old macOS programs
+    gAllImages.setProgramVars((AllImages::ProgramVars*)progVars, false, false);
 }
 
+static void entry_setLaunchMode(uint32_t flags)
+{
+    gAllImages.setLaunchMode(flags);
+}
+
+static MainFunc entry_getDriverkitMain(void)
+{
+    return gAllImages.getDriverkitMain();
+}
+
+
 static_assert((closure::kFormatVersion & LibDyldEntryVector::kBinaryFormatVersionMask) == closure::kFormatVersion, "binary format version overflow");
 
 const LibDyldEntryVector entryVectorForDyld = {
@@ -171,6 +183,8 @@ const LibDyldEntryVector entryVectorForDyld = {
     &entry_setNotifyMonitoringDyld,
     &entry_setHasCacheOverrides,
     &entry_setProgramVars,
+    &entry_setLaunchMode,
+    &entry_getDriverkitMain,
 };
 
 VIS_HIDDEN void _dyld_atfork_prepare()
index d6a8529273fc3e242341a723bcccd70f373b8fea..b28cd58a6342f428326beeda11c63e8c0709e1bc 100644 (file)
@@ -37,16 +37,17 @@ struct ProgramVars;
 
 namespace dyld3 {
 
+typedef void  (*MainFunc)(void);
 
 struct LibDyldEntryVector
 {
-    enum { kCurrentVectorVersion = 8 };
+    enum { kCurrentVectorVersion = 10 };
     // The 32-bit caches steal bits to make rebase chains, so use 32-bits for the binary format version storage, but mask only some to actually use
     enum { kBinaryFormatVersionMask = 0x00FFFFFF };
 
     uint32_t    vectorVersion;              // should be kCurrentVectorVersion
     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        (*setVars)(const mach_header* mainMH, int argc, const char* argv[], const char* envp[], const char* apple[], bool keysOff, bool platformBinariesOnly);
     void        (*setHaltFunction)(void (*func)(const char* message) __attribute__((noreturn)) );
     void        (*setOldAllImageInfo)(dyld_all_image_infos*);
     void        (*setInitialImageList)(const closure::LaunchClosure* closure,
@@ -70,6 +71,12 @@ struct LibDyldEntryVector
 
     // added in version 8
     void        (*setProgramVars)(struct ProgramVars* progVars);
+    
+    // added in version 9
+    void        (*setLaunchMode)(uint32_t flags);
+
+    // added in version 10
+    MainFunc    (*getDriverkitMain)(void);
 };
 
 extern const LibDyldEntryVector entryVectorForDyld;
index 22f39d8f563b33b50150e79631a2353316d8abbc..fb330572da80877edd22aa07e2fbad3610072bea 100644 (file)
@@ -55,25 +55,28 @@ namespace {
 template <typename P>
 class Adjustor {
 public:
-                    Adjustor(DyldSharedCache* cacheBuffer, macho_header<P>* mh, const std::vector<CacheBuilder::SegmentMappingInfo>& mappingInfo, Diagnostics& diag);
+                    Adjustor(uint64_t cacheBaseAddress, dyld3::MachOAnalyzer* mh, const char* dylibID,
+                             const std::vector<CacheBuilder::SegmentMappingInfo>& mappingInfo, Diagnostics& diag);
     void            adjustImageForNewSegmentLocations(CacheBuilder::ASLR_Tracker& aslrTracker,
-                                                      CacheBuilder::LOH_Tracker& lohTracker,
-                                                      const CacheBuilder::CacheCoalescedText& coalescedText,
+                                                      CacheBuilder::LOH_Tracker* lohTracker,
+                                                      const CacheBuilder::CacheCoalescedText* coalescedText,
                                                       const CacheBuilder::DylibTextCoalescer& textCoalescer);
  
 private:
-    void            adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTracker, CacheBuilder::LOH_Tracker& lohTracker,
-                                                const CacheBuilder::CacheCoalescedText& coalescedText,
+    void            adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTracker,
+                                                CacheBuilder::LOH_Tracker* lohTracker,
+                                                const CacheBuilder::CacheCoalescedText* coalescedText,
                                                 const CacheBuilder::DylibTextCoalescer& textCoalescer);
     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,
+                                    uint64_t imageStartAddress, uint64_t imageEndAddress, bool convertRebaseChains,
                                     CacheBuilder::ASLR_Tracker& aslrTracker, CacheBuilder::LOH_Tracker* lohTracker,
                                     uint32_t*& lastMappedAddr32, uint32_t& lastKind, uint64_t& lastToNewAddress);
     void            adjustDataPointers(CacheBuilder::ASLR_Tracker& aslrTracker);
     void            adjustRebaseChains(CacheBuilder::ASLR_Tracker& aslrTracker);
     void            slidePointer(int segIndex, uint64_t segOffset, uint8_t type, CacheBuilder::ASLR_Tracker& aslrTracker);
     void            adjustSymbolTable();
-    void            adjustChainedFixups();
+    void            adjustChainedFixups(const CacheBuilder::DylibTextCoalescer& textCoalescer);
+    void            adjustExternalRelocations();
     void            adjustExportsTrie(std::vector<uint8_t>& newTrieBytes);
     void            rebuildLinkEdit();
     void            adjustCode();
@@ -81,80 +84,78 @@ private:
     void            rebuildLinkEditAndLoadCommands(const CacheBuilder::DylibTextCoalescer& textCoalescer);
     uint64_t        slideForOrigAddress(uint64_t addr);
     void            convertGeneric64RebaseToIntermediate(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr, CacheBuilder::ASLR_Tracker& aslrTracker, uint64_t targetSlide);
-    void            convertArm64eRebaseToIntermediate(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr, CacheBuilder::ASLR_Tracker& aslrTracker, uint64_t targetSlide);
+    void            convertArm64eRebaseToIntermediate(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr, CacheBuilder::ASLR_Tracker& aslrTracker,
+                                                      uint64_t targetSlide, bool convertRebaseChains);
 
 
     typedef typename P::uint_t pint_t;
     typedef typename P::E E;
 
-    DyldSharedCache*                                        _cacheBuffer;
-    macho_header<P>*                                        _mh;
+    uint64_t                                                _cacheBaseAddress   = 0;
+    dyld3::MachOAnalyzer*                                   _mh;
     Diagnostics&                                            _diagnostics;
     const uint8_t*                                          _linkeditBias       = nullptr;
     unsigned                                                _linkeditSegIndex   = 0;
     bool                                                    _maskPointers       = false;
     bool                                                    _splitSegInfoV2     = false;
-    const char*                                             _installName        = nullptr;
-    macho_symtab_command<P>*                                _symTabCmd          = nullptr;
-    macho_dysymtab_command<P>*                              _dynSymTabCmd       = nullptr;
-    macho_dyld_info_command<P>*                             _dyldInfo           = nullptr;
-    macho_linkedit_data_command<P>*                         _splitSegInfoCmd    = nullptr;
-    macho_linkedit_data_command<P>*                         _functionStartsCmd  = nullptr;
-    macho_linkedit_data_command<P>*                         _dataInCodeCmd      = nullptr;
-    macho_linkedit_data_command<P>*                         _exportTrieCmd      = nullptr;
-    macho_linkedit_data_command<P>*                         _chainedFixupsCmd   = nullptr;
+    const char*                                             _dylibID            = nullptr;
+    symtab_command*                                         _symTabCmd          = nullptr;
+    dysymtab_command*                                       _dynSymTabCmd       = nullptr;
+    dyld_info_command*                                      _dyldInfo           = nullptr;
+    linkedit_data_command*                                  _splitSegInfoCmd    = nullptr;
+    linkedit_data_command*                                  _functionStartsCmd  = nullptr;
+    linkedit_data_command*                                  _dataInCodeCmd      = nullptr;
+    linkedit_data_command*                                  _exportTrieCmd      = nullptr;
+    linkedit_data_command*                                  _chainedFixupsCmd   = nullptr;
     uint16_t                                                _chainedFixupsFormat = 0;
     std::vector<uint64_t>                                   _segOrigStartAddresses;
+    std::vector<uint64_t>                                   _segSizes;
     std::vector<uint64_t>                                   _segSlides;
     std::vector<macho_segment_command<P>*>                  _segCmds;
     const std::vector<CacheBuilder::SegmentMappingInfo>&    _mappingInfo;
 };
 
 template <typename P>
-Adjustor<P>::Adjustor(DyldSharedCache* cacheBuffer, macho_header<P>* mh, const std::vector<CacheBuilder::SegmentMappingInfo>& mappingInfo, Diagnostics& diag)
-    : _cacheBuffer(cacheBuffer), _mh(mh), _diagnostics(diag), _mappingInfo(mappingInfo)
+Adjustor<P>::Adjustor(uint64_t cacheBaseAddress, dyld3::MachOAnalyzer* mh, const char* dylibID,
+                      const std::vector<CacheBuilder::SegmentMappingInfo>& mappingInfo, Diagnostics& diag)
+    : _cacheBaseAddress(cacheBaseAddress), _mh(mh), _diagnostics(diag), _dylibID(dylibID), _mappingInfo(mappingInfo)
 {
-    assert((mh->magic() == MH_MAGIC) || (mh->magic() == MH_MAGIC_64));
-    macho_segment_command<P>* segCmd;
-    const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
-    const uint32_t cmd_count = mh->ncmds();
-    const macho_load_command<P>* cmd = cmds;
-    unsigned segIndex = 0;
-    for (uint32_t i = 0; i < cmd_count; ++i) {
-        switch (cmd->cmd()) {
-            case LC_ID_DYLIB:
-                _installName = ((macho_dylib_command<P>*)cmd)->name();
-                break;
+    assert((_mh->magic == MH_MAGIC) || (_mh->magic == MH_MAGIC_64));
+
+    __block unsigned segIndex = 0;
+    mh->forEachLoadCommand(diag, ^(const load_command *cmd, bool &stop) {
+        switch ( cmd->cmd ) {
             case LC_SYMTAB:
-                _symTabCmd = (macho_symtab_command<P>*)cmd;
+                _symTabCmd = (symtab_command*)cmd;
                 break;
             case LC_DYSYMTAB:
-                _dynSymTabCmd = (macho_dysymtab_command<P>*)cmd;
+                _dynSymTabCmd = (dysymtab_command*)cmd;
                 break;
             case LC_DYLD_INFO:
             case LC_DYLD_INFO_ONLY:
-                _dyldInfo = (macho_dyld_info_command<P>*)cmd;
+                _dyldInfo = (dyld_info_command*)cmd;
                 break;
             case LC_SEGMENT_SPLIT_INFO:
-                _splitSegInfoCmd = (macho_linkedit_data_command<P>*)cmd;
+                _splitSegInfoCmd = (linkedit_data_command*)cmd;
                 break;
             case LC_FUNCTION_STARTS:
-                _functionStartsCmd = (macho_linkedit_data_command<P>*)cmd;
+                _functionStartsCmd = (linkedit_data_command*)cmd;
                 break;
             case LC_DATA_IN_CODE:
-                _dataInCodeCmd = (macho_linkedit_data_command<P>*)cmd;
+                _dataInCodeCmd = (linkedit_data_command*)cmd;
                 break;
             case LC_DYLD_CHAINED_FIXUPS:
-                _chainedFixupsCmd = (macho_linkedit_data_command<P>*)cmd;
-                _chainedFixupsFormat = dyld3::MachOAnalyzer::chainedPointerFormat((dyld_chained_fixups_header*)&_linkeditBias[_chainedFixupsCmd->dataoff()]);
+                _chainedFixupsCmd = (linkedit_data_command*)cmd;
+                _chainedFixupsFormat = dyld3::MachOAnalyzer::chainedPointerFormat((dyld_chained_fixups_header*)&_linkeditBias[_chainedFixupsCmd->dataoff]);
                 break;
             case LC_DYLD_EXPORTS_TRIE:
-                _exportTrieCmd = (macho_linkedit_data_command<P>*)cmd;
+                _exportTrieCmd = (linkedit_data_command*)cmd;
                 break;
             case macho_segment_command<P>::CMD:
-                segCmd = (macho_segment_command<P>*)cmd;
+                macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
                 _segCmds.push_back(segCmd);
                 _segOrigStartAddresses.push_back(segCmd->vmaddr());
+                _segSizes.push_back(segCmd->vmsize());
                 _segSlides.push_back(_mappingInfo[segIndex].dstCacheUnslidAddress - segCmd->vmaddr());
                 if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
                     _linkeditBias = (uint8_t*)_mappingInfo[segIndex].dstSegment - segCmd->fileoff();
@@ -163,33 +164,47 @@ Adjustor<P>::Adjustor(DyldSharedCache* cacheBuffer, macho_header<P>* mh, const s
                 ++segIndex;
                 break;
         }
-        cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-    }
-    _maskPointers = (P::E::get32(mh->cputype()) == CPU_TYPE_ARM64) || (P::E::get32(mh->cputype()) == CPU_TYPE_ARM64_32);
+    });
+
+    _maskPointers = (mh->cputype == CPU_TYPE_ARM64) || (mh->cputype == CPU_TYPE_ARM64_32);
     if ( _splitSegInfoCmd != NULL ) {
-        const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()];
+        const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff];
         _splitSegInfoV2 = (*infoStart == DYLD_CACHE_ADJ_V2_FORMAT);
     }
     else {
-        _diagnostics.error("missing LC_SEGMENT_SPLIT_INFO in %s", _installName);
+        bool canHaveMissingSplitSeg = false;
+#if BUILDING_APP_CACHE_UTIL
+        if ( mh->isKextBundle() ) {
+            if ( mh->isArch("x86_64") || mh->isArch("x86_64h") )
+                canHaveMissingSplitSeg = true;
+        }
+#endif
+        if ( !canHaveMissingSplitSeg )
+            _diagnostics.error("missing LC_SEGMENT_SPLIT_INFO in %s", _dylibID);
+    }
+
+    // Set the chained pointer format on old arm64e binaries using threaded rebase, and
+    // which don't have LC_DYLD_CHAINED_FIXUPS
+    if ( (_chainedFixupsCmd == nullptr) && mh->isArch("arm64e") ) {
+        _chainedFixupsFormat = DYLD_CHAINED_PTR_ARM64E;
     }
 }
 
 template <typename P>
 void Adjustor<P>::adjustImageForNewSegmentLocations(CacheBuilder::ASLR_Tracker& aslrTracker,
-                                                    CacheBuilder::LOH_Tracker& lohTracker,
-                                                    const CacheBuilder::CacheCoalescedText& coalescedText,
+                                                    CacheBuilder::LOH_Tracker* lohTracker,
+                                                    const CacheBuilder::CacheCoalescedText* coalescedText,
                                                     const CacheBuilder::DylibTextCoalescer& textCoalescer)
 {
     if ( _diagnostics.hasError() )
         return;
     if ( _splitSegInfoV2 ) {
         adjustReferencesUsingInfoV2(aslrTracker, lohTracker, coalescedText, textCoalescer);
-        adjustChainedFixups();
+        adjustChainedFixups(textCoalescer);
     }
     else if ( _chainedFixupsCmd != nullptr ) {
         // need to adjust the chain fixup segment_offset fields in LINKEDIT before chains can be walked
-        adjustChainedFixups();
+        adjustChainedFixups(textCoalescer);
         adjustRebaseChains(aslrTracker);
         adjustCode();
     }
@@ -202,13 +217,15 @@ void Adjustor<P>::adjustImageForNewSegmentLocations(CacheBuilder::ASLR_Tracker&
     adjustSymbolTable();
     if ( _diagnostics.hasError() )
         return;
+
+    adjustExternalRelocations();
     if ( _diagnostics.hasError() )
         return;
     rebuildLinkEditAndLoadCommands(textCoalescer);
 
 #if DEBUG
     Diagnostics  diag;
-    ((dyld3::MachOAnalyzer*)_mh)->validateDyldCacheDylib(diag, _installName);
+    _mh->validateDyldCacheDylib(diag, _dylibID);
     if ( diag.hasError() ) {
         fprintf(stderr, "%s\n", diag.errorMessage().c_str());
     }
@@ -226,7 +243,7 @@ uint64_t Adjustor<P>::slideForOrigAddress(uint64_t addr)
     if ( _maskPointers && (addr & 0xF000000000000000) ) {
         return slideForOrigAddress(addr & 0x0FFFFFFFFFFFFFFF);
     }
-    _diagnostics.error("slide not known for dylib address 0x%llX in %s", addr, _installName);
+    _diagnostics.error("slide not known for dylib address 0x%llX in %s", addr, _dylibID);
     return 0;
 }
 
@@ -239,58 +256,62 @@ void Adjustor<P>::rebuildLinkEditAndLoadCommands(const CacheBuilder::DylibTextCo
 
     // Remove: code signature, rebase info, code-sign-dirs, split seg info
     uint32_t chainedFixupsOffset = 0;
-    uint32_t chainedFixupsSize   = _chainedFixupsCmd ? _chainedFixupsCmd->datasize() : 0;
+    uint32_t chainedFixupsSize   = _chainedFixupsCmd ? _chainedFixupsCmd->datasize : 0;
     uint32_t bindOffset          = chainedFixupsOffset + chainedFixupsSize;
-    uint32_t bindSize            = _dyldInfo ? _dyldInfo->bind_size() : 0;
+    uint32_t bindSize            = _dyldInfo ? _dyldInfo->bind_size : 0;
     uint32_t weakBindOffset      = bindOffset + bindSize;
-    uint32_t weakBindSize        = _dyldInfo ? _dyldInfo->weak_bind_size() : 0;
+    uint32_t weakBindSize        = _dyldInfo ? _dyldInfo->weak_bind_size : 0;
     uint32_t lazyBindOffset      = weakBindOffset + weakBindSize;
-    uint32_t lazyBindSize        = _dyldInfo ? _dyldInfo->lazy_bind_size() : 0;
+    uint32_t lazyBindSize        = _dyldInfo ? _dyldInfo->lazy_bind_size : 0;
     uint32_t exportOffset        = lazyBindOffset + lazyBindSize;
     uint32_t exportSize          = (uint32_t)newTrieBytes.size();
     uint32_t splitSegInfoOffset  = exportOffset + exportSize;
-    uint32_t splitSegInfosSize   = (_splitSegInfoCmd ? _splitSegInfoCmd->datasize() : 0);
+    uint32_t splitSegInfosSize   = (_splitSegInfoCmd ? _splitSegInfoCmd->datasize : 0);
     uint32_t funcStartsOffset    = splitSegInfoOffset + splitSegInfosSize;
-    uint32_t funcStartsSize      = (_functionStartsCmd ? _functionStartsCmd->datasize() : 0);
+    uint32_t funcStartsSize      = (_functionStartsCmd ? _functionStartsCmd->datasize : 0);
     uint32_t dataInCodeOffset    = funcStartsOffset + funcStartsSize;
-    uint32_t dataInCodeSize      = (_dataInCodeCmd ? _dataInCodeCmd->datasize() : 0);
+    uint32_t dataInCodeSize      = (_dataInCodeCmd ? _dataInCodeCmd->datasize : 0);
     uint32_t symbolTableOffset   = dataInCodeOffset + dataInCodeSize;
-    uint32_t symbolTableSize     = _symTabCmd->nsyms() * sizeof(macho_nlist<P>);
+    uint32_t symbolTableSize     = _symTabCmd->nsyms * sizeof(macho_nlist<P>);
     uint32_t indirectTableOffset = symbolTableOffset + symbolTableSize;
-    uint32_t indirectTableSize   = _dynSymTabCmd->nindirectsyms() * sizeof(uint32_t);
-    uint32_t symbolStringsOffset = indirectTableOffset + indirectTableSize;
-    uint32_t symbolStringsSize   = _symTabCmd->strsize();
+    uint32_t indirectTableSize   = _dynSymTabCmd ? (_dynSymTabCmd->nindirectsyms * sizeof(uint32_t)) : 0;
+    uint32_t externalRelocOffset = indirectTableOffset + indirectTableSize;
+    uint32_t externalRelocSize   = _dynSymTabCmd ? (_dynSymTabCmd->nextrel * sizeof(relocation_info)) : 0;
+    uint32_t symbolStringsOffset = externalRelocOffset + externalRelocSize;
+    uint32_t symbolStringsSize   = _symTabCmd->strsize;
     uint32_t newLinkEditSize     = symbolStringsOffset + symbolStringsSize;
 
     size_t linkeditBufferSize = align(_segCmds[_linkeditSegIndex]->vmsize(), 12);
     if ( linkeditBufferSize < newLinkEditSize ) {
-        _diagnostics.error("LINKEDIT overflow in %s", _installName);
+        _diagnostics.error("LINKEDIT overflow in %s", _dylibID);
         return;
     }
 
     uint8_t* newLinkeditBufer = (uint8_t*)::calloc(linkeditBufferSize, 1);
     if ( chainedFixupsSize )
-        memcpy(&newLinkeditBufer[chainedFixupsOffset], &_linkeditBias[_chainedFixupsCmd->dataoff()], chainedFixupsSize);
+        memcpy(&newLinkeditBufer[chainedFixupsOffset], &_linkeditBias[_chainedFixupsCmd->dataoff], chainedFixupsSize);
     if ( bindSize )
-        memcpy(&newLinkeditBufer[bindOffset], &_linkeditBias[_dyldInfo->bind_off()], bindSize);
+        memcpy(&newLinkeditBufer[bindOffset], &_linkeditBias[_dyldInfo->bind_off], bindSize);
     if ( lazyBindSize )
-        memcpy(&newLinkeditBufer[lazyBindOffset], &_linkeditBias[_dyldInfo->lazy_bind_off()], lazyBindSize);
+        memcpy(&newLinkeditBufer[lazyBindOffset], &_linkeditBias[_dyldInfo->lazy_bind_off], lazyBindSize);
     if ( weakBindSize )
-        memcpy(&newLinkeditBufer[weakBindOffset], &_linkeditBias[_dyldInfo->weak_bind_off()], weakBindSize);
+        memcpy(&newLinkeditBufer[weakBindOffset], &_linkeditBias[_dyldInfo->weak_bind_off], weakBindSize);
     if ( exportSize )
         memcpy(&newLinkeditBufer[exportOffset], &newTrieBytes[0], exportSize);
     if ( splitSegInfosSize )
-        memcpy(&newLinkeditBufer[splitSegInfoOffset], &_linkeditBias[_splitSegInfoCmd->dataoff()], splitSegInfosSize);
+        memcpy(&newLinkeditBufer[splitSegInfoOffset], &_linkeditBias[_splitSegInfoCmd->dataoff], splitSegInfosSize);
     if ( funcStartsSize )
-        memcpy(&newLinkeditBufer[funcStartsOffset], &_linkeditBias[_functionStartsCmd->dataoff()], funcStartsSize);
+        memcpy(&newLinkeditBufer[funcStartsOffset], &_linkeditBias[_functionStartsCmd->dataoff], funcStartsSize);
     if ( dataInCodeSize )
-        memcpy(&newLinkeditBufer[dataInCodeOffset], &_linkeditBias[_dataInCodeCmd->dataoff()], dataInCodeSize);
+        memcpy(&newLinkeditBufer[dataInCodeOffset], &_linkeditBias[_dataInCodeCmd->dataoff], dataInCodeSize);
     if ( symbolTableSize )
-        memcpy(&newLinkeditBufer[symbolTableOffset], &_linkeditBias[_symTabCmd->symoff()], symbolTableSize);
+        memcpy(&newLinkeditBufer[symbolTableOffset], &_linkeditBias[_symTabCmd->symoff], symbolTableSize);
     if ( indirectTableSize )
-        memcpy(&newLinkeditBufer[indirectTableOffset], &_linkeditBias[_dynSymTabCmd->indirectsymoff()], indirectTableSize);
+        memcpy(&newLinkeditBufer[indirectTableOffset], &_linkeditBias[_dynSymTabCmd->indirectsymoff], indirectTableSize);
+    if ( externalRelocSize )
+        memcpy(&newLinkeditBufer[externalRelocOffset], &_linkeditBias[_dynSymTabCmd->extreloff], externalRelocSize);
     if ( symbolStringsSize )
-        memcpy(&newLinkeditBufer[symbolStringsOffset], &_linkeditBias[_symTabCmd->stroff()], symbolStringsSize);
+        memcpy(&newLinkeditBufer[symbolStringsOffset], &_linkeditBias[_symTabCmd->stroff], symbolStringsSize);
 
     memcpy(_mappingInfo[_linkeditSegIndex].dstSegment, newLinkeditBufer, newLinkEditSize);
     ::bzero(((uint8_t*)_mappingInfo[_linkeditSegIndex].dstSegment)+newLinkEditSize, linkeditBufferSize-newLinkEditSize);
@@ -298,73 +319,71 @@ void Adjustor<P>::rebuildLinkEditAndLoadCommands(const CacheBuilder::DylibTextCo
     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>));
-    uint32_t cmd_count = _mh->ncmds();
-    const macho_load_command<P>* cmd = cmds;
-    const unsigned origLoadCommandsSize = _mh->sizeofcmds();
-    unsigned bytesRemaining = origLoadCommandsSize;
-    unsigned removedCount = 0;
-    unsigned segIndex = 0;
-    for (uint32_t i = 0; i < cmd_count; ++i) {
-        macho_symtab_command<P>*           symTabCmd;
-        macho_dysymtab_command<P>*         dynSymTabCmd;
-        macho_dyld_info_command<P>*        dyldInfo;
-        macho_linkedit_data_command<P>*    functionStartsCmd;
-        macho_linkedit_data_command<P>*    dataInCodeCmd;
-        macho_linkedit_data_command<P>*    chainedFixupsCmd;
-        macho_linkedit_data_command<P>*    exportTrieCmd;
-        macho_linkedit_data_command<P>*    splitSegInfoCmd;
+    
+    __block unsigned segIndex = 0;
+    _mh->forEachLoadCommand(_diagnostics, ^(const load_command *cmd, bool &stop) {
+        symtab_command*                    symTabCmd;
+        dysymtab_command*                  dynSymTabCmd;
+        dyld_info_command*                 dyldInfo;
+        linkedit_data_command*             functionStartsCmd;
+        linkedit_data_command*             dataInCodeCmd;
+        linkedit_data_command*             chainedFixupsCmd;
+        linkedit_data_command*             exportTrieCmd;
+        linkedit_data_command*             splitSegInfoCmd;
         macho_segment_command<P>*          segCmd;
         macho_routines_command<P>*         routinesCmd;
-        macho_dylib_command<P>*            dylibIDCmd;
-        uint32_t cmdSize = cmd->cmdsize();
+        dylib_command*                     dylibIDCmd;
         int32_t segFileOffsetDelta;
-        bool remove = false;
-        switch ( cmd->cmd() ) {
+        switch ( cmd->cmd ) {
             case LC_ID_DYLIB:
-                dylibIDCmd = (macho_dylib_command<P>*)cmd;
-                dylibIDCmd->set_timestamp(2); // match what static linker sets in LC_LOAD_DYLIB
+                dylibIDCmd = (dylib_command*)cmd;
+                dylibIDCmd->dylib.timestamp = 2; // match what static linker sets in LC_LOAD_DYLIB
                 break;
             case LC_SYMTAB:
-                symTabCmd = (macho_symtab_command<P>*)cmd;
-                symTabCmd->set_symoff(linkeditStartOffset+symbolTableOffset);
-                symTabCmd->set_stroff(linkeditStartOffset+symbolStringsOffset);
-              break;
+                symTabCmd = (symtab_command*)cmd;
+                symTabCmd->symoff = linkeditStartOffset + symbolTableOffset;
+                symTabCmd->stroff = linkeditStartOffset + symbolStringsOffset;
+                break;
             case LC_DYSYMTAB:
-                dynSymTabCmd = (macho_dysymtab_command<P>*)cmd;
-                dynSymTabCmd->set_indirectsymoff(linkeditStartOffset+indirectTableOffset);
+                dynSymTabCmd = (dysymtab_command*)cmd;
+                dynSymTabCmd->indirectsymoff = linkeditStartOffset + indirectTableOffset;
+                // Clear local relocations (ie, old style rebases) as they were tracked earlier when we applied split seg
+                dynSymTabCmd->locreloff = 0;
+                dynSymTabCmd->nlocrel = 0 ;
+                // Update external relocations as we need these later to resolve binds from kexts
+                dynSymTabCmd->extreloff = linkeditStartOffset + externalRelocOffset;
                 break;
             case LC_DYLD_INFO:
             case LC_DYLD_INFO_ONLY:
-                dyldInfo = (macho_dyld_info_command<P>*)cmd;
-                dyldInfo->set_rebase_off(0);
-                dyldInfo->set_rebase_size(0);
-                dyldInfo->set_bind_off(bindSize ? linkeditStartOffset+bindOffset : 0);
-                dyldInfo->set_bind_size(bindSize);
-                dyldInfo->set_weak_bind_off(weakBindSize ? linkeditStartOffset+weakBindOffset : 0);
-                dyldInfo->set_weak_bind_size(weakBindSize);
-                dyldInfo->set_lazy_bind_off(lazyBindSize ? linkeditStartOffset+lazyBindOffset : 0);
-                dyldInfo->set_lazy_bind_size(lazyBindSize);
-                dyldInfo->set_export_off(exportSize ? linkeditStartOffset+exportOffset : 0);
-                dyldInfo->set_export_size(exportSize);
+                dyldInfo = (dyld_info_command*)cmd;
+                dyldInfo->rebase_off = 0;
+                dyldInfo->rebase_size = 0;
+                dyldInfo->bind_off = bindSize ? linkeditStartOffset + bindOffset : 0;
+                dyldInfo->bind_size = bindSize;
+                dyldInfo->weak_bind_off = weakBindSize ? linkeditStartOffset + weakBindOffset : 0;
+                dyldInfo->weak_bind_size = weakBindSize;
+                dyldInfo->lazy_bind_off = lazyBindSize ? linkeditStartOffset + lazyBindOffset : 0;
+                dyldInfo->lazy_bind_size = lazyBindSize;
+                dyldInfo->export_off = exportSize ? linkeditStartOffset + exportOffset : 0;
+                dyldInfo->export_size = exportSize;
                 break;
             case LC_FUNCTION_STARTS:
-                functionStartsCmd = (macho_linkedit_data_command<P>*)cmd;
-                functionStartsCmd->set_dataoff(linkeditStartOffset+funcStartsOffset);
-               break;
+                functionStartsCmd = (linkedit_data_command*)cmd;
+                functionStartsCmd->dataoff = linkeditStartOffset + funcStartsOffset;
+                break;
             case LC_DATA_IN_CODE:
-                dataInCodeCmd = (macho_linkedit_data_command<P>*)cmd;
-                dataInCodeCmd->set_dataoff(linkeditStartOffset+dataInCodeOffset);
+                dataInCodeCmd = (linkedit_data_command*)cmd;
+                dataInCodeCmd->dataoff = linkeditStartOffset + dataInCodeOffset;
                 break;
             case LC_DYLD_CHAINED_FIXUPS:
-                chainedFixupsCmd = (macho_linkedit_data_command<P>*)cmd;
-                chainedFixupsCmd->set_dataoff(chainedFixupsSize ? linkeditStartOffset+chainedFixupsOffset : 0);
-                chainedFixupsCmd->set_datasize(chainedFixupsSize);
+                chainedFixupsCmd = (linkedit_data_command*)cmd;
+                chainedFixupsCmd->dataoff = chainedFixupsSize ? linkeditStartOffset + chainedFixupsOffset : 0;
+                chainedFixupsCmd->datasize = chainedFixupsSize;
                 break;
             case LC_DYLD_EXPORTS_TRIE:
-                exportTrieCmd = (macho_linkedit_data_command<P>*)cmd;
-                exportTrieCmd->set_dataoff(exportSize ? linkeditStartOffset+exportOffset : 0);
-                exportTrieCmd->set_datasize(exportSize);
+                exportTrieCmd = (linkedit_data_command*)cmd;
+                exportTrieCmd->dataoff = exportSize ? linkeditStartOffset + exportOffset : 0;
+                exportTrieCmd->datasize = exportSize;
                 break;
             case macho_routines_command<P>::CMD:
                 routinesCmd = (macho_routines_command<P>*)cmd;
@@ -382,9 +401,21 @@ void Adjustor<P>::rebuildLinkEditAndLoadCommands(const CacheBuilder::DylibTextCo
                 if ( segCmd->nsects() > 0 ) {
                     macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
                     macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
-
+                    
                     for (macho_section<P>*  sect=sectionsStart; sect < sectionsEnd; ++sect) {
-                        if ( (strcmp(segCmd->segname(), "__TEXT") == 0) && textCoalescer.sectionWasCoalesced(sect->sectname())) {
+                        bool coalescedSection = false;
+                        if ( textCoalescer.sectionWasCoalesced(sect->segname(), sect->sectname())) {
+                            coalescedSection = true;
+                        }
+#if BUILDING_APP_CACHE_UTIL
+                        if ( strcmp(segCmd->segname(), "__CTF") == 0 ) {
+                            // The kernel __CTF segment data is completely removed when we link the baseKC
+                            if ( _mh->isStaticExecutable() )
+                                coalescedSection = true;
+                        }
+#endif
+
+                        if ( coalescedSection ) {
                             // Put coalesced sections at the end of the segment
                             sect->set_addr(segCmd->vmaddr() + segCmd->filesize());
                             sect->set_offset(0);
@@ -398,13 +429,20 @@ void Adjustor<P>::rebuildLinkEditAndLoadCommands(const CacheBuilder::DylibTextCo
                 }
                 ++segIndex;
                 break;
-             case LC_RPATH:
-                _diagnostics.warning("dyld shared cache does not support LC_RPATH found in %s", _installName);
-                remove = true;
-                break;
             case LC_SEGMENT_SPLIT_INFO:
-                splitSegInfoCmd = (macho_linkedit_data_command<P>*)cmd;
-                splitSegInfoCmd->set_dataoff(linkeditStartOffset+splitSegInfoOffset);
+                splitSegInfoCmd = (linkedit_data_command*)cmd;
+                splitSegInfoCmd->dataoff = linkeditStartOffset + splitSegInfoOffset;
+                break;
+            default:
+                break;
+        }
+    });
+
+    _mh->removeLoadCommand(_diagnostics, ^(const load_command *cmd, bool &remove, bool &stop) {
+        switch ( cmd->cmd ) {
+            case LC_RPATH:
+                _diagnostics.warning("dyld shared cache does not support LC_RPATH found in %s", _dylibID);
+                remove = true;
                 break;
             case LC_CODE_SIGNATURE:
             case LC_DYLIB_CODE_SIGN_DRS:
@@ -413,40 +451,29 @@ void Adjustor<P>::rebuildLinkEditAndLoadCommands(const CacheBuilder::DylibTextCo
             default:
                 break;
         }
-        macho_load_command<P>* nextCmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmdSize);
-        if ( remove ) {
-            ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining);
-            ++removedCount;
-        }
-        else {
-            bytesRemaining -= cmdSize;
-            cmd = nextCmd;
-        }
-    }
-    // zero out stuff removed
-    ::bzero((void*)cmd, bytesRemaining);
-    // update header
-    _mh->set_ncmds(cmd_count-removedCount);
-    _mh->set_sizeofcmds(origLoadCommandsSize-bytesRemaining);
-    _mh->set_flags(_mh->flags() | 0x80000000);
+    });
+    _mh->flags |= 0x80000000;
 }
 
 
 template <typename P>
 void Adjustor<P>::adjustSymbolTable()
 {
-    macho_nlist<P>*  symbolTable = (macho_nlist<P>*)&_linkeditBias[_symTabCmd->symoff()];
+    if ( _dynSymTabCmd == nullptr )
+        return;
+
+    macho_nlist<P>*  symbolTable = (macho_nlist<P>*)&_linkeditBias[_symTabCmd->symoff];
 
     // adjust global symbol table entries
-    macho_nlist<P>* lastExport = &symbolTable[_dynSymTabCmd->iextdefsym()+_dynSymTabCmd->nextdefsym()];
-    for (macho_nlist<P>* entry = &symbolTable[_dynSymTabCmd->iextdefsym()]; entry < lastExport; ++entry) {
+    macho_nlist<P>* lastExport = &symbolTable[_dynSymTabCmd->iextdefsym + _dynSymTabCmd->nextdefsym];
+    for (macho_nlist<P>* entry = &symbolTable[_dynSymTabCmd->iextdefsym]; entry < lastExport; ++entry) {
         if ( (entry->n_type() & N_TYPE) == N_SECT )
             entry->set_n_value(entry->n_value() + slideForOrigAddress(entry->n_value()));
     }
 
     // adjust local symbol table entries
-    macho_nlist<P>*  lastLocal = &symbolTable[_dynSymTabCmd->ilocalsym()+_dynSymTabCmd->nlocalsym()];
-    for (macho_nlist<P>* entry = &symbolTable[_dynSymTabCmd->ilocalsym()]; entry < lastLocal; ++entry) {
+    macho_nlist<P>*  lastLocal = &symbolTable[_dynSymTabCmd->ilocalsym+_dynSymTabCmd->nlocalsym];
+    for (macho_nlist<P>* entry = &symbolTable[_dynSymTabCmd->ilocalsym]; entry < lastLocal; ++entry) {
         if ( (entry->n_sect() != NO_SECT) && ((entry->n_type() & N_STAB) == 0) )
             entry->set_n_value(entry->n_value() + slideForOrigAddress(entry->n_value()));
     }
@@ -454,27 +481,77 @@ void Adjustor<P>::adjustSymbolTable()
 
 
 template <typename P>
-void Adjustor<P>::adjustChainedFixups()
+void Adjustor<P>::adjustChainedFixups(const CacheBuilder::DylibTextCoalescer& textCoalescer)
 {
     if ( _chainedFixupsCmd == nullptr )
         return;
 
     // Pass a start hint in to withChainStarts which takes account of the LINKEDIT shifting but we haven't
     // yet updated that LC_SEGMENT to point to the new data
-    const dyld_chained_fixups_header* header = (dyld_chained_fixups_header*)&_linkeditBias[_chainedFixupsCmd->dataoff()];
+    const dyld_chained_fixups_header* header = (dyld_chained_fixups_header*)&_linkeditBias[_chainedFixupsCmd->dataoff];
     uint64_t startsOffset = ((uint64_t)header + header->starts_offset) - (uint64_t)_mh;
 
     // segment_offset in dyld_chained_starts_in_segment is wrong.  We need to move it to the new segment offset
-    ((dyld3::MachOAnalyzer*)_mh)->withChainStarts(_diagnostics, startsOffset, ^(const dyld_chained_starts_in_image* starts) {
+    _mh->withChainStarts(_diagnostics, startsOffset, ^(const dyld_chained_starts_in_image* starts) {
         for (uint32_t segIndex=0; segIndex < starts->seg_count; ++segIndex) {
             if ( starts->seg_info_offset[segIndex] == 0 )
                 continue;
             dyld_chained_starts_in_segment* segInfo = (dyld_chained_starts_in_segment*)((uint8_t*)starts + starts->seg_info_offset[segIndex]);
             segInfo->segment_offset = (uint64_t)_mappingInfo[segIndex].dstSegment - (uint64_t)_mh;
+
+            // If the whole segment was coalesced the remove its chain starts
+            if ( textCoalescer.segmentWasCoalesced(_segCmds[segIndex]->segname()) ) {
+                segInfo->page_count = 0;
+            }
         }
     });
 }
 
+template <typename P>
+static uint64_t externalRelocBaseAddress(const dyld3::MachOAnalyzer* ma,
+                                         std::vector<macho_segment_command<P>*> segCmds,
+                                         std::vector<uint64_t> segOrigStartAddresses)
+{
+    if ( ma->isArch("x86_64") || ma->isArch("x86_64h") ) {
+#if BUILDING_APP_CACHE_UTIL
+        if ( ma->isKextBundle() ) {
+            // for kext bundles the reloc base address starts at __TEXT segment
+            return segOrigStartAddresses[0];
+        }
+#endif
+        // for x86_64 reloc base address starts at first writable segment (usually __DATA)
+        for (uint32_t i=0; i < segCmds.size(); ++i) {
+            if ( segCmds[i]->initprot() & VM_PROT_WRITE )
+                return segOrigStartAddresses[i];
+        }
+    }
+    // For everyone else we start at 0
+    return 0;
+}
+
+
+template <typename P>
+void Adjustor<P>::adjustExternalRelocations()
+{
+    if ( _dynSymTabCmd == nullptr )
+        return;
+
+    // section index 0 refers to mach_header
+    uint64_t baseAddress = _mappingInfo[0].dstCacheUnslidAddress;
+
+    const uint64_t                  relocsStartAddress = externalRelocBaseAddress(_mh, _segCmds, _segOrigStartAddresses);
+    relocation_info*                relocsStart = (relocation_info*)&_linkeditBias[_dynSymTabCmd->extreloff];
+    relocation_info*                relocsEnd   = &relocsStart[_dynSymTabCmd->nextrel];
+    for (relocation_info* reloc = relocsStart; reloc < relocsEnd; ++reloc) {
+        // External relocations should be relative to the base address of the mach-o as otherwise they
+        // probably won't fit in 32-bits.
+        uint64_t newAddress = reloc->r_address + slideForOrigAddress(relocsStartAddress + reloc->r_address);
+        newAddress -= baseAddress;
+        reloc->r_address = (int32_t)newAddress;
+        assert((uint64_t)reloc->r_address == newAddress);
+    }
+}
+
 template <typename P>
 void Adjustor<P>::slidePointer(int segIndex, uint64_t segOffset, uint8_t type, CacheBuilder::ASLR_Tracker& aslrTracker)
 {
@@ -497,7 +574,7 @@ void Adjustor<P>::slidePointer(int segIndex, uint64_t segOffset, uint8_t type, C
         case REBASE_TYPE_TEXT_PCREL32:
             // general text relocs not support
         default:
-            _diagnostics.error("unknown rebase type 0x%02X in %s", type, _installName);
+            _diagnostics.error("unknown rebase type 0x%02X in %s", type, _dylibID);
     }
 }
 
@@ -554,13 +631,57 @@ static uint32_t setArmWord(uint32_t instruction, uint16_t word) {
 
 
 template <typename P>
-void Adjustor<P>::convertArm64eRebaseToIntermediate(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr, CacheBuilder::ASLR_Tracker& aslrTracker, uint64_t targetSlide)
+void Adjustor<P>::convertArm64eRebaseToIntermediate(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr, CacheBuilder::ASLR_Tracker& aslrTracker,
+                                                    uint64_t targetSlide, bool convertRebaseChains)
 {
     assert(chainPtr->arm64e.authRebase.bind == 0);
+    assert( (_chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E)
+           || (_chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E_USERLAND)
+           || (_chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E_USERLAND24)
+           || (_chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E_KERNEL) );
     dyld3::MachOLoaded::ChainedFixupPointerOnDisk orgPtr = *chainPtr;
     dyld3::MachOLoaded::ChainedFixupPointerOnDisk tmp;
     if ( chainPtr->arm64e.authRebase.auth ) {
         uint64_t targetVMAddr = orgPtr.arm64e.authRebase.target + _segOrigStartAddresses[0] + targetSlide;
+
+        // The merging code may have set the high bits, eg, to a tagged pointer
+        // Note authRebase has no high8, so this is invalid if it occurs
+        uint8_t high8 = targetVMAddr >> 56;
+        if ( high8 ) {
+            // The kernel uses the high bits in the vmAddr, so don't error there
+            bool badPointer = true;
+            if ( _chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E_KERNEL ) {
+                uint64_t vmOffset = targetVMAddr - _cacheBaseAddress;
+                if ( (vmOffset >> 56) == 0 )
+                    badPointer = false;
+            }
+
+            if ( badPointer ) {
+                _diagnostics.error("Cannot set tag on pointer in '%s' as high bits are incompatible with pointer authentication", _dylibID);
+                return;
+            }
+        }
+
+        if ( _chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E_USERLAND ) {
+            // <rdar://60351693> the rebase target is a vmoffset, so we need to switch to tracking the target out of line
+            aslrTracker.setAuthData(chainPtr, chainPtr->arm64e.authRebase.diversity, chainPtr->arm64e.authRebase.addrDiv, chainPtr->arm64e.authRebase.key);
+            aslrTracker.setRebaseTarget64(chainPtr, targetVMAddr);
+            chainPtr->arm64e.rebase.target = 0; // actual target vmAddr stored in side table
+            chainPtr->arm64e.rebase.high8  = 0;
+            chainPtr->arm64e.rebase.next   = orgPtr.arm64e.rebase.next;
+            chainPtr->arm64e.rebase.bind   = 0;
+            chainPtr->arm64e.rebase.auth   = 0;
+            return;
+        }
+
+        if ( convertRebaseChains ) {
+            // This chain has been broken by merging CF constants.
+            // Instead of trying to maintain the chain, just set the raw value now
+            aslrTracker.setAuthData(chainPtr, chainPtr->arm64e.authRebase.diversity, chainPtr->arm64e.authRebase.addrDiv, chainPtr->arm64e.authRebase.key);
+            chainPtr->raw64 = targetVMAddr;
+            return;
+        }
+
         // we need to change the rebase to point to the new address in the dyld cache, but it may not fit
         tmp.arm64e.authRebase.target = targetVMAddr;
         if ( tmp.arm64e.authRebase.target == targetVMAddr ) {
@@ -583,7 +704,7 @@ void Adjustor<P>::convertArm64eRebaseToIntermediate(dyld3::MachOLoaded::ChainedF
         // target cannot fit into rebase chain, so store target in side table
         aslrTracker.setAuthData(chainPtr, chainPtr->arm64e.authRebase.diversity, chainPtr->arm64e.authRebase.addrDiv, chainPtr->arm64e.authRebase.key);
         aslrTracker.setRebaseTarget64(chainPtr, targetVMAddr);
-        chainPtr->arm64e.rebase.target = CacheBuilder::kRebaseTargetInSideTableArm64e; // magic value that means look in side table
+        chainPtr->arm64e.rebase.target = 0; // actual target vmAddr stored in side table
         chainPtr->arm64e.rebase.high8  = 0;
         chainPtr->arm64e.rebase.next   = orgPtr.arm64e.rebase.next;
         chainPtr->arm64e.rebase.bind   = 0;
@@ -591,16 +712,58 @@ void Adjustor<P>::convertArm64eRebaseToIntermediate(dyld3::MachOLoaded::ChainedF
         return;
     }
     else {
-        uint64_t targetVMAddr = orgPtr.arm64e.rebase.target + targetSlide;
+        uint64_t targetVMAddr = 0;
+        switch (_chainedFixupsFormat) {
+            case DYLD_CHAINED_PTR_ARM64E:
+                targetVMAddr = orgPtr.arm64e.rebase.target + targetSlide;
+                break;
+            case DYLD_CHAINED_PTR_ARM64E_USERLAND:
+            case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
+                // <rdar://60351693> the rebase target is a vmoffset, so we need to switch to tracking the target out of line
+                aslrTracker.setRebaseTarget64(chainPtr, orgPtr.arm64e.rebase.target + targetSlide);
+                orgPtr.arm64e.rebase.target = 0;
+                targetVMAddr = 0;
+                break;
+            case DYLD_CHAINED_PTR_ARM64E_KERNEL:
+                targetVMAddr = orgPtr.arm64e.rebase.target + _segOrigStartAddresses[0] + targetSlide;
+                break;
+            default:
+                _diagnostics.error("Unknown chain format");
+                return;
+        }
+
+        // The merging code may have set the high bits, eg, to a tagged pointer
+        uint8_t high8 = targetVMAddr >> 56;
+        if ( chainPtr->arm64e.rebase.high8 ) {
+            if ( high8 ) {
+                _diagnostics.error("Cannot set tag on pointer as high bits are in use");
+                return;
+            }
+            aslrTracker.setHigh8(chainPtr, chainPtr->arm64e.rebase.high8);
+        } else {
+            if ( high8 ) {
+                aslrTracker.setHigh8(chainPtr, high8);
+                targetVMAddr &= 0x00FFFFFFFFFFFFFF;
+            }
+        }
+
+        if ( convertRebaseChains ) {
+            // This chain has been broken by merging CF constants.
+            // Instead of trying to maintain the chain, just set the raw value now
+            chainPtr->raw64 = targetVMAddr;
+            return;
+        }
+
         tmp.arm64e.rebase.target = targetVMAddr;
         if ( tmp.arm64e.rebase.target == targetVMAddr ) {
             // target dyld cache address fits in plain rebase, so all we need to do is adjust that
             chainPtr->arm64e.rebase.target = targetVMAddr;
             return;
         }
+
         // target cannot fit into rebase chain, so store target in side table
         aslrTracker.setRebaseTarget64(chainPtr, targetVMAddr);
-        chainPtr->arm64e.rebase.target = CacheBuilder::kRebaseTargetInSideTableArm64e; // magic value that means look in side table
+        chainPtr->arm64e.rebase.target = 0; // actual target vmAddr stored in side table
     }
 }
 
@@ -608,10 +771,27 @@ void Adjustor<P>::convertArm64eRebaseToIntermediate(dyld3::MachOLoaded::ChainedF
 template <typename P>
 void Adjustor<P>::convertGeneric64RebaseToIntermediate(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr, CacheBuilder::ASLR_Tracker& aslrTracker, uint64_t targetSlide)
 {
+    assert( (_chainedFixupsFormat == DYLD_CHAINED_PTR_64) || (_chainedFixupsFormat == DYLD_CHAINED_PTR_64_OFFSET) );
     dyld3::MachOLoaded::ChainedFixupPointerOnDisk orgPtr = *chainPtr;
     dyld3::MachOLoaded::ChainedFixupPointerOnDisk tmp;
 
-    uint64_t targetVMAddr = orgPtr.generic64.rebase.target + targetSlide;
+    uint64_t targetVMAddr = 0;
+    switch (_chainedFixupsFormat) {
+        case DYLD_CHAINED_PTR_64:
+            targetVMAddr = orgPtr.generic64.rebase.target + targetSlide;
+            break;
+        case DYLD_CHAINED_PTR_64_OFFSET:
+            // <rdar://60351693> the rebase target is a vmoffset, so we need to switch to tracking the target out of line
+            targetVMAddr = orgPtr.generic64.rebase.target + _segOrigStartAddresses[0] + targetSlide;
+            aslrTracker.setRebaseTarget64(chainPtr, targetVMAddr);
+            chainPtr->generic64.rebase.target = 0;
+            return;
+            break;
+        default:
+            _diagnostics.error("Unknown chain format");
+            return;
+    }
+
     // we need to change the rebase to point to the new address in the dyld cache, but it may not fit
     tmp.generic64.rebase.target = targetVMAddr;
     if ( tmp.generic64.rebase.target == targetVMAddr ) {
@@ -622,13 +802,14 @@ void Adjustor<P>::convertGeneric64RebaseToIntermediate(dyld3::MachOLoaded::Chain
 
     // target cannot fit into rebase chain, so store target in side table
      aslrTracker.setRebaseTarget64(chainPtr, targetVMAddr);
-     chainPtr->generic64.rebase.target = CacheBuilder::kRebaseTargetInSideTableArm64; // magic value that means look in side table
+     chainPtr->generic64.rebase.target = 0; // actual target vmAddr stored in side table
 }
 
 
 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,
+                                  bool convertRebaseChains,
                                   CacheBuilder::ASLR_Tracker& aslrTracker, CacheBuilder::LOH_Tracker* lohTracker,
                                   uint32_t*& lastMappedAddr32, uint32_t& lastKind, uint64_t& lastToNewAddress)
 {
@@ -638,7 +819,7 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
     uint32_t* mappedAddr32 = 0;
     uint32_t instruction;
     dyld3::MachOLoaded::ChainedFixupPointerOnDisk* chainPtr;
-    int64_t offsetAdjust;
+    uint32_t newPageOffset;
     int64_t delta;
     switch ( kind ) {
         case DYLD_CACHE_ADJ_V2_DELTA_32:
@@ -647,7 +828,7 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
             delta = (int32_t)value32;
             delta += adjust;
             if ( (delta > 0x80000000) || (-delta > 0x80000000) ) {
-                _diagnostics.error("DYLD_CACHE_ADJ_V2_DELTA_32 can't be adjust by 0x%016llX in %s", adjust, _installName);
+                _diagnostics.error("DYLD_CACHE_ADJ_V2_DELTA_32 can't be adjust by 0x%016llX in %s", adjust, _dylibID);
                 return;
             }
             P::E::set32(*mappedAddr32, (int32_t)delta);
@@ -665,17 +846,27 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
                             aslrTracker.add(mappedAddr32);
                             uint32_t target = (uint32_t)(chainPtr->generic32.rebase.target + targetSlide);
                             aslrTracker.setRebaseTarget32(chainPtr, target);
-                            chainPtr->generic32.rebase.target = CacheBuilder::kRebaseTargetInSideTableGeneric32;
+                            chainPtr->generic32.rebase.target = 0; // actual target stored in side table
                         }
                         break;
                     default:
-                        _diagnostics.error("unknown 32-bit chained fixup format %d in %s", _chainedFixupsFormat, _installName);
+                        _diagnostics.error("unknown 32-bit chained fixup format %d in %s", _chainedFixupsFormat, _dylibID);
                         break;
                 }
             }
+            else if ( _mh->usesClassicRelocationsInKernelCollection() ) {
+                // Classic relocs are not guaranteed to be aligned, so always store them in the side table
+                if ( (uint32_t)toNewAddress != (uint32_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, _dylibID);
+                    return;
+                }
+                aslrTracker.setRebaseTarget32(mappedAddr32, (uint32_t)toNewAddress);
+                E::set32(*mappedAddr32, 0);
+                aslrTracker.add(mappedAddr32);
+            }
             else {
                 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);
+                    _diagnostics.error("bad DYLD_CACHE_ADJ_V2_POINTER_32 value not as expected at address 0x%llX in %s", fromNewAddress, _dylibID);
                     return;
                 }
                 E::set32(*mappedAddr32, (uint32_t)toNewAddress);
@@ -688,14 +879,18 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
                 chainPtr = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)mappedAddr64;
                 switch (_chainedFixupsFormat) {
                     case DYLD_CHAINED_PTR_ARM64E:
+                    case DYLD_CHAINED_PTR_ARM64E_USERLAND:
+                    case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
+                    case DYLD_CHAINED_PTR_ARM64E_KERNEL:
                         // ignore binds and adjust rebases to new segment locations
                         if ( chainPtr->arm64e.authRebase.bind == 0 ) {
-                            convertArm64eRebaseToIntermediate(chainPtr, aslrTracker, targetSlide);
+                            convertArm64eRebaseToIntermediate(chainPtr, aslrTracker, targetSlide, convertRebaseChains);
                             // Note, the pointer remains a chain with just the target of the rebase adjusted to the new target location
                             aslrTracker.add(chainPtr);
                         }
                         break;
                     case DYLD_CHAINED_PTR_64:
+                    case DYLD_CHAINED_PTR_64_OFFSET:
                         // ignore binds and adjust rebases to new segment locations
                         if ( chainPtr->generic64.rebase.bind == 0 ) {
                             convertGeneric64RebaseToIntermediate(chainPtr, aslrTracker, targetSlide);
@@ -703,18 +898,26 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
                             aslrTracker.add(chainPtr);
                         }
                         break;
-                    case DYLD_CHAINED_PTR_64_OFFSET:
-                    case DYLD_CHAINED_PTR_ARM64E_OFFSET:
-                        _diagnostics.error("unhandled 64-bit chained fixup format %d in %s", _chainedFixupsFormat, _installName);
-                        break;
                     default:
-                        _diagnostics.error("unknown 64-bit chained fixup format %d in %s", _chainedFixupsFormat, _installName);
+                        _diagnostics.error("unknown 64-bit chained fixup format %d in %s", _chainedFixupsFormat, _dylibID);
                         break;
                 }
             }
+            else if ( _mh->usesClassicRelocationsInKernelCollection() ) {
+                if ( toNewAddress != (E::get64(*mappedAddr64) + targetSlide) ) {
+                    _diagnostics.error("bad DYLD_CACHE_ADJ_V2_POINTER_64 value not as expected at address 0x%llX in %s", fromNewAddress, _dylibID);
+                    return;
+                }
+                aslrTracker.setRebaseTarget64(mappedAddr64, toNewAddress);
+                E::set64(*mappedAddr64, 0); // actual target vmAddr stored in side table
+                aslrTracker.add(mappedAddr64);
+                uint8_t high8 = toNewAddress >> 56;
+                if ( high8 )
+                    aslrTracker.setHigh8(mappedAddr64, high8);
+            }
             else {
                 if ( toNewAddress != (E::get64(*mappedAddr64) + targetSlide) ) {
-                    _diagnostics.error("bad DYLD_CACHE_ADJ_V2_POINTER_64 value not as expected at address 0x%llX in %s", fromNewAddress, _installName);
+                    _diagnostics.error("bad DYLD_CACHE_ADJ_V2_POINTER_64 value not as expected at address 0x%llX in %s", fromNewAddress, _dylibID);
                     return;
                 }
                 E::set64(*mappedAddr64, toNewAddress);
@@ -729,7 +932,7 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
             chainPtr = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)mappedAddr;
             // ignore binds, they are proccessed later
             if ( chainPtr->arm64e.authRebase.bind == 0 ) {
-                convertArm64eRebaseToIntermediate(chainPtr, aslrTracker, targetSlide);
+                convertArm64eRebaseToIntermediate(chainPtr, aslrTracker, targetSlide, convertRebaseChains);
                 // Note, the pointer remains a chain with just the target of the rebase adjusted to the new target location
                 aslrTracker.add(chainPtr);
             }
@@ -746,7 +949,7 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
             value32 = P::E::get32(*mappedAddr32);
             value64 = toNewAddress - imageStartAddress;
             if ( value64 > imageEndAddress ) {
-                _diagnostics.error("DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 can't be adjust to 0x%016llX in %s", toNewAddress, _installName);
+                _diagnostics.error("DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 can't be adjust to 0x%016llX in %s", toNewAddress, _dylibID);
                 return;
             }
             P::E::set32(*mappedAddr32, (uint32_t)value64);
@@ -760,7 +963,7 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
                 int64_t pageDistance = ((toNewAddress & ~0xFFF) - (fromNewAddress & ~0xFFF));
                 int64_t newPage21 = pageDistance >> 12;
                 if ( (newPage21 > 2097151) || (newPage21 < -2097151) ) {
-                    _diagnostics.error("DYLD_CACHE_ADJ_V2_ARM64_ADRP can't be adjusted that far in %s", _installName);
+                    _diagnostics.error("DYLD_CACHE_ADJ_V2_ARM64_ADRP can't be adjusted that far in %s", _dylibID);
                     return;
                 }
                 instruction = (instruction & 0x9F00001F) | ((newPage21 << 29) & 0x60000000) | ((newPage21 << 3) & 0x00FFFFE0);
@@ -775,83 +978,81 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
             if (lohTracker)
                 (*lohTracker)[toNewAddress].insert(mappedAddr);
             instruction = P::E::get32(*mappedAddr32);
-            offsetAdjust = (adjust & 0xFFF);
-            if ( offsetAdjust == 0 )
-                break;
+            // This is a page offset, so if we pack both the __TEXT page with the add/ldr, and
+            // the destination page with the target data, then the adjust isn't correct.  Instead
+            // we always want the page offset of the target, ignoring where the source add/ldr slid
+            newPageOffset = (uint32_t)(toNewAddress & 0xFFF);
             if ( (instruction & 0x3B000000) == 0x39000000 ) {
                 // LDR/STR imm12
-                if ( offsetAdjust != 0 ) {
-                    uint32_t encodedAddend = ((instruction & 0x003FFC00) >> 10);
-                    uint32_t newAddend = 0;
-                    switch ( instruction & 0xC0000000 ) {
-                        case 0x00000000:
-                            if ( (instruction & 0x04800000) == 0x04800000 ) {
-                                if ( offsetAdjust & 0xF ) {
-                                    _diagnostics.error("can't adjust off12 scale=16 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName);
-                                    return;
-                                }
-                                if ( encodedAddend*16 >= 4096 ) {
-                                    _diagnostics.error("off12 scale=16 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName);
-                                }
-                                newAddend = (encodedAddend + offsetAdjust/16) % 256;
-                            }
-                            else {
-                                // scale=1
-                                newAddend = (encodedAddend + (int32_t)offsetAdjust) % 4096;
-                            }
-                            break;
-                        case 0x40000000:
-                            if ( offsetAdjust & 1 ) {
-                                _diagnostics.error("can't adjust off12 scale=2 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName);
-                                return;
-                            }
-                            if ( encodedAddend*2 >= 4096 ) {
-                                _diagnostics.error("off12 scale=2 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName);
-                                return;
-                            }
-                            newAddend = (encodedAddend + offsetAdjust/2) % 2048;
-                            break;
-                        case 0x80000000:
-                            if ( offsetAdjust & 3 ) {
-                                _diagnostics.error("can't adjust off12 scale=4 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName);
-                                return;
-                            }
-                            if ( encodedAddend*4 >= 4096 ) {
-                                _diagnostics.error("off12 scale=4 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName);
-                                return;
-                            }
-                            newAddend = (encodedAddend + offsetAdjust/4) % 1024;
-                            break;
-                        case 0xC0000000:
-                            if ( offsetAdjust & 7 ) {
-                                _diagnostics.error("can't adjust off12 scale=8 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName);
+                uint32_t encodedAddend = ((instruction & 0x003FFC00) >> 10);
+                uint32_t newAddend = 0;
+                switch ( instruction & 0xC0000000 ) {
+                    case 0x00000000:
+                        if ( (instruction & 0x04800000) == 0x04800000 ) {
+                            if ( newPageOffset & 0xF ) {
+                                _diagnostics.error("can't adjust off12 scale=16 instruction to %d bytes at mapped address=%p in %s", newPageOffset, mappedAddr, _dylibID);
                                 return;
                             }
-                            if ( encodedAddend*8 >= 4096 ) {
-                                _diagnostics.error("off12 scale=8 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName);
-                                return;
+                            if ( encodedAddend*16 >= 4096 ) {
+                                _diagnostics.error("off12 scale=16 instruction points outside its page at mapped address=%p in %s", mappedAddr, _dylibID);
                             }
-                            newAddend = (encodedAddend + offsetAdjust/8) % 512;
-                            break;
-                    }
-                    uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10);
-                    P::E::set32(*mappedAddr32, newInstruction);
+                            newAddend = (newPageOffset/16);
+                        }
+                        else {
+                            // scale=1
+                            newAddend = newPageOffset;
+                        }
+                        break;
+                    case 0x40000000:
+                        if ( newPageOffset & 1 ) {
+                            _diagnostics.error("can't adjust off12 scale=2 instruction to %d bytes at mapped address=%p in %s", newPageOffset, mappedAddr, _dylibID);
+                            return;
+                        }
+                        if ( encodedAddend*2 >= 4096 ) {
+                            _diagnostics.error("off12 scale=2 instruction points outside its page at mapped address=%p in %s", mappedAddr, _dylibID);
+                            return;
+                        }
+                        newAddend = (newPageOffset/2);
+                        break;
+                    case 0x80000000:
+                        if ( newPageOffset & 3 ) {
+                            _diagnostics.error("can't adjust off12 scale=4 instruction to %d bytes at mapped address=%p in %s", newPageOffset, mappedAddr, _dylibID);
+                            return;
+                        }
+                        if ( encodedAddend*4 >= 4096 ) {
+                            _diagnostics.error("off12 scale=4 instruction points outside its page at mapped address=%p in %s", mappedAddr, _dylibID);
+                            return;
+                        }
+                        newAddend = (newPageOffset/4);
+                        break;
+                    case 0xC0000000:
+                        if ( newPageOffset & 7 ) {
+                            _diagnostics.error("can't adjust off12 scale=8 instruction to %d bytes at mapped address=%p in %s", newPageOffset, mappedAddr, _dylibID);
+                            return;
+                        }
+                        if ( encodedAddend*8 >= 4096 ) {
+                            _diagnostics.error("off12 scale=8 instruction points outside its page at mapped address=%p in %s", mappedAddr, _dylibID);
+                            return;
+                        }
+                        newAddend = (newPageOffset/8);
+                        break;
                 }
+                uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10);
+                P::E::set32(*mappedAddr32, newInstruction);
             }
             else if ( (instruction & 0xFFC00000) == 0x91000000 ) {
                 // ADD imm12
                 if ( instruction & 0x00C00000 ) {
-                    _diagnostics.error("ADD off12 uses shift at mapped address=%p in %s", mappedAddr, _installName);
+                    _diagnostics.error("ADD off12 uses shift at mapped address=%p in %s", mappedAddr, _dylibID);
                     return;
                 }
-                uint32_t encodedAddend = ((instruction & 0x003FFC00) >> 10);
-                uint32_t newAddend = (encodedAddend + offsetAdjust) & 0xFFF;
+                uint32_t newAddend = newPageOffset;
                 uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10);
                 P::E::set32(*mappedAddr32, newInstruction);
             }
             else if ( instruction != 0xD503201F ) {
                 // ignore imm12 instructions optimized into a NOP, but warn about others
-                _diagnostics.error("unknown off12 instruction 0x%08X at 0x%0llX in %s", instruction, fromNewAddress, _installName);
+                _diagnostics.error("unknown off12 instruction 0x%08X at 0x%0llX in %s", instruction, fromNewAddress, _dylibID);
                 return;
             }
             break;
@@ -880,7 +1081,7 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
                         instruction1 = setThumbWord(instruction1, full >> 16);
                     }
                     else {
-                        _diagnostics.error("two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but not paried in %s", _installName);
+                        _diagnostics.error("two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but not paried in %s", _dylibID);
                         return;
                     }
                     P::E::set32(*lastMappedAddr32, instruction1);
@@ -888,7 +1089,7 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
                     kind = 0;
                 }
                 else {
-                    _diagnostics.error("two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but target different addresses in %s", _installName);
+                    _diagnostics.error("two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but target different addresses in %s", _dylibID);
                     return;
                 }
             }
@@ -918,7 +1119,7 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
                         instruction1 = setArmWord(instruction1, full >> 16);
                     }
                     else {
-                        _diagnostics.error("two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but not paired in %s", _installName);
+                        _diagnostics.error("two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but not paired in %s", _dylibID);
                         return;
                     }
                     P::E::set32(*lastMappedAddr32, instruction1);
@@ -926,18 +1127,35 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
                     kind = 0;
                 }
                 else {
-                    _diagnostics.error("two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but target different addresses in %s", _installName);
+                    _diagnostics.error("two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but target different addresses in %s", _dylibID);
                     return;
                 }
             }
             break;
-        case DYLD_CACHE_ADJ_V2_ARM64_BR26:
+        case DYLD_CACHE_ADJ_V2_ARM64_BR26: {
+            if ( adjust == 0 )
+                break;
+            mappedAddr32 = (uint32_t*)mappedAddr;
+            instruction = P::E::get32(*mappedAddr32);
+
+            int64_t deltaToFinalTarget = toNewAddress - fromNewAddress;
+            // Make sure the target is in range
+            static const int64_t b128MegLimit = 0x07FFFFFF;
+            if ( (deltaToFinalTarget > -b128MegLimit) && (deltaToFinalTarget < b128MegLimit) ) {
+                instruction = (instruction & 0xFC000000) | ((deltaToFinalTarget >> 2) & 0x03FFFFFF);
+                P::E::set32(*mappedAddr32, instruction);
+                break;
+            } else {
+                _diagnostics.error("br26 instruction exceeds maximum range at mapped address=%p in %s", mappedAddr, _dylibID);
+                return;
+            }
+        }
         case DYLD_CACHE_ADJ_V2_THUMB_BR22:
         case DYLD_CACHE_ADJ_V2_ARM_BR24:
             // nothing to do with calls to stubs
             break;
         default:
-            _diagnostics.error("unknown split seg kind=%d in %s", kind, _installName);
+            _diagnostics.error("unknown split seg kind=%d in %s", kind, _dylibID);
             return;
     }
     lastKind = kind;
@@ -947,66 +1165,101 @@ void Adjustor<P>::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t f
 
 template <typename P>
 void Adjustor<P>::adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTracker,
-                                              CacheBuilder::LOH_Tracker& lohTracker,
-                                              const CacheBuilder::CacheCoalescedText& coalescedText,
+                                              CacheBuilder::LOH_Tracker* lohTracker,
+                                              const CacheBuilder::CacheCoalescedText* coalescedText,
                                               const CacheBuilder::DylibTextCoalescer& textCoalescer)
 {
-    static const bool log = false;
+    static const bool logDefault = false;
+    bool log = logDefault;
 
-    const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()];
-    const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize()];
+    const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff];
+    const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize];
     if ( *infoStart++ != DYLD_CACHE_ADJ_V2_FORMAT ) {
-        _diagnostics.error("malformed split seg info in %s", _installName);
+        _diagnostics.error("malformed split seg info in %s", _dylibID);
         return;
     }
     // build section arrays of slide and mapped address for each section
     std::vector<uint64_t> sectionSlides;
     std::vector<uint64_t> sectionNewAddress;
     std::vector<uint8_t*> sectionMappedAddress;
+
+    // Also track coalesced sections, if we have any
+    typedef CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset DylibSectionOffsetToCacheSectionOffset;
+    std::vector<uint64_t>                                       coalescedSectionOriginalVMAddrs;
+    std::vector<uint64_t>                                       coalescedSectionNewVMAddrs;
+    std::vector<uint8_t*>                                       coalescedSectionBufferAddrs;
+    std::vector<const DylibSectionOffsetToCacheSectionOffset*>  coalescedSectionOffsetMaps;
+    std::vector<uint64_t>                                       coalescedSectionObjcTags;
+
     sectionSlides.reserve(16);
     sectionNewAddress.reserve(16);
     sectionMappedAddress.reserve(16);
+    coalescedSectionOriginalVMAddrs.reserve(16);
+    coalescedSectionNewVMAddrs.reserve(16);
+    coalescedSectionBufferAddrs.reserve(16);
+    coalescedSectionOffsetMaps.reserve(16);
+    coalescedSectionObjcTags.reserve(16);
+
     // section index 0 refers to mach_header
     sectionMappedAddress.push_back((uint8_t*)_mappingInfo[0].dstSegment);
     sectionSlides.push_back(_segSlides[0]);
     sectionNewAddress.push_back(_mappingInfo[0].dstCacheUnslidAddress);
+    coalescedSectionOriginalVMAddrs.push_back(0);
+    coalescedSectionNewVMAddrs.push_back(0);
+    coalescedSectionBufferAddrs.push_back(nullptr);
+    coalescedSectionOffsetMaps.push_back(nullptr);
+    coalescedSectionObjcTags.push_back(0);
+
+    uint64_t imageStartAddress  = sectionNewAddress.front();
+    uint64_t imageEndAddress    = 0;
+
     // section 1 and later refer to real sections
     unsigned sectionIndex = 0;
     unsigned objcSelRefsSectionIndex = ~0U;
-    std::map<uint64_t, std::string_view> coalescedSectionNames;
-    std::map<uint64_t, uint64_t> coalescedSectionOriginalVMAddrs;
     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) {
-            if ( (strcmp(segCmd->segname(), "__TEXT") == 0) && textCoalescer.sectionWasCoalesced(sect->sectname())) {
+            if ( textCoalescer.sectionWasCoalesced(sect->segname(), sect->sectname())) {
                 // If we coalesced the segment then the sections aren't really there to be fixed up
+                const DylibSectionOffsetToCacheSectionOffset& offsetMap = textCoalescer.getSectionCoalescer(sect->segname(),
+                                                                                                            sect->sectname());
+                uint64_t coalescedSectionNewVMAddr = coalescedText->getSectionVMAddr(sect->segname(), sect->sectname());
+                uint8_t* coalescedSectionNewBufferAddr = coalescedText->getSectionBufferAddr(sect->segname(), sect->sectname());
+                uint64_t coalescedSectionObjcTag = coalescedText->getSectionObjcTag(sect->segname(), sect->sectname());
                 sectionMappedAddress.push_back(nullptr);
                 sectionSlides.push_back(0);
                 sectionNewAddress.push_back(0);
+                coalescedSectionOriginalVMAddrs.push_back(sect->addr());
+                coalescedSectionNewVMAddrs.push_back(coalescedSectionNewVMAddr);
+                coalescedSectionBufferAddrs.push_back(coalescedSectionNewBufferAddr);
+                coalescedSectionOffsetMaps.push_back(&offsetMap);
+                coalescedSectionObjcTags.push_back(coalescedSectionObjcTag);
+                ++sectionIndex;
                 if (log) {
                     fprintf(stderr, " %s/%s, sectIndex=%d, mapped at=%p\n",
                             sect->segname(), sect->sectname(), sectionIndex, sectionMappedAddress.back());
                 }
-                ++sectionIndex;
-                std::string_view sectionName = sect->sectname();
-                if (sectionName.size() > 16)
-                    sectionName = sectionName.substr(0, 16);
-                coalescedSectionNames[sectionIndex] = sectionName;
-                coalescedSectionOriginalVMAddrs[sectionIndex] = sect->addr();
             } else {
                 sectionMappedAddress.push_back((uint8_t*)_mappingInfo[segmentIndex].dstSegment + sect->addr() - segCmd->vmaddr());
                 sectionSlides.push_back(_segSlides[segmentIndex]);
                 sectionNewAddress.push_back(_mappingInfo[segmentIndex].dstCacheUnslidAddress + sect->addr() - segCmd->vmaddr());
+                coalescedSectionOriginalVMAddrs.push_back(0);
+                coalescedSectionNewVMAddrs.push_back(0);
+                coalescedSectionBufferAddrs.push_back(nullptr);
+                coalescedSectionOffsetMaps.push_back(nullptr);
+                coalescedSectionObjcTags.push_back(0);
+                ++sectionIndex;
                 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;
+
+                imageEndAddress = sectionNewAddress.back();
             }
         }
     }
@@ -1029,15 +1282,10 @@ void Adjustor<P>::adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTr
         uint8_t* fromSectionMappedAddress = sectionMappedAddress[fromSectionIndex];
         uint64_t toSectionSlide = sectionSlides[toSectionIndex];
         uint64_t toSectionNewAddress = sectionNewAddress[toSectionIndex];
-        CacheBuilder::LOH_Tracker* lohTrackerPtr = (toSectionIndex == objcSelRefsSectionIndex) ? &lohTracker : nullptr;
+        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;
 
-        // We don't support updating split seg from a coalesced segment
-        if (coalescedSectionNames.find(fromSectionIndex) != coalescedSectionNames.end()) {
-            _diagnostics.error("split seg from coalesced segment in %s", _installName);
-            return;
-        }
         for (uint64_t j=0; j < toOffsetCount; ++j) {
             uint64_t toSectionDelta = read_uleb128(p, infoEnd);
             uint64_t fromOffsetCount = read_uleb128(p, infoEnd);
@@ -1045,7 +1293,7 @@ void Adjustor<P>::adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTr
             for (uint64_t k=0; k < fromOffsetCount; ++k) {
                 uint64_t kind = read_uleb128(p, infoEnd);
                 if ( kind > 13 ) {
-                    _diagnostics.error("unknown split seg info v2 kind value (%llu) in %s", kind, _installName);
+                    _diagnostics.error("unknown split seg info v2 kind value (%llu) in %s", kind, _dylibID);
                     return;
                 }
                 uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd);
@@ -1053,37 +1301,84 @@ void Adjustor<P>::adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTr
                 for (uint64_t l=0; l < fromSectDeltaCount; ++l) {
                     uint64_t delta = read_uleb128(p, infoEnd);
                     fromSectionOffset += delta;
-                    //if (log) printf("   kind=%lld, from offset=0x%0llX, to offset=0x%0llX, adjust=0x%llX, targetSlide=0x%llX\n", kind, fromSectionOffset, toSectionOffset, delta, toSectionSlide);
-
-                    uint8_t*  fromMappedAddr = fromSectionMappedAddress + fromSectionOffset;
-                    uint64_t toNewAddress = toSectionNewAddress + toSectionOffset;
-                    uint64_t fromNewAddress = fromSectionNewAddress + fromSectionOffset;
-                    uint64_t imageStartAddress = sectionNewAddress.front();
-                    uint64_t imageEndAddress = sectionNewAddress.back();
-                    if ( toSectionIndex != 255 ) {
-                        auto textCoalIt = coalescedSectionNames.find(toSectionIndex);
-                        if (textCoalIt != coalescedSectionNames.end() ) {
-                            //printf("Section name: %s\n", textCoalIt->second.data());
-                            const CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& offsetMap = textCoalescer.getSectionCoalescer(textCoalIt->second);
-                            auto offsetIt = offsetMap.find((uint32_t)toSectionOffset);
-                            assert(offsetIt != offsetMap.end());
-                            uint64_t baseVMAddr = coalescedText.getSectionData(textCoalIt->second).bufferVMAddr;
-                            toNewAddress = baseVMAddr + offsetIt->second;
-
-                            // The 'to' section is gone, but we still need the 'to' slide.  Instead of a section slide, compute the slide
-                            // for this individual atom
-                            uint64_t toAtomOriginalVMAddr = coalescedSectionOriginalVMAddrs[toSectionIndex] + toSectionOffset;
-                            uint64_t toAtomSlide = toNewAddress - toAtomOriginalVMAddr;
-                            int64_t deltaAdjust = toAtomSlide - fromSectionSlide;
-                            adjustReference((uint32_t)kind, fromMappedAddr, fromNewAddress, toNewAddress, deltaAdjust, toAtomSlide,
-                                            imageStartAddress, imageEndAddress, aslrTracker, lohTrackerPtr, lastMappedAddr32, lastKind, lastToNewAddress);
-
-                        } else {
-                            int64_t deltaAdjust = toSectionSlide - fromSectionSlide;
-                            adjustReference((uint32_t)kind, fromMappedAddr, fromNewAddress, toNewAddress, deltaAdjust, toSectionSlide,
-                                            imageStartAddress, imageEndAddress, aslrTracker, lohTrackerPtr, lastMappedAddr32, lastKind, lastToNewAddress);
+                    if (log) printf("   kind=%lld, from offset=0x%0llX, to offset=0x%0llX, adjust=0x%llX, targetSlide=0x%llX\n", kind, fromSectionOffset, toSectionOffset, delta, toSectionSlide);
+
+                    // It's possible for all either of from/to sectiobs to be coalesced/optimized.
+                    // Handle each of those combinations.
+                    uint8_t* fromMappedAddr = nullptr;
+                    uint64_t fromNewAddress = 0;
+                    uint64_t fromAtomSlide  = 0;
+                    bool convertRebaseChains = false;
+                    if ( coalescedSectionOffsetMaps[fromSectionIndex] != nullptr ) {
+                        // From was optimized/coalesced
+                        // This is only supported on pointer kind fixups, ie, pointers in RW segments
+                        assert( (kind == DYLD_CACHE_ADJ_V2_POINTER_64) || (kind == DYLD_CACHE_ADJ_V2_THREADED_POINTER_64) );
+                        // Find where the atom moved to with its new section
+                        // CFString's and similar may have fixups in the middle of the atom, but the map only
+                        // tracks the start offset for the atom.  We use lower_bound to find the atom containing
+                        // the offset we are looking for
+                        const DylibSectionOffsetToCacheSectionOffset* offsetMap = coalescedSectionOffsetMaps[fromSectionIndex];
+                        auto offsetIt = offsetMap->lower_bound((uint32_t)fromSectionOffset);
+                        if ( offsetIt->first != fromSectionOffset ) {
+                            // This points to the middle of the atom, so check the previous atom
+                            assert(offsetIt != offsetMap->begin());
+                            --offsetIt;
+                            assert(offsetIt->first <= fromSectionOffset);
                         }
+                        assert(offsetIt != offsetMap->end());
+                        // FIXME: Other CF type's have different atom sizes
+                        uint64_t offsetInAtom = fromSectionOffset - offsetIt->first;
+                        assert(offsetInAtom < (uint64_t)DyldSharedCache::ConstantClasses::cfStringAtomSize);
+
+                        uint8_t* baseMappedAddr = coalescedSectionBufferAddrs[fromSectionIndex];
+                        fromMappedAddr = baseMappedAddr + offsetIt->second + offsetInAtom;
+                        uint64_t baseVMAddr = coalescedSectionNewVMAddrs[fromSectionIndex];
+                        fromNewAddress = baseVMAddr + offsetIt->second + offsetInAtom;
+
+                        // The 'from' section is gone, but we still need the 'from' slide.  Instead of a section slide,
+                        // compute the slide for this individual atom
+                        uint64_t fromAtomOriginalVMAddr = coalescedSectionOriginalVMAddrs[fromSectionIndex] + fromSectionOffset;
+                        fromAtomSlide = fromNewAddress - fromAtomOriginalVMAddr;
+                        convertRebaseChains = true;
+                    } else {
+                        // From was not optimized/coalesced
+                        fromMappedAddr = fromSectionMappedAddress + fromSectionOffset;
+                        fromNewAddress = fromSectionNewAddress + fromSectionOffset;
+                        fromAtomSlide = fromSectionSlide;
+                    }
+
+                    uint64_t toNewAddress   = 0;
+                    uint64_t toAtomSlide    = 0;
+                    if ( coalescedSectionOffsetMaps[toSectionIndex] != nullptr ) {
+                        // To was optimized/coalesced
+                        const DylibSectionOffsetToCacheSectionOffset* offsetMap = coalescedSectionOffsetMaps[toSectionIndex];
+                        auto offsetIt = offsetMap->find((uint32_t)toSectionOffset);
+                        assert(offsetIt != offsetMap->end());
+                        uint64_t baseVMAddr = coalescedSectionNewVMAddrs[toSectionIndex];
+                        toNewAddress = baseVMAddr + offsetIt->second;
+
+                        // Add in the high bits which are the tagged pointer TBI bits
+                        toNewAddress |= coalescedSectionObjcTags[toSectionIndex];
+
+                        // The 'to' section is gone, but we still need the 'to' slide.  Instead of a section slide,
+                        // compute the slide for this individual atom
+                        uint64_t toAtomOriginalVMAddr = coalescedSectionOriginalVMAddrs[toSectionIndex] + toSectionOffset;
+                        toAtomSlide = toNewAddress - toAtomOriginalVMAddr;
+                    } else {
+                        // To was not optimized/coalesced
+                        toNewAddress = toSectionNewAddress + toSectionOffset;
+                        toAtomSlide = toSectionSlide;
                     }
+
+                    int64_t  deltaAdjust        = toAtomSlide - fromAtomSlide;
+                    if (log) {
+                        printf("   kind=%lld, from offset=0x%0llX, to offset=0x%0llX, adjust=0x%llX, targetSlide=0x%llX\n",
+                               kind, fromSectionOffset, toSectionOffset, deltaAdjust, toSectionSlide);
+                    }
+                    adjustReference((uint32_t)kind, fromMappedAddr, fromNewAddress, toNewAddress, deltaAdjust,
+                                    toAtomSlide, imageStartAddress, imageEndAddress, convertRebaseChains,
+                                    aslrTracker, lohTrackerPtr,
+                                    lastMappedAddr32, lastKind, lastToNewAddress);
                     if ( _diagnostics.hasError() )
                         return;
                 }
@@ -1093,14 +1388,12 @@ void Adjustor<P>::adjustReferencesUsingInfoV2(CacheBuilder::ASLR_Tracker& aslrTr
 
 }
 
-
 template <typename P>
 void Adjustor<P>::adjustRebaseChains(CacheBuilder::ASLR_Tracker& aslrTracker)
 {
-    const dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)_mh;
-    const dyld_chained_fixups_header* chainHeader = (dyld_chained_fixups_header*)(&_linkeditBias[_chainedFixupsCmd->dataoff()]);
+    const dyld_chained_fixups_header* chainHeader = (dyld_chained_fixups_header*)(&_linkeditBias[_chainedFixupsCmd->dataoff]);
     const dyld_chained_starts_in_image* startsInfo = (dyld_chained_starts_in_image*)((uint8_t*)chainHeader + chainHeader->starts_offset);
-    ma->forEachFixupInAllChains(_diagnostics, startsInfo, false,
+    _mh->forEachFixupInAllChains(_diagnostics, startsInfo, false,
         ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
             switch ( segInfo->pointer_format ) {
                 case DYLD_CHAINED_PTR_64:
@@ -1113,7 +1406,7 @@ void Adjustor<P>::adjustRebaseChains(CacheBuilder::ASLR_Tracker& aslrTracker)
                     }
                     break;
                 case DYLD_CHAINED_PTR_64_OFFSET:
-                    _diagnostics.error("unhandled 64-bit chained fixup format %d in %s", _chainedFixupsFormat, _installName);
+                    _diagnostics.error("unhandled 64-bit chained fixup format %d in %s", _chainedFixupsFormat, _dylibID);
                     break;
                 default:
                     _diagnostics.error("unsupported chained fixup format %d", segInfo->pointer_format);
@@ -1122,12 +1415,99 @@ void Adjustor<P>::adjustRebaseChains(CacheBuilder::ASLR_Tracker& aslrTracker)
     });
 }
 
+static int uint32Sorter(const void* l, const void* r) {
+    if ( *((uint32_t*)l) < *((uint32_t*)r) )
+        return -1;
+    else
+        return 1;
+}
+
+template <typename P>
+static uint64_t localRelocBaseAddress(const dyld3::MachOAnalyzer* ma,
+                                      std::vector<macho_segment_command<P>*> segCmds,
+                                      std::vector<uint64_t> segOrigStartAddresses)
+{
+    if ( ma->isArch("x86_64") || ma->isArch("x86_64h") ) {
+#if BUILDING_APP_CACHE_UTIL
+        if ( ma->isKextBundle() ) {
+            // for kext bundles the reloc base address starts at __TEXT segment
+            return segOrigStartAddresses[0];
+        }
+#endif
+        // for all other kinds, the x86_64 reloc base address starts at first writable segment (usually __DATA)
+        for (uint32_t i=0; i < segCmds.size(); ++i) {
+            if ( segCmds[i]->initprot() & VM_PROT_WRITE )
+                return segOrigStartAddresses[i];
+        }
+    }
+    return segOrigStartAddresses[0];
+}
+
+static bool segIndexAndOffsetForAddress(uint64_t addr, const std::vector<uint64_t>& segOrigStartAddresses,
+                                        std::vector<uint64_t> segSizes, uint32_t& segIndex, uint64_t& segOffset)
+{
+    for (uint32_t i=0; i < segOrigStartAddresses.size(); ++i) {
+        if ( (segOrigStartAddresses[i] <= addr) && (addr < (segOrigStartAddresses[i] + segSizes[i])) ) {
+            segIndex  = i;
+            segOffset = addr - segOrigStartAddresses[i];
+            return true;
+        }
+    }
+    return false;
+}
 
 template <typename P>
 void Adjustor<P>::adjustDataPointers(CacheBuilder::ASLR_Tracker& aslrTracker)
 {
-    const uint8_t* p = &_linkeditBias[_dyldInfo->rebase_off()];
-    const uint8_t* end = &p[_dyldInfo->rebase_size()];
+    if ( (_dynSymTabCmd != nullptr) && (_dynSymTabCmd->locreloff != 0) ) {
+        // kexts may have old style relocations instead of dyldinfo rebases
+        assert(_dyldInfo == nullptr);
+
+        // old binary, walk relocations
+        const uint64_t                  relocsStartAddress = localRelocBaseAddress(_mh, _segCmds, _segOrigStartAddresses);
+        const relocation_info* const    relocsStart = (const relocation_info* const)&_linkeditBias[_dynSymTabCmd->locreloff];
+        const relocation_info* const    relocsEnd   = &relocsStart[_dynSymTabCmd->nlocrel];
+        bool                            stop = false;
+        const uint8_t                   relocSize = (_mh->is64() ? 3 : 2);
+        STACK_ALLOC_OVERFLOW_SAFE_ARRAY(uint32_t, relocAddrs, 2048);
+        for (const relocation_info* reloc=relocsStart; (reloc < relocsEnd) && !stop; ++reloc) {
+            if ( reloc->r_length != relocSize ) {
+                _diagnostics.error("local relocation has wrong r_length");
+                break;
+            }
+            if ( reloc->r_type != 0 ) { // 0 == X86_64_RELOC_UNSIGNED == GENERIC_RELOC_VANILLA ==  ARM64_RELOC_UNSIGNED
+                _diagnostics.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, _segOrigStartAddresses, _segSizes, segIndex, segOffset) ) {
+                    uint8_t type = REBASE_TYPE_POINTER;
+                    assert(_mh->cputype != CPU_TYPE_I386);
+                    slidePointer(segIndex, segOffset, type, aslrTracker);
+                }
+                else {
+                    _diagnostics.error("local relocation has out of range r_address");
+                    break;
+                }
+            }
+        }
+        // then process indirect symbols
+        // FIXME: Do we need indirect symbols?  Aren't those handled as binds?
+
+        return;
+    }
+
+    if ( _dyldInfo == NULL )
+        return;
+
+    const uint8_t* p = &_linkeditBias[_dyldInfo->rebase_off];
+    const uint8_t* end = &p[_dyldInfo->rebase_size];
     
     uint8_t type = 0;
     int segIndex = 0;
@@ -1182,7 +1562,7 @@ void Adjustor<P>::adjustDataPointers(CacheBuilder::ASLR_Tracker& aslrTracker)
                 }
                 break;
             default:
-                _diagnostics.error("unknown rebase opcode 0x%02X in %s", opcode, _installName);
+                _diagnostics.error("unknown rebase opcode 0x%02X in %s", opcode, _dylibID);
                 done = true;
                 break;
         }
@@ -1318,8 +1698,11 @@ template <typename P>
 void Adjustor<P>::adjustCode()
 {
     // find compressed info on how code needs to be updated
-    const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()];
-    const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize()];;
+    if ( _splitSegInfoCmd == nullptr )
+        return;
+
+    const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff];
+    const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize];;
 
     // This encoding only works if all data segments slide by the same amount
     uint64_t codeToDataDelta = _segSlides[1] - _segSlides[0];
@@ -1343,11 +1726,11 @@ void Adjustor<P>::adjustExportsTrie(std::vector<uint8_t>& newTrieBytes)
     uint32_t exportOffset   = 0;
     uint32_t exportSize     = 0;
     if ( _dyldInfo != nullptr ) {
-        exportOffset = _dyldInfo->export_off();
-        exportSize = _dyldInfo->export_size();
-    } else {
-        exportOffset = _exportTrieCmd->dataoff();
-        exportSize = _exportTrieCmd->datasize();
+        exportOffset = _dyldInfo->export_off;
+        exportSize = _dyldInfo->export_size;
+    } else if (_exportTrieCmd != nullptr) {
+        exportOffset = _exportTrieCmd->dataoff;
+        exportSize = _exportTrieCmd->datasize;
     }
 
     if ( exportSize == 0 )
@@ -1359,7 +1742,7 @@ void Adjustor<P>::adjustExportsTrie(std::vector<uint8_t>& newTrieBytes)
     const uint8_t* end = &start[exportSize];
     std::vector<ExportInfoTrie::Entry> originalExports;
     if ( !ExportInfoTrie::parseTrie(start, end, originalExports) ) {
-        _diagnostics.error("malformed exports trie in %s", _installName);
+        _diagnostics.error("malformed exports trie in %s", _dylibID);
         return;
     }
 
@@ -1392,16 +1775,27 @@ void Adjustor<P>::adjustExportsTrie(std::vector<uint8_t>& newTrieBytes)
 
 } // anonymous namespace
 
-void CacheBuilder::adjustDylibSegments(const DylibInfo& dylib, Diagnostics& diag) const
+void CacheBuilder::adjustDylibSegments(const DylibInfo& dylib, Diagnostics& diag,
+                                       uint64_t cacheBaseAddress,
+                                       CacheBuilder::ASLR_Tracker& aslrTracker,
+                                       CacheBuilder::LOH_Tracker* lohTracker,
+                                       const CacheBuilder::CacheCoalescedText* coalescedText) const
 {
-    DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    
+    dyld3::MachOAnalyzer* mh = (dyld3::MachOAnalyzer*)dylib.cacheLocation[0].dstSegment;
     if ( _is64 ) {
-        Adjustor<Pointer64<LittleEndian>> adjustor64(cache, (macho_header<Pointer64<LittleEndian>>*)dylib.cacheLocation[0].dstSegment, dylib.cacheLocation, diag);
-        adjustor64.adjustImageForNewSegmentLocations(_aslrTracker, _lohTracker, _coalescedText, dylib.textCoalescer);
+        Adjustor<Pointer64<LittleEndian>> adjustor64(cacheBaseAddress,
+                                                     mh,
+                                                     dylib.dylibID.c_str(),
+                                                     dylib.cacheLocation, diag);
+        adjustor64.adjustImageForNewSegmentLocations(aslrTracker, lohTracker, coalescedText, dylib.textCoalescer);
     }
     else {
-        Adjustor<Pointer32<LittleEndian>> adjustor32(cache, (macho_header<Pointer32<LittleEndian>>*)dylib.cacheLocation[0].dstSegment, dylib.cacheLocation, diag);
-        adjustor32.adjustImageForNewSegmentLocations(_aslrTracker, _lohTracker, _coalescedText, dylib.textCoalescer);
+        Adjustor<Pointer32<LittleEndian>> adjustor32(cacheBaseAddress,
+                                                     mh,
+                                                     dylib.dylibID.c_str(),
+                                                     dylib.cacheLocation, diag);
+        adjustor32.adjustImageForNewSegmentLocations(aslrTracker, lohTracker, coalescedText, dylib.textCoalescer);
     }
 }
 
diff --git a/dyld3/shared-cache/AppCacheBuilder.cpp b/dyld3/shared-cache/AppCacheBuilder.cpp
new file mode 100644 (file)
index 0000000..c26de4d
--- /dev/null
@@ -0,0 +1,5423 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
+*
+* Copyright (c) 2014 Apple Inc. All rights reserved.
+*
+* @APPLE_LICENSE_HEADER_START@
+*
+* This file contains Original Code and/or Modifications of Original Code
+* as defined in and that are subject to the Apple Public Source License
+* Version 2.0 (the 'License'). You may not use this file except in
+* compliance with the License. Please obtain a copy of the License at
+* http://www.opensource.apple.com/apsl/ and read it before using this
+* file.
+*
+* The Original Code and all software distributed under the License are
+* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+* Please see the License for the specific language governing rights and
+* limitations under the License.
+*
+* @APPLE_LICENSE_HEADER_END@
+*/
+
+#include "AppCacheBuilder.h"
+
+#include <mach/mach_time.h>
+#include <sys/stat.h>
+#include <CoreFoundation/CFArray.h>
+#include <CoreFoundation/CFError.h>
+#include <CoreFoundation/CFNumber.h>
+#include <CoreFoundation/CFPropertyList.h>
+#include <CoreFoundation/CFString.h>
+
+#include <CommonCrypto/CommonHMAC.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <CommonCrypto/CommonDigestSPI.h>
+
+AppCacheBuilder::AppCacheBuilder(const DyldSharedCache::CreateOptions& options,
+                                 const Options& appCacheOptions,
+                                 const dyld3::closure::FileSystem& fileSystem)
+    : CacheBuilder(options, fileSystem), appCacheOptions(appCacheOptions)
+{
+    // FIXME: 32-bit support
+    _is64 = true;
+}
+
+AppCacheBuilder::~AppCacheBuilder() {
+    if (prelinkInfoDict) {
+        CFRelease(prelinkInfoDict);
+    }
+    if (_fullAllocatedBuffer) {
+        vm_deallocate(mach_task_self(), _fullAllocatedBuffer, _allocatedBufferSize);
+    }
+}
+
+
+void AppCacheBuilder::makeSortedDylibs(const std::vector<InputDylib>& dylibs)
+{
+    for (const InputDylib& file : dylibs) {
+        if ( file.dylib.loadedFileInfo.fileContent == nullptr ) {
+            codelessKexts.push_back(file);
+        } else {
+            AppCacheDylibInfo& dylibInfo = sortedDylibs.emplace_back();
+            dylibInfo.input         = &file.dylib;
+            dylibInfo.dylibID       = file.dylibID;
+            dylibInfo.dependencies  = file.dylibDeps;
+            dylibInfo.infoPlist     = file.infoPlist;
+            dylibInfo.errors        = file.errors;
+            dylibInfo.bundlePath    = file.bundlePath;
+            dylibInfo.stripMode     = file.stripMode;
+        }
+    }
+
+    std::sort(sortedDylibs.begin(), sortedDylibs.end(), [&](const DylibInfo& a, const DylibInfo& b) {
+        // Sort the kernel first, then kext's
+        bool isStaticExecutableA = a.input->mappedFile.mh->isStaticExecutable();
+        bool isStaticExecutableB = b.input->mappedFile.mh->isStaticExecutable();
+        if (isStaticExecutableA != isStaticExecutableB)
+            return isStaticExecutableA;
+
+        // Sort split seg next
+        bool splitSegA = a.input->mappedFile.mh->hasSplitSeg();
+        bool splitSegB = b.input->mappedFile.mh->hasSplitSeg();
+        if (splitSegA != splitSegB)
+            return splitSegA;
+
+        // Finally sort by path
+        return a.input->mappedFile.runtimePath < b.input->mappedFile.runtimePath;
+    });
+
+    // Sort codeless kext's by ID
+    std::sort(codelessKexts.begin(), codelessKexts.end(), [&](const InputDylib& a, const InputDylib& b) {
+        return a.dylibID < b.dylibID;
+    });
+}
+
+
+void AppCacheBuilder::forEachCacheDylib(void (^callback)(const dyld3::MachOAnalyzer* ma,
+                                                         const std::string& dylibID,
+                                                         DylibStripMode stripMode,
+                                                         const std::vector<std::string>& dependencies,
+                                                         Diagnostics& dylibDiag,
+                                                         bool& stop)) const {
+    bool stop = false;
+    for (const AppCacheDylibInfo& dylib : sortedDylibs) {
+        for (const SegmentMappingInfo& loc : dylib.cacheLocation) {
+            if (!strcmp(loc.segName, "__TEXT")) {
+                // Assume __TEXT contains the mach header
+                callback((const dyld3::MachOAnalyzer*)loc.dstSegment, dylib.dylibID, dylib.stripMode,
+                         dylib.dependencies, *dylib.errors, stop);
+                break;
+            }
+        }
+        if (stop)
+            break;
+    }
+}
+
+void AppCacheBuilder::forEachDylibInfo(void (^callback)(const DylibInfo& dylib, Diagnostics& dylibDiag)) {
+    for (const AppCacheDylibInfo& dylibInfo : sortedDylibs)
+        callback(dylibInfo, *dylibInfo.errors);
+}
+
+const CacheBuilder::DylibInfo* AppCacheBuilder::getKernelStaticExecutableInputFile() const {
+    for (const auto& dylib : sortedDylibs) {
+        const dyld3::MachOAnalyzer* ma = dylib.input->mappedFile.mh;
+        if ( ma->isStaticExecutable() )
+            return &dylib;
+    }
+    return nullptr;
+}
+
+const dyld3::MachOAnalyzer* AppCacheBuilder::getKernelStaticExecutableFromCache() const {
+    // FIXME: Support reading this from a prebuilt KC
+    assert(appCacheOptions.cacheKind == Options::AppCacheKind::kernel);
+
+    __block const dyld3::MachOAnalyzer* kernelMA = nullptr;
+    forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID,
+                        DylibStripMode stripMode, const std::vector<std::string>& dependencies,
+                        Diagnostics& dylibDiag,
+                        bool& stop) {
+        if ( ma->isStaticExecutable() ) {
+            kernelMA = ma;
+            stop = true;
+        }
+    });
+
+    assert(kernelMA != nullptr);
+    return kernelMA;
+}
+
+void AppCacheBuilder::forEachRegion(void (^callback)(const Region& region)) const {
+    // cacheHeaderRegion
+    callback(cacheHeaderRegion);
+
+    // readOnlyTextRegion
+    callback(readOnlyTextRegion);
+
+    // readExecuteRegion
+    if ( readExecuteRegion.sizeInUse != 0 )
+        callback(readExecuteRegion);
+
+    // branchStubsRegion
+    if ( branchStubsRegion.bufferSize != 0 )
+        callback(branchStubsRegion);
+
+    // dataConstRegion
+    if ( dataConstRegion.sizeInUse != 0 )
+        callback(dataConstRegion);
+
+    // branchGOTsRegion
+    if ( branchGOTsRegion.bufferSize != 0 )
+        callback(branchGOTsRegion);
+
+    // readWriteRegion
+    if ( readWriteRegion.sizeInUse != 0 )
+        callback(readWriteRegion);
+
+    // hibernateRegion
+    if ( hibernateRegion.sizeInUse != 0 )
+        callback(hibernateRegion);
+
+    // -sectcreate
+    for (const Region& region : customDataRegions)
+        callback(region);
+
+    // prelinkInfoRegion
+    if ( prelinkInfoDict != nullptr )
+        callback(prelinkInfoRegion);
+
+    // nonSplitSegRegions
+    for (const Region& region : nonSplitSegRegions)
+        callback(region);
+
+    // _readOnlyRegion
+    callback(_readOnlyRegion);
+
+    // fixupsRegion
+    // We don't count this as its not a real region
+}
+
+uint64_t AppCacheBuilder::numRegions() const {
+    __block uint64_t count = 0;
+
+    forEachRegion(^(const Region &region) {
+        ++count;
+    });
+
+    return count;
+}
+
+uint64_t AppCacheBuilder::fixupsPageSize() const {
+    bool use4K = false;
+    use4K |= (_options.archs == &dyld3::GradedArchs::x86_64);
+    use4K |= (_options.archs == &dyld3::GradedArchs::x86_64h);
+    return use4K ? 4096 : 16384;
+}
+
+uint64_t AppCacheBuilder::numWritablePagesToFixup(uint64_t numBytesToFixup) const {
+    uint64_t pageSize = fixupsPageSize();
+    assert((numBytesToFixup % pageSize) == 0);
+    uint64_t numPagesToFixup = numBytesToFixup / pageSize;
+    return numPagesToFixup;
+}
+
+// Returns true if each kext inside the KC needs to be reloadable, ie, have its
+// pages reset and its start method rerun.  This means we can't pack pages and need
+// fixups on each kext individually
+bool AppCacheBuilder::fixupsArePerKext() const {
+    if ( appCacheOptions.cacheKind == Options::AppCacheKind::pageableKC )
+        return true;
+    bool isX86 = (_options.archs == &dyld3::GradedArchs::x86_64) || (_options.archs == &dyld3::GradedArchs::x86_64h);
+    return isX86 && (appCacheOptions.cacheKind == Options::AppCacheKind::auxKC);
+}
+
+// x86_64 kext's don't contain stubs for branches so we need to generate some
+// if branches cross from one KC to another, eg, from the auxKC to the base KC
+uint64_t AppCacheBuilder::numBranchRelocationTargets() {
+    bool mayHaveBranchRelocations = false;
+    mayHaveBranchRelocations |= (_options.archs == &dyld3::GradedArchs::x86_64);
+    mayHaveBranchRelocations |= (_options.archs == &dyld3::GradedArchs::x86_64h);
+    if ( !mayHaveBranchRelocations )
+        return 0;
+
+    switch (appCacheOptions.cacheKind) {
+        case Options::AppCacheKind::none:
+        case Options::AppCacheKind::kernel:
+            // Nothing to do here as we can't bind from a lower level up to a higher one
+            return 0;
+        case Options::AppCacheKind::pageableKC:
+        case Options::AppCacheKind::kernelCollectionLevel2:
+        case Options::AppCacheKind::auxKC:
+            // Any calls in these might be to a lower level so add space for each call target
+            break;
+    }
+
+    uint64_t totalTargets = 0;
+    for (const DylibInfo& dylib : sortedDylibs) {
+        // We need the symbol name and libOrdinal just in case we bind to the same symbol name in 2 different KCs
+        typedef std::pair<std::string_view, int> Symbol;
+        struct SymbolHash
+        {
+            size_t operator() (const Symbol& symbol) const
+            {
+                return std::hash<std::string_view>{}(symbol.first) ^ std::hash<int>{}(symbol.second);
+            }
+        };
+        __block std::unordered_set<Symbol, SymbolHash> seenSymbols;
+        dylib.input->mappedFile.mh->forEachBind(_diagnostics,
+                                                ^(uint64_t runtimeOffset, int libOrdinal, uint8_t type,
+                                                  const char *symbolName, bool weakImport,
+                                                  bool lazyBind, uint64_t addend, bool &stop) {
+            if ( type != BIND_TYPE_TEXT_PCREL32 )
+                return;
+            seenSymbols.insert({ symbolName, libOrdinal });
+        }, ^(const char *symbolName) {
+        });
+        totalTargets += seenSymbols.size();
+    }
+    return totalTargets;
+}
+
+void AppCacheBuilder::assignSegmentRegionsAndOffsets()
+{
+    // Segments can be re-ordered in memory relative to the order of the LC_SEGMENT load comamnds
+    // so first make space for all the cache location objects so that we get the order the same
+    // as the LC_SEGMENTs
+    for (DylibInfo& dylib : sortedDylibs) {
+        dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+            dylib.cacheLocation.push_back({});
+        });
+    }
+
+    // If we are building the kernel collection, then inherit the base address of the statically linked kernel
+    const dyld3::MachOAnalyzer* kernelMA = nullptr;
+    if ( appCacheOptions.cacheKind == Options::AppCacheKind::kernel ) {
+        for (DylibInfo& dylib : sortedDylibs) {
+            if ( dylib.input->mappedFile.mh->isStaticExecutable() ) {
+                kernelMA = dylib.input->mappedFile.mh;
+                break;
+            }
+        }
+        if ( kernelMA == nullptr ) {
+            _diagnostics.error("Could not find kernel image");
+            return;
+        }
+        cacheBaseAddress = kernelMA->preferredLoadAddress();
+    }
+
+    // x86_64 doesn't have stubs for kext branches.  So work out how many potential targets
+    // we need to emit stubs for.
+    uint64_t branchTargetsFromKexts = numBranchRelocationTargets();
+
+    uint32_t minimumSegmentAlignmentP2 = 14;
+    if ( (_options.archs == &dyld3::GradedArchs::x86_64) || (_options.archs == &dyld3::GradedArchs::x86_64h) ) {
+        minimumSegmentAlignmentP2 = 12;
+    }
+
+    auto getMinAlignment = ^(const dyld3::MachOAnalyzer* ma) {
+        // The kernel wants to be able to unmap its own segments so always align it.
+        // And align the pageable KC as each kext can be mapped individually
+        if ( ma == kernelMA )
+            return minimumSegmentAlignmentP2;
+        if ( fixupsArePerKext() )
+            return minimumSegmentAlignmentP2;
+        if ( _options.archs == &dyld3::GradedArchs::arm64e )
+            return minimumSegmentAlignmentP2;
+        return 0U;
+    };
+
+    {
+        // __TEXT segments with r/o permissions
+        __block uint64_t offsetInRegion = 0;
+        for (DylibInfo& dylib : sortedDylibs) {
+            bool canBePacked = dylib.input->mappedFile.mh->hasSplitSeg();
+            if (!canBePacked)
+                continue;
+
+            __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(segInfo.segName, "__DATA_CONST") == 0)
+                    || (strcmp(segInfo.segName, "__PPLDATA_CONST") == 0)
+                    || (strcmp(segInfo.segName, "__LASTDATA_CONST") == 0) )
+                    return;
+                if ( strcmp(segInfo.segName, "__LINKEDIT") == 0 )
+                    return;
+                if ( strcmp(segInfo.segName, "__LINKINFO") == 0 )
+                    return;
+
+                uint32_t minAlignmentP2 = getMinAlignment(dylib.input->mappedFile.mh);
+                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+                uint64_t dstCacheSegmentSize = align(segInfo.sizeOfSections, minAlignmentP2);
+
+                // __CTF is not mapped in to the kernel, so remove it from the final binary.
+                if ( strcmp(segInfo.segName, "__CTF") == 0 ) {
+                    copySize = 0;
+                    dstCacheSegmentSize = 0;
+                }
+
+                // kxld packs __TEXT so we will do
+                // Note we align to at least 16-bytes as LDR's can scale up to 16 from their address
+                // and aligning them less than 16 would break that
+                offsetInRegion = align(offsetInRegion, std::max(segInfo.p2align, 4U));
+                offsetInRegion = align(offsetInRegion, minAlignmentP2);
+                SegmentMappingInfo loc;
+                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+                loc.segName                = segInfo.segName;
+                loc.dstSegment             = nullptr;
+                loc.dstCacheUnslidAddress  = offsetInRegion; // This will be updated later once we've assigned addresses
+                loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
+                loc.dstCacheSegmentSize    = (uint32_t)dstCacheSegmentSize;
+                loc.dstCacheFileSize       = (uint32_t)copySize;
+                loc.copySegmentSize        = (uint32_t)copySize;
+                loc.srcSegmentIndex        = segInfo.segIndex;
+                loc.parentRegion           = &readOnlyTextRegion;
+                dylib.cacheLocation[segInfo.segIndex] = loc;
+                offsetInRegion += dstCacheSegmentSize;
+            });
+        }
+
+        // kclist needs this segment, even if its empty, so leave it in there
+        readOnlyTextRegion.bufferSize   = align(offsetInRegion, 14);
+        readOnlyTextRegion.sizeInUse    = readOnlyTextRegion.bufferSize;
+        readOnlyTextRegion.permissions  = VM_PROT_READ;
+        readOnlyTextRegion.name         = "__PRELINK_TEXT";
+    }
+
+    // __TEXT segments with r/x permissions
+    {
+        // __TEXT segments with r/x permissions
+        __block uint64_t offsetInRegion = 0;
+        for (DylibInfo& dylib : sortedDylibs) {
+            bool canBePacked = dylib.input->mappedFile.mh->hasSplitSeg();
+            if (!canBePacked)
+                continue;
+
+            __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 ( strcmp(segInfo.segName, "__HIB") == 0 )
+                    return;
+                if ( segInfo.protections != (VM_PROT_READ | VM_PROT_EXECUTE) )
+                   return;
+                // kxld packs __TEXT_EXEC so we will do
+                // Note we align to at least 16-bytes as LDR's can scale up to 16 from their address
+                // and aligning them less than 16 would break that
+                uint32_t minAlignmentP2 = getMinAlignment(dylib.input->mappedFile.mh);
+                offsetInRegion = align(offsetInRegion, std::max(segInfo.p2align, 4U));
+                offsetInRegion = align(offsetInRegion, minAlignmentP2);
+                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+                uint64_t dstCacheSegmentSize = align(segInfo.sizeOfSections, minAlignmentP2);
+                SegmentMappingInfo loc;
+                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+                loc.segName                = segInfo.segName;
+                loc.dstSegment             = nullptr;
+                loc.dstCacheUnslidAddress  = offsetInRegion; // This will be updated later once we've assigned addresses
+                loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
+                loc.dstCacheSegmentSize    = (uint32_t)dstCacheSegmentSize;
+                loc.dstCacheFileSize       = (uint32_t)copySize;
+                loc.copySegmentSize        = (uint32_t)copySize;
+                loc.srcSegmentIndex        = segInfo.segIndex;
+                loc.parentRegion           = &readExecuteRegion;
+                dylib.cacheLocation[segInfo.segIndex] = loc;
+                offsetInRegion += loc.dstCacheSegmentSize;
+            });
+        }
+
+        // align r/x region end
+        readExecuteRegion.bufferSize  = align(offsetInRegion, 14);
+        readExecuteRegion.sizeInUse   = readExecuteRegion.bufferSize;
+        readExecuteRegion.permissions = VM_PROT_READ | VM_PROT_EXECUTE;
+        readExecuteRegion.name        = "__TEXT_EXEC";
+    }
+
+    if ( branchTargetsFromKexts != 0 ) {
+        // 6-bytes per jmpq
+        branchStubsRegion.bufferSize    = align(branchTargetsFromKexts * 6, 14);
+        branchStubsRegion.sizeInUse     = branchStubsRegion.bufferSize;
+        branchStubsRegion.permissions   = VM_PROT_READ | VM_PROT_EXECUTE;
+        branchStubsRegion.name          = "__BRANCH_STUBS";
+    }
+
+    // __DATA_CONST segments
+    {
+        __block uint64_t offsetInRegion = 0;
+        for (DylibInfo& dylib : sortedDylibs) {
+            if (!dylib.input->mappedFile.mh->hasSplitSeg())
+                continue;
+
+            __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_EXECUTE) != 0 )
+                    return;
+                if ( (strcmp(segInfo.segName, "__DATA_CONST") != 0)
+                    && (strcmp(segInfo.segName, "__PPLDATA_CONST") != 0)
+                    && (strcmp(segInfo.segName, "__LASTDATA_CONST") != 0)  )
+                    return;
+                // kxld packs __DATA_CONST so we will do
+                uint32_t minAlignmentP2 = getMinAlignment(dylib.input->mappedFile.mh);
+                offsetInRegion = align(offsetInRegion, segInfo.p2align);
+                offsetInRegion = align(offsetInRegion, minAlignmentP2);
+                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+                uint64_t dstCacheSegmentSize = align(segInfo.sizeOfSections, minAlignmentP2);
+                SegmentMappingInfo loc;
+                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+                loc.segName                = segInfo.segName;
+                loc.dstSegment             = nullptr;
+                loc.dstCacheUnslidAddress  = offsetInRegion; // This will be updated later once we've assigned addresses
+                loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
+                loc.dstCacheSegmentSize    = (uint32_t)dstCacheSegmentSize;
+                loc.dstCacheFileSize       = (uint32_t)copySize;
+                loc.copySegmentSize        = (uint32_t)copySize;
+                loc.srcSegmentIndex        = segInfo.segIndex;
+                loc.parentRegion           = &dataConstRegion;
+                dylib.cacheLocation[segInfo.segIndex] = loc;
+                offsetInRegion += loc.dstCacheSegmentSize;
+            });
+        }
+
+        // align r/o region end
+        dataConstRegion.bufferSize  = align(offsetInRegion, 14);
+        dataConstRegion.sizeInUse   = dataConstRegion.bufferSize;
+        dataConstRegion.permissions = VM_PROT_READ;
+        dataConstRegion.name        = "__DATA_CONST";
+    }
+
+    // Branch GOTs
+    if ( branchTargetsFromKexts != 0 ) {
+        // 8-bytes per GOT
+        branchGOTsRegion.bufferSize     = align(branchTargetsFromKexts * 8, 14);
+        branchGOTsRegion.sizeInUse      = branchGOTsRegion.bufferSize;
+        branchGOTsRegion.permissions    = VM_PROT_READ | VM_PROT_WRITE;
+        branchGOTsRegion.name           = "__BRANCH_GOTS";
+    }
+
+    // __DATA* segments
+    {
+        __block uint64_t offsetInRegion = 0;
+        for (DylibInfo& dylib : sortedDylibs) {
+            if (!dylib.input->mappedFile.mh->hasSplitSeg())
+                continue;
+
+            __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 ( strcmp(segInfo.segName, "__HIB") == 0 )
+                    return;
+                if ( (strcmp(segInfo.segName, "__DATA_CONST") == 0)
+                    || (strcmp(segInfo.segName, "__PPLDATA_CONST") == 0)
+                    || (strcmp(segInfo.segName, "__LASTDATA_CONST") == 0) )
+                    return;
+                if ( segInfo.protections != (VM_PROT_READ | VM_PROT_WRITE) )
+                    return;
+                // kxld packs __DATA so we will do
+                uint32_t minAlignmentP2 = getMinAlignment(dylib.input->mappedFile.mh);
+                offsetInRegion = align(offsetInRegion, segInfo.p2align);
+                offsetInRegion = align(offsetInRegion, minAlignmentP2);
+                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+                uint64_t dstCacheSegmentSize = align(segInfo.sizeOfSections, minAlignmentP2);
+                SegmentMappingInfo loc;
+                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+                loc.segName                = segInfo.segName;
+                loc.dstSegment             = nullptr;
+                loc.dstCacheUnslidAddress  = offsetInRegion; // This will be updated later once we've assigned addresses
+                loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
+                loc.dstCacheSegmentSize    = (uint32_t)dstCacheSegmentSize;
+                loc.dstCacheFileSize       = (uint32_t)copySize;
+                loc.copySegmentSize        = (uint32_t)copySize;
+                loc.srcSegmentIndex        = segInfo.segIndex;
+                loc.parentRegion           = &readWriteRegion;
+                dylib.cacheLocation[segInfo.segIndex] = loc;
+                offsetInRegion += loc.dstCacheSegmentSize;
+            });
+        }
+
+        // align r/w region end
+        readWriteRegion.bufferSize  = align(offsetInRegion, 14);
+        readWriteRegion.sizeInUse   = readWriteRegion.bufferSize;
+        readWriteRegion.permissions = VM_PROT_READ | VM_PROT_WRITE;
+        readWriteRegion.name        = "__DATA";
+    }
+
+    {
+        // Hibernate region
+        __block uint64_t offsetInRegion = 0;
+        for (DylibInfo& dylib : sortedDylibs) {
+            if ( !dylib.input->mappedFile.mh->isStaticExecutable() )
+                continue;
+
+            __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 ( strcmp(segInfo.segName, "__HIB") != 0 )
+                    return;
+                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+                SegmentMappingInfo loc;
+                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+                loc.segName                = segInfo.segName;
+                loc.dstSegment             = nullptr;
+                loc.dstCacheUnslidAddress  = offsetInRegion; // This will be updated later once we've assigned addresses
+                loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
+                loc.dstCacheSegmentSize    = (uint32_t)segInfo.vmSize;
+                loc.dstCacheFileSize       = (uint32_t)copySize;
+                loc.copySegmentSize        = (uint32_t)copySize;
+                loc.srcSegmentIndex        = segInfo.segIndex;
+                loc.parentRegion           = &hibernateRegion;
+                dylib.cacheLocation[segInfo.segIndex] = loc;
+                offsetInRegion += loc.dstCacheSegmentSize;
+
+                hibernateAddress = segInfo.vmAddr;
+            });
+
+            // Only xnu has __HIB, so no need to continue once we've found it.
+            break;
+        }
+
+        hibernateRegion.bufferSize   = align(offsetInRegion, 14);
+        hibernateRegion.sizeInUse    = hibernateRegion.bufferSize;
+        hibernateRegion.permissions  = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
+        hibernateRegion.name         = "__HIB";
+    }
+
+    // __TEXT and __DATA from non-split seg dylibs, if we have any
+    {
+        for (DylibInfo& dylib : sortedDylibs) {
+            bool canBePacked = dylib.input->mappedFile.mh->hasSplitSeg();
+            if (canBePacked)
+                continue;
+
+            __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 ( strcmp(segInfo.segName, "__LINKEDIT") == 0 )
+                    return;
+
+                nonSplitSegRegions.emplace_back();
+                nonSplitSegRegions.back().permissions = segInfo.protections;
+                nonSplitSegRegions.back().name        = "__REGION" + std::to_string(nonSplitSegRegions.size() - 1);
+
+                // Note we don't align the region offset as we have no split seg
+                uint64_t offsetInRegion = 0;
+                SegmentMappingInfo loc;
+                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+                loc.segName                = segInfo.segName;
+                loc.dstSegment             = nullptr;
+                loc.dstCacheUnslidAddress  = offsetInRegion; // This will be updated later once we've assigned addresses
+                loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
+                loc.dstCacheSegmentSize    = (uint32_t)segInfo.vmSize;
+                loc.dstCacheFileSize       = (uint32_t)segInfo.fileSize;
+                loc.copySegmentSize        = (uint32_t)segInfo.fileSize;
+                loc.srcSegmentIndex        = segInfo.segIndex;
+                loc.parentRegion           = &nonSplitSegRegions.back();
+                dylib.cacheLocation[segInfo.segIndex] = loc;
+                offsetInRegion += loc.dstCacheSegmentSize;
+
+                // record non-split seg region end
+                nonSplitSegRegions.back().bufferSize  = offsetInRegion;
+                nonSplitSegRegions.back().sizeInUse   = nonSplitSegRegions.back().bufferSize;
+            });
+        }
+    }
+
+    // -sectcreate
+    if ( !customSegments.empty() ) {
+        for (CustomSegment& segment: customSegments) {
+            uint64_t offsetInRegion = 0;
+            for (CustomSegment::CustomSection& section : segment.sections) {
+                section.offsetInRegion = offsetInRegion;
+                offsetInRegion += section.data.size();
+            }
+
+            Region& customRegion = customDataRegions.emplace_back();
+            segment.parentRegion = &customRegion;
+
+            // align region end
+            customRegion.bufferSize  = align(offsetInRegion, 14);
+            customRegion.sizeInUse   = customRegion.bufferSize;
+            customRegion.permissions = VM_PROT_READ;
+            customRegion.name        = segment.segmentName;
+        }
+    }
+
+    // __PRELINK_INFO
+    {
+        // This is populated with regular kexts and codeless kexts
+        struct PrelinkInfo {
+            CFDictionaryRef             infoPlist       = nullptr;
+            const dyld3::MachOAnalyzer* ma              = nullptr;
+            std::string_view            bundlePath;
+            std::string_view            executablePath;
+        };
+        std::vector<PrelinkInfo> infos;
+        for (AppCacheDylibInfo& dylib : sortedDylibs) {
+            if (dylib.infoPlist == nullptr)
+                continue;
+            infos.push_back({ dylib.infoPlist, dylib.input->mappedFile.mh, dylib.bundlePath, dylib.input->loadedFileInfo.path });
+        }
+        for (InputDylib& dylib : codelessKexts) {
+            infos.push_back({ dylib.infoPlist, nullptr, dylib.bundlePath, "" });
+        }
+
+        CFMutableArrayRef bundlesArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0,
+                                                                 &kCFTypeArrayCallBacks);
+        for (PrelinkInfo& info : infos) {
+            CFDictionaryRef infoPlist = info.infoPlist;
+            // Create a copy of the dictionary so that we can add more fields
+            CFMutableDictionaryRef dictCopyRef = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, infoPlist);
+
+            // _PrelinkBundlePath
+            CFStringRef bundlePath = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, info.bundlePath.data(),
+                                                                     kCFStringEncodingASCII, kCFAllocatorNull);
+            CFDictionarySetValue(dictCopyRef, CFSTR("_PrelinkBundlePath"), bundlePath);
+            CFRelease(bundlePath);
+
+            // Note we want this address to be a large enough integer in the xml format that we have enough space
+            // to replace it with its real address later
+            const uint64_t largeAddress = 0x7FFFFFFFFFFFFFFF;
+
+            // _PrelinkExecutableLoadAddr
+            // Leave a placeholder for this for now just so that we have enough space for it later
+            CFNumberRef loadAddrRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &largeAddress);
+            CFDictionarySetValue(dictCopyRef, CFSTR("_PrelinkExecutableLoadAddr"), loadAddrRef);
+            CFRelease(loadAddrRef);
+
+            // _PrelinkExecutableRelativePath
+            if ( info.executablePath != "" ) {
+                const char* relativePath = info.executablePath.data();
+                if ( strncmp(relativePath, info.bundlePath.data(), info.bundlePath.size()) == 0 ) {
+                    relativePath = relativePath + info.bundlePath.size();
+                    if ( relativePath[0] == '/' )
+                        ++relativePath;
+                } else if ( const char* lastSlash = strrchr(relativePath, '/') )
+                    relativePath = lastSlash+1;
+                CFStringRef executablePath = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, relativePath,
+                                                                             kCFStringEncodingASCII, kCFAllocatorNull);
+                CFDictionarySetValue(dictCopyRef, CFSTR("_PrelinkExecutableRelativePath"), executablePath);
+                CFRelease(executablePath);
+            }
+
+            // _PrelinkExecutableSize
+            // This seems to be the file size of __TEXT
+            __block uint64_t textSegFileSize = 0;
+            if ( info.ma != nullptr ) {
+                info.ma->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+                    if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+                        textSegFileSize = segInfo.fileSize;
+                });
+            }
+            if (textSegFileSize != 0) {
+                CFNumberRef fileSizeRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &textSegFileSize);
+                CFDictionarySetValue(dictCopyRef, CFSTR("_PrelinkExecutableSize"), fileSizeRef);
+                CFRelease(fileSizeRef);
+            }
+
+            // _PrelinkExecutableSourceAddr
+            // Leave a placeholder for this for now just so that we have enough space for it later
+            CFNumberRef sourceAddrRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &largeAddress);
+            CFDictionarySetValue(dictCopyRef, CFSTR("_PrelinkExecutableSourceAddr"), sourceAddrRef);
+            CFRelease(sourceAddrRef);
+
+            // _PrelinkKmodInfo
+            // Leave a placeholder for this for now just so that we have enough space for it later
+            dyld3::MachOAnalyzer::FoundSymbol foundInfo;
+            if ( (info.ma != nullptr) ) {
+                // Check for a global first
+                __block bool found = false;
+                found = info.ma->findExportedSymbol(_diagnostics, "_kmod_info", true, foundInfo, nullptr);
+                if ( !found ) {
+                    // And fall back to a local if we need to
+                    info.ma->forEachLocalSymbol(_diagnostics, ^(const char* aSymbolName, uint64_t n_value, uint8_t n_type,
+                                                                uint8_t n_sect, uint16_t n_desc, bool& stop) {
+                        if ( strcmp(aSymbolName, "_kmod_info") == 0 ) {
+                            found = true;
+                            stop = true;
+                        }
+                    });
+                }
+
+                if ( found ) {
+                    CFNumberRef kmodInfoAddrRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &largeAddress);
+                    CFDictionarySetValue(dictCopyRef, CFSTR("_PrelinkKmodInfo"), kmodInfoAddrRef);
+                    CFRelease(kmodInfoAddrRef);
+                }
+            }
+
+            CFArrayAppendValue(bundlesArrayRef, dictCopyRef);
+            // Release the temporary dictionary now that its in the array
+            CFRelease(dictCopyRef);
+        }
+
+        prelinkInfoDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                                    &kCFTypeDictionaryKeyCallBacks,
+                                                    &kCFTypeDictionaryValueCallBacks);
+
+        // First add any data from addPrelinkInfo()
+        if ( extraPrelinkInfo != nullptr ) {
+            CFDictionaryApplierFunction applier = [](const void *key, const void *value, void *context) {
+                CFMutableDictionaryRef parentDict = (CFMutableDictionaryRef)context;
+                CFDictionaryAddValue(parentDict, key, value);
+            };
+            CFDictionaryApplyFunction(extraPrelinkInfo, applier, (void*)prelinkInfoDict);
+        }
+
+        if ( bundlesArrayRef != nullptr ) {
+            CFDictionaryAddValue(prelinkInfoDict, CFSTR("_PrelinkInfoDictionary"), bundlesArrayRef);
+            CFRelease(bundlesArrayRef);
+        }
+
+        // Add a placeholder for the collection UUID
+        {
+            uuid_t uuid = {};
+            CFDataRef dataRef = CFDataCreate(kCFAllocatorDefault, (const uint8_t*)&uuid, sizeof(uuid));
+            CFDictionaryAddValue(prelinkInfoDict, CFSTR("_PrelinkKCID"), dataRef);
+            CFRelease(dataRef);
+        }
+
+        // The pageable/aux KCs should embed the UUID of the base kernel collection
+        if ( existingKernelCollection != nullptr ) {
+            uuid_t uuid = {};
+            bool foundUUID = existingKernelCollection->getUuid(uuid);
+            if ( !foundUUID ) {
+                _diagnostics.error("Could not find UUID in base kernel collection");
+                return;
+            }
+            CFDataRef dataRef = CFDataCreate(kCFAllocatorDefault, (const uint8_t*)&uuid, sizeof(uuid));
+            CFDictionaryAddValue(prelinkInfoDict, CFSTR("_BootKCID"), dataRef);
+            CFRelease(dataRef);
+        }
+
+        // The aux KC should embed the UUID of the pageable kernel collection if we have one
+        if ( pageableKernelCollection != nullptr ) {
+            uuid_t uuid = {};
+            bool foundUUID = pageableKernelCollection->getUuid(uuid);
+            if ( !foundUUID ) {
+                _diagnostics.error("Could not find UUID in pageable kernel collection");
+                return;
+            }
+            CFDataRef dataRef = CFDataCreate(kCFAllocatorDefault, (const uint8_t*)&uuid, sizeof(uuid));
+            CFDictionaryAddValue(prelinkInfoDict, CFSTR("_PageableKCID"), dataRef);
+            CFRelease(dataRef);
+        }
+
+        CFErrorRef errorRef = nullptr;
+        CFDataRef xmlData = CFPropertyListCreateData(kCFAllocatorDefault, prelinkInfoDict,
+                                                     kCFPropertyListXMLFormat_v1_0, 0, &errorRef);
+        if (errorRef != nullptr) {
+            CFStringRef errorString = CFErrorCopyDescription(errorRef);
+            _diagnostics.error("Could not serialise plist because :%s",
+                               CFStringGetCStringPtr(errorString, kCFStringEncodingASCII));
+            CFRelease(xmlData);
+            CFRelease(errorRef);
+            return;
+        } else {
+            CFIndex xmlDataLength = CFDataGetLength(xmlData);
+            CFRelease(xmlData);
+
+            // align region end
+            prelinkInfoRegion.bufferSize  = align(xmlDataLength, 14);
+            prelinkInfoRegion.sizeInUse   = prelinkInfoRegion.bufferSize;
+            prelinkInfoRegion.permissions = VM_PROT_READ | VM_PROT_WRITE;
+            prelinkInfoRegion.name        = "__PRELINK_INFO";
+        }
+    }
+
+    // Do all __LINKINFO regardless of split seg
+    _nonLinkEditReadOnlySize = 0;
+    __block uint64_t offsetInRegion = 0;
+    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(segInfo.segName, "__LINKINFO") != 0 )
+                return;
+            // Keep segments 4K or more aligned
+            offsetInRegion = align(offsetInRegion, std::max((int)segInfo.p2align, (int)12));
+            size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+            SegmentMappingInfo loc;
+            loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+            loc.segName                = segInfo.segName;
+            loc.dstSegment             = nullptr;
+            loc.dstCacheUnslidAddress  = offsetInRegion; // This will be updated later once we've assigned addresses
+            loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
+            loc.dstCacheSegmentSize    = (uint32_t)align(segInfo.sizeOfSections, 12);
+            loc.dstCacheFileSize       = (uint32_t)copySize;
+            loc.copySegmentSize        = (uint32_t)copySize;
+            loc.srcSegmentIndex        = segInfo.segIndex;
+            loc.parentRegion           = &_readOnlyRegion;
+            dylib.cacheLocation[segInfo.segIndex] = loc;
+            offsetInRegion += loc.dstCacheSegmentSize;
+        });
+    }
+
+    // Align the end of the __LINKINFO
+    offsetInRegion = align(offsetInRegion, 14);
+    _nonLinkEditReadOnlySize = offsetInRegion;
+
+    // Do all __LINKEDIT, regardless of split seg
+    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(segInfo.segName, "__LINKEDIT") != 0 )
+                return;
+            // Keep segments 4K or more aligned
+            offsetInRegion = align(offsetInRegion, std::max((int)segInfo.p2align, (int)12));
+            size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+            SegmentMappingInfo loc;
+            loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+            loc.segName                = segInfo.segName;
+            loc.dstSegment             = nullptr;
+            loc.dstCacheUnslidAddress  = offsetInRegion; // This will be updated later once we've assigned addresses
+            loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
+            loc.dstCacheSegmentSize    = (uint32_t)align(segInfo.sizeOfSections, 12);
+            loc.dstCacheFileSize       = (uint32_t)copySize;
+            loc.copySegmentSize        = (uint32_t)copySize;
+            loc.srcSegmentIndex        = segInfo.segIndex;
+            loc.parentRegion           = &_readOnlyRegion;
+            dylib.cacheLocation[segInfo.segIndex] = loc;
+            offsetInRegion += loc.dstCacheSegmentSize;
+        });
+    }
+
+    // align r/o region end
+    _readOnlyRegion.bufferSize  = align(offsetInRegion, 14);
+    _readOnlyRegion.sizeInUse   = _readOnlyRegion.bufferSize;
+    _readOnlyRegion.permissions = VM_PROT_READ;
+    _readOnlyRegion.name        = "__LINKEDIT";
+
+    // Add space in __LINKEDIT for chained fixups and classic relocs
+    {
+
+        // The pageableKC (and sometimes auxKC) has 1 LC_DYLD_CHAINED_FIXUPS per kext
+        // while other KCs have 1 for the whole KC.
+        // It also tracks each segment in each kext for chained fixups, not the segments on the KC itself
+        __block uint64_t numSegmentsForChainedFixups = 0;
+        uint64_t numChainedFixupHeaders = 0;
+        if ( fixupsArePerKext() ) {
+            for (DylibInfo& dylib : sortedDylibs) {
+                dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+                    ++numSegmentsForChainedFixups;
+                });
+            }
+            numChainedFixupHeaders = sortedDylibs.size();
+
+            // Branch stubs need fixups on the GOTs region.  So add in a top-level chained fixup entry
+            // and for now all the regions as we don't know what segment index the branch GOTs will be
+            numSegmentsForChainedFixups += numRegions();
+            numChainedFixupHeaders++;
+        } else {
+            numSegmentsForChainedFixups = numRegions();
+            numChainedFixupHeaders = 1;
+        }
+
+        uint64_t numBytesForPageStarts = 0;
+        if ( dataConstRegion.sizeInUse != 0 )
+            numBytesForPageStarts += sizeof(dyld_chained_starts_in_segment) + (sizeof(uint16_t) * numWritablePagesToFixup(dataConstRegion.bufferSize));
+        if ( branchGOTsRegion.bufferSize != 0 )
+            numBytesForPageStarts += sizeof(dyld_chained_starts_in_segment) + (sizeof(uint16_t) * numWritablePagesToFixup(branchGOTsRegion.bufferSize));
+        if ( readWriteRegion.sizeInUse != 0 )
+            numBytesForPageStarts += sizeof(dyld_chained_starts_in_segment) + (sizeof(uint16_t) * numWritablePagesToFixup(readWriteRegion.bufferSize));
+        if ( hibernateRegion.sizeInUse != 0 )
+            numBytesForPageStarts += sizeof(dyld_chained_starts_in_segment) + (sizeof(uint16_t) * numWritablePagesToFixup(hibernateRegion.bufferSize));
+        for (const Region& region : nonSplitSegRegions) {
+            // Assume writable regions have fixups to emit
+            // Note, third party kext's have __TEXT fixups, so assume all of these have fixups
+            // LINKEDIT is already elsewhere
+            numBytesForPageStarts += sizeof(dyld_chained_starts_in_segment) + (sizeof(uint16_t) * numWritablePagesToFixup(region.bufferSize));
+        }
+
+        uint64_t numBytesForChainedFixups = 0;
+        if ( numBytesForPageStarts != 0 ) {
+            numBytesForChainedFixups = numBytesForPageStarts;
+            numBytesForChainedFixups += sizeof(dyld_chained_fixups_header) * numChainedFixupHeaders;
+            numBytesForChainedFixups += sizeof(dyld_chained_starts_in_image) * numChainedFixupHeaders;
+            numBytesForChainedFixups += sizeof(uint32_t) * numSegmentsForChainedFixups;
+        }
+
+        __block uint64_t numBytesForClassicRelocs = 0;
+        if ( appCacheOptions.cacheKind == Options::AppCacheKind::kernel ) {
+            if ( const DylibInfo* dylib = getKernelStaticExecutableInputFile() ) {
+                if ( dylib->input->mappedFile.mh->usesClassicRelocationsInKernelCollection() ) {
+                    dylib->input->mappedFile.mh->forEachRebase(_diagnostics, false, ^(uint64_t runtimeOffset, bool &stop) {
+                        numBytesForClassicRelocs += sizeof(relocation_info);
+                    });
+                }
+            }
+        }
+
+        // align fixups region end
+        if ( (numBytesForChainedFixups != 0) || (numBytesForClassicRelocs != 0) ) {
+            uint64_t numBytes = align(numBytesForChainedFixups, 3) + align(numBytesForClassicRelocs, 3);
+            fixupsSubRegion.bufferSize  = align(numBytes, 14);
+            fixupsSubRegion.sizeInUse   = fixupsSubRegion.bufferSize;
+            fixupsSubRegion.permissions = VM_PROT_READ;
+            fixupsSubRegion.name        = "__FIXUPS";
+        }
+    }
+}
+
+void AppCacheBuilder::assignSegmentAddresses() {
+    // Segments already have offsets in to their regions.  Now assign the regions their addresses
+    // in the full allocated buffer, and then assign all segments in those regions
+    for (DylibInfo& dylib : sortedDylibs) {
+        for (SegmentMappingInfo& loc : dylib.cacheLocation) {
+            loc.dstSegment = loc.parentRegion->buffer + loc.dstCacheFileOffset;
+            loc.dstCacheUnslidAddress   = loc.parentRegion->unslidLoadAddress + loc.dstCacheFileOffset;
+            loc.dstCacheFileOffset      = (uint32_t)loc.parentRegion->cacheFileOffset + loc.dstCacheFileOffset;
+        }
+    }
+}
+
+void AppCacheBuilder::copyRawSegments() {
+    const bool log = false;
+
+    // Call the base class to copy segment data
+    CacheBuilder::copyRawSegments();
+
+    // The copy any custom sections
+    for (const CustomSegment& segment : customSegments) {
+        for (const CustomSegment::CustomSection& section : segment.sections) {
+            uint8_t* dstBuffer = segment.parentRegion->buffer + section.offsetInRegion;
+            uint64_t dstVMAddr = segment.parentRegion->unslidLoadAddress + section.offsetInRegion;
+            if (log) fprintf(stderr, "copy %s segment %s %s (0x%08lX bytes) from %p to %p (logical addr 0x%llX)\n",
+                             _options.archs->name(), segment.segmentName.c_str(), section.sectionName.c_str(),
+                             section.data.size(), section.data.data(), dstBuffer, dstVMAddr);
+            ::memcpy(dstBuffer, section.data.data(), section.data.size());
+        }
+    }
+}
+
+static uint8_t getFixupLevel(AppCacheBuilder::Options::AppCacheKind kind) {
+    uint8_t currentLevel = (uint8_t)~0U;
+    switch (kind) {
+        case AppCacheBuilder::Options::AppCacheKind::none:
+            assert(0 && "Cache kind should have been set");
+            break;
+        case AppCacheBuilder::Options::AppCacheKind::kernel:
+            currentLevel = 0;
+            break;
+        case AppCacheBuilder::Options::AppCacheKind::pageableKC:
+            // The pageableKC sits right above the baseKC which is level 0
+            currentLevel = 1;
+            break;
+        case AppCacheBuilder::Options::AppCacheKind::kernelCollectionLevel2:
+            assert(0 && "Unimplemented");
+            break;
+        case AppCacheBuilder::Options::AppCacheKind::auxKC:
+            currentLevel = 3;
+            break;
+    }
+    return currentLevel;
+}
+
+uint32_t AppCacheBuilder::getCurrentFixupLevel() const {
+    return getFixupLevel(appCacheOptions.cacheKind);
+}
+
+struct VTableBindSymbol {
+    std::string_view binaryID;
+    std::string symbolName;
+};
+
+// For every dylib, lets make a map from its exports to its defs
+struct DylibSymbols {
+    // Define a bunch of constructors so that we know we are getting move constructors not copies
+    DylibSymbols() = default;
+    DylibSymbols(const DylibSymbols&) = delete;
+    DylibSymbols(DylibSymbols&&) = default;
+    DylibSymbols(std::map<std::string_view, uint64_t>&& globals,
+                 std::map<std::string_view, uint64_t>&& locals,
+                 std::unique_ptr<std::unordered_set<std::string>> kpiSymbols,
+                 uint32_t dylibLevel, const std::string& dylibName)
+        : globals(std::move(globals)), locals(std::move(locals)), kpiSymbols(std::move(kpiSymbols)),
+          dylibLevel(dylibLevel), dylibName(dylibName) { }
+
+    DylibSymbols& operator=(const DylibSymbols& other) = delete;
+    DylibSymbols& operator=(DylibSymbols&& other) = default;
+
+    std::map<std::string_view, uint64_t> globals;
+
+    // We also need to track locals as vtable patching supports patching with these too
+    std::map<std::string_view, uint64_t> locals;
+
+    // KPI (ie, a symbol set embedded in this binary)
+    std::unique_ptr<std::unordered_set<std::string>> kpiSymbols;
+
+    // Kernel collections can reference each other in levels.  This is the level
+    // of the exported dylib.  Eg, the base KC is 0, and the aux KC is 3
+    uint32_t dylibLevel = 0;
+
+    // Store the name of the dylib for fast lookups
+    std::string dylibName;
+
+    // Keep track of the binds in this dylib as these tell us if a vtable slot is to a local
+    // or external definition of a function
+    std::unordered_map<const uint8_t*, VTableBindSymbol> resolvedBindLocations;
+};
+
+class VTablePatcher {
+public:
+
+    VTablePatcher(uint32_t numFixupLevels);
+
+    bool hasError() const;
+
+    void addKernelCollection(const dyld3::MachOAppCache* cacheMA, AppCacheBuilder::Options::AppCacheKind kind,
+                             const uint8_t* basePointer, uint64_t baseAddress);
+    void addDylib(Diagnostics& diags, const dyld3::MachOAnalyzer* ma, const std::string& dylibID,
+                  const std::vector<std::string>& dependencies, uint8_t cacheLevel);
+
+    void findMetaclassDefinitions(std::map<std::string, DylibSymbols>& dylibsToSymbols,
+                                  const std::string& kernelID, const dyld3::MachOAnalyzer* kernelMA,
+                                  AppCacheBuilder::Options::AppCacheKind cacheKind);
+    void findExistingFixups(Diagnostics& diags,
+                            const dyld3::MachOAppCache* existingKernelCollection,
+                            const dyld3::MachOAppCache* pageableKernelCollection);
+    void findBaseKernelVTables(Diagnostics& diags, const dyld3::MachOAppCache* existingKernelCollection,
+                               std::map<std::string, DylibSymbols>& dylibsToSymbols);
+    void findPageableKernelVTables(Diagnostics& diags, const dyld3::MachOAppCache* existingKernelCollection,
+                                   std::map<std::string, DylibSymbols>& dylibsToSymbols);
+    void findVTables(uint8_t currentLevel, const dyld3::MachOAnalyzer* kernelMA,
+                     std::map<std::string, DylibSymbols>& dylibsToSymbols,
+                     const AppCacheBuilder::ASLR_Tracker& aslrTracker,
+                     const std::map<const uint8_t*, const VTableBindSymbol>& missingBindLocations);
+    void calculateSymbols();
+    void patchVTables(Diagnostics& diags,
+                      std::map<const uint8_t*, const VTableBindSymbol>& missingBindLocations,
+                      AppCacheBuilder::ASLR_Tracker& aslrTracker,
+                      uint8_t currentLevel);
+
+private:
+
+    void logFunc(const char* format, ...) {
+        if ( logPatching ) {
+            va_list list;
+            va_start(list, format);
+            vfprintf(stderr, format, list);
+            va_end(list);
+        }
+    };
+
+    void logFuncVerbose(const char* format, ...) {
+        if ( logPatchingVerbose ) {
+            va_list list;
+            va_start(list, format);
+            vfprintf(stderr, format, list);
+            va_end(list);
+        }
+    };
+
+    // Extract a substring by dropping optional prefix/suffix
+    std::string_view extractString(std::string_view str, std::string_view prefix, std::string_view suffix) {
+        if ( !prefix.empty() ) {
+            // Make sure we have the prefix we are looking for
+            if ( str.find(prefix) != 0 ) {
+                return std::string_view();
+            }
+            str.remove_prefix(prefix.size());
+        }
+        if ( !suffix.empty() ) {
+            // Make sure we have the prefix we are looking for
+            size_t pos = str.rfind(suffix);
+            if ( pos != (str.size() - suffix.size()) ) {
+                return std::string_view();
+            }
+            str.remove_suffix(suffix.size());
+        }
+        return str;
+    };
+
+    struct VTable {
+        struct Entry {
+            const uint8_t*  location            = nullptr;
+            uint64_t        targetVMAddr        = ~0ULL;
+            uint32_t        targetCacheLevel    = ~0;
+            // Pointer auth
+            uint16_t        diversity           = 0;
+            bool            hasAddrDiv          = false;
+            uint8_t         key                 = 0;
+            bool            hasPointerAuth      = false;
+        };
+
+        const dyld3::MachOAnalyzer* ma                      = nullptr;
+        const uint8_t*              superVTable             = nullptr;
+        const DylibSymbols*         dylib                   = nullptr;
+        bool                        fromParentCollection    = false;
+        bool                        patched                 = false;
+        std::string                 name                    = "";
+        std::vector<Entry>          entries;
+    };
+
+    struct SymbolLocation {
+        uint64_t    vmAddr          = 0;
+        bool        foundSymbol     = 0;
+
+        bool found() const {
+            return foundSymbol;
+        }
+    };
+
+    struct Fixup {
+        uint64_t targetVMAddr = 0;
+        uint8_t  cacheLevel   = 0;
+        // Pointer auth
+        uint16_t        diversity       = 0;
+        bool            hasAddrDiv      = false;
+        uint8_t         key             = 0;
+        bool            hasPointerAuth  = false;
+    };
+
+    struct VTableDylib {
+        Diagnostics*                diags           = nullptr;
+        const dyld3::MachOAnalyzer* ma              = nullptr;
+        std::string                 dylibID         = "";
+        std::vector<std::string>    dependencies;
+        uint32_t                    cacheLevel      = ~0U;
+    };
+
+    struct KernelCollection {
+        const dyld3::MachOAppCache*                 ma                      = nullptr;
+
+        // We need the base pointers to the buffers for every level
+        // These are the base of the allocated memory, which corresponds to pointing to the lowest
+        // vmAddr for the buffer.  These do *not* necessarily point to a mach_header
+        const uint8_t*                              basePointer             = nullptr;
+
+        // We also need the base vm addresses to the buffers for every level
+        uint64_t                                    baseAddress             = ~0ULL;
+
+        std::unordered_map<uint64_t, const char*>   symbolNames;
+        std::map<uint64_t, std::string_view>        metaclassDefinitions;
+    };
+
+    SymbolLocation findVTablePatchingSymbol(std::string_view symbolName, const DylibSymbols& dylibSymbols);
+
+    std::vector<VTableDylib>                                dylibs;
+    std::map<const uint8_t*, VTable>                        vtables;
+    std::vector<KernelCollection>                           collections;
+    const uint8_t*                                          baseMetaClassVTableLoc  = nullptr;
+
+    // Record all the fixup locations in the base/pageable KCs as we need to use them instead of the ASLR tracker
+    std::map<const uint8_t*, Fixup>                         existingCollectionFixupLocations;
+
+    const uint32_t                                          pointerSize             = 8;
+    const bool                                              logPatching             = false;
+    const bool                                              logPatchingVerbose      = false;
+
+    // Magic constants for vtable patching
+    //const char*                                           cxxPrefix                   = "__Z";
+    const char*                                             vtablePrefix                = "__ZTV";
+    const char*                                             osObjPrefix                 = "__ZN";
+    // const char*                                          vtableReservedToken         = "_RESERVED";
+    const char*                                             metaclassToken              = "10gMetaClassE";
+    const char*                                             superMetaclassPointerToken  = "10superClassE";
+    const char*                                             metaclassVTablePrefix       = "__ZTVN";
+    const char*                                             metaclassVTableSuffix       = "9MetaClassE";
+};
+
+VTablePatcher::VTablePatcher(uint32_t numFixupLevels) {
+    collections.resize(numFixupLevels);
+}
+
+bool VTablePatcher::hasError() const {
+    for (const VTableDylib& dylib : dylibs) {
+        if ( dylib.diags->hasError() )
+            return true;
+    }
+    return false;
+}
+
+void VTablePatcher::addKernelCollection(const dyld3::MachOAppCache* cacheMA, AppCacheBuilder::Options::AppCacheKind kind,
+                                        const uint8_t* basePointer, uint64_t baseAddress) {
+    uint8_t cacheLevel = getFixupLevel(kind);
+
+    assert(cacheLevel < collections.size());
+    assert(collections[cacheLevel].ma == nullptr);
+
+    collections[cacheLevel].ma          = cacheMA;
+    collections[cacheLevel].basePointer = basePointer;
+    collections[cacheLevel].baseAddress = baseAddress;
+}
+
+void VTablePatcher::addDylib(Diagnostics &diags, const dyld3::MachOAnalyzer *ma,
+                             const std::string& dylibID, const std::vector<std::string>& dependencies,
+                             uint8_t cacheLevel) {
+    dylibs.push_back((VTableDylib){ &diags, ma, dylibID, dependencies, cacheLevel });
+}
+
+VTablePatcher::SymbolLocation VTablePatcher::findVTablePatchingSymbol(std::string_view symbolName,
+                                                                      const DylibSymbols& dylibSymbols) {
+    // First look in the globals
+    auto globalsIt = dylibSymbols.globals.find(symbolName);
+    if ( globalsIt != dylibSymbols.globals.end() ) {
+        return { globalsIt->second, true };
+    }
+
+    // Then again in the locals
+    auto localsIt = dylibSymbols.locals.find(symbolName);
+    if ( localsIt != dylibSymbols.locals.end() ) {
+        return { localsIt->second, true };
+    }
+
+    return { ~0ULL, false };
+};
+
+void VTablePatcher::findMetaclassDefinitions(std::map<std::string, DylibSymbols>& dylibsToSymbols,
+                                             const std::string& kernelID, const dyld3::MachOAnalyzer* kernelMA,
+                                             AppCacheBuilder::Options::AppCacheKind cacheKind) {
+    for (VTableDylib& dylib : dylibs) {
+        auto& metaclassDefinitions = collections[dylib.cacheLevel].metaclassDefinitions;
+        dylib.ma->forEachGlobalSymbol(*dylib.diags, ^(const char *symbolName, uint64_t n_value,
+                                                      uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) {
+            if ( strstr(symbolName, metaclassToken) != nullptr )
+                metaclassDefinitions[n_value] = symbolName;
+        });
+        dylib.ma->forEachLocalSymbol(*dylib.diags, ^(const char *symbolName, uint64_t n_value,
+                                                     uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) {
+            if ( strstr(symbolName, metaclassToken) != nullptr )
+                metaclassDefinitions[n_value] = symbolName;
+        });
+    }
+
+    // Keep track of the root OSMetaClass from which all other metaclasses inherit
+    DylibSymbols& kernelDylibSymbols = dylibsToSymbols[kernelID];
+    SymbolLocation symbolLocation = findVTablePatchingSymbol("__ZTV11OSMetaClass", kernelDylibSymbols);
+    if ( symbolLocation.found() ) {
+        baseMetaClassVTableLoc = (uint8_t*)kernelMA + (symbolLocation.vmAddr - kernelMA->preferredLoadAddress());
+
+        VTable& vtable = vtables[baseMetaClassVTableLoc];
+        vtable.ma                   = kernelMA;
+        vtable.dylib                = &kernelDylibSymbols;
+        vtable.fromParentCollection = (cacheKind != AppCacheBuilder::Options::AppCacheKind::kernel);
+        vtable.patched              = true;
+        vtable.name                 = "__ZTV11OSMetaClass";
+    }
+}
+
+void VTablePatcher::findExistingFixups(Diagnostics& diags,
+                                       const dyld3::MachOAppCache* existingKernelCollection,
+                                       const dyld3::MachOAppCache* pageableKernelCollection) {
+
+    const bool is64 = pointerSize == 8;
+
+    if ( existingKernelCollection != nullptr ) {
+        uint8_t kernelLevel = getFixupLevel(AppCacheBuilder::Options::AppCacheKind::kernel);
+        uint64_t kernelBaseAddress = collections[kernelLevel].baseAddress;
+        const uint8_t* kernelBasePointer = collections[kernelLevel].basePointer;
+
+        // We may have both chained and classic fixups.  First add chained
+        if ( existingKernelCollection->hasChainedFixupsLoadCommand() ) {
+            existingKernelCollection->withChainStarts(diags, 0, ^(const dyld_chained_starts_in_image* starts) {
+                existingKernelCollection->forEachFixupInAllChains(diags, starts, false,
+                                                                  ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
+                    uint64_t vmOffset = 0;
+                    bool isRebase = fixupLoc->isRebase(segInfo->pointer_format, kernelBaseAddress, vmOffset);
+                    assert(isRebase);
+                    uint64_t targetVMAddr   = kernelBaseAddress + vmOffset;
+                    uint16_t diversity      = fixupLoc->kernel64.diversity;
+                    bool     hasAddrDiv     = fixupLoc->kernel64.addrDiv;
+                    uint8_t  key            = fixupLoc->kernel64.key;
+                    bool     hasPointerAuth = fixupLoc->kernel64.isAuth;
+                    existingCollectionFixupLocations[(const uint8_t*)fixupLoc] = { targetVMAddr, kernelLevel, diversity, hasAddrDiv, key, hasPointerAuth };
+                });
+            });
+        }
+
+        // And add classic if we have them
+        existingKernelCollection->forEachRebase(diags, ^(const char *opcodeName, const dyld3::MachOAnalyzer::LinkEditInfo &leInfo,
+                                                                const dyld3::MachOAnalyzer::SegmentInfo *segments,
+                                                                bool segIndexSet, uint32_t pointerSize, uint8_t segmentIndex,
+                                                                uint64_t segmentOffset, dyld3::MachOAnalyzer::Rebase kind, bool &stop) {
+            uint64_t rebaseVmAddr  = segments[segmentIndex].vmAddr + segmentOffset;
+            uint64_t runtimeOffset = rebaseVmAddr - kernelBaseAddress;
+            const uint8_t* fixupLoc = kernelBasePointer + runtimeOffset;
+            uint64_t targetVMAddr = 0;
+            if ( is64 ) {
+                targetVMAddr = *(uint64_t*)fixupLoc;
+            } else {
+                targetVMAddr = *(uint32_t*)fixupLoc;
+            }
+            // Classic relocs have no pointer auth
+            uint16_t diversity      = 0;
+            bool     hasAddrDiv     = false;
+            uint8_t  key            = 0;
+            bool     hasPointerAuth = false;
+            existingCollectionFixupLocations[(const uint8_t*)fixupLoc] = { targetVMAddr, kernelLevel, diversity, hasAddrDiv, key, hasPointerAuth };
+        });
+    }
+
+    // Add pageable fixup locations if we have it
+    if ( pageableKernelCollection != nullptr ) {
+        // We only have chained fixups here to add, but they are on each kext, not on the KC itself
+        pageableKernelCollection->forEachDylib(diags, ^(const dyld3::MachOAnalyzer *ma, const char *name, bool &stop) {
+            // Skip kexts without fixups
+            if ( !ma->hasChainedFixupsLoadCommand() )
+                return;
+            ma->withChainStarts(diags, 0, ^(const dyld_chained_starts_in_image* starts) {
+                ma->forEachFixupInAllChains(diags, starts, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
+                    uint64_t vmOffset = 0;
+                    bool isRebase = fixupLoc->isRebase(DYLD_CHAINED_PTR_64_KERNEL_CACHE, 0, vmOffset);
+                    assert(isRebase);
+                    uint8_t targetFixupLevel    = fixupLoc->kernel64.cacheLevel;
+                    uint64_t targetVMAddr       = collections[targetFixupLevel].baseAddress + vmOffset;
+                    uint16_t diversity          = fixupLoc->kernel64.diversity;
+                    bool     hasAddrDiv         = fixupLoc->kernel64.addrDiv;
+                    uint8_t  key                = fixupLoc->kernel64.key;
+                    bool     hasPointerAuth     = fixupLoc->kernel64.isAuth;
+                    existingCollectionFixupLocations[(const uint8_t*)fixupLoc] = { targetVMAddr, targetFixupLevel, diversity, hasAddrDiv, key, hasPointerAuth };
+                });
+            });
+        });
+    }
+}
+
+void VTablePatcher::findBaseKernelVTables(Diagnostics& diags, const dyld3::MachOAppCache* existingKernelCollection,
+                                          std::map<std::string, DylibSymbols>& dylibsToSymbols)
+{
+    const bool is64 = pointerSize == 8;
+
+    uint8_t kernelLevel = getFixupLevel(AppCacheBuilder::Options::AppCacheKind::kernel);
+    uint64_t kernelBaseAddress = collections[kernelLevel].baseAddress;
+    const uint8_t* kernelBasePointer = collections[kernelLevel].basePointer;
+    uint16_t chainedPointerFormat = 0;
+
+    if ( existingKernelCollection->hasChainedFixupsLoadCommand() )
+        chainedPointerFormat = existingKernelCollection->chainedPointerFormat();
+
+    // Map from dylibID to list of dependencies
+    std::map<std::string, const std::vector<std::string>*> kextDependencies;
+    for (VTableDylib& dylib : dylibs) {
+        if ( dylib.cacheLevel != kernelLevel )
+            continue;
+        kextDependencies[dylib.dylibID] = &dylib.dependencies;
+    }
+
+    bool kernelUsesClassicRelocs = existingKernelCollection->usesClassicRelocationsInKernelCollection();
+    existingKernelCollection->forEachDylib(diags, ^(const dyld3::MachOAnalyzer *ma, const char *dylibID, bool &stop) {
+        uint64_t loadAddress = ma->preferredLoadAddress();
+
+        auto visitBaseKernelCollectionSymbols = ^(const char *symbolName, uint64_t n_value) {
+            if ( strstr(symbolName, superMetaclassPointerToken) == nullptr )
+                return;
+            uint8_t* fixupLoc = (uint8_t*)ma + (n_value - loadAddress);
+            logFunc("Found superclass pointer with name '%s' in '%s' at %p\n", symbolName, dylibID, fixupLoc);
+
+            // 2 - Derive the name of the class from the super MetaClass pointer.
+            std::string_view className = extractString(symbolName, osObjPrefix, superMetaclassPointerToken);
+            // If the string isn't prefixed/suffixed appropriately, then give up on this one
+            if ( className.empty() ) {
+                logFunc("Unsupported vtable superclass name\n");
+                return;
+            }
+            logFunc("Class name: '%s'\n", std::string(className).c_str());
+
+            // 3 - Derive the name of the class's vtable from the name of the class
+            // We support namespaces too which means adding an N before the class name and E after
+            std::string classVTableName = std::string(vtablePrefix) + std::string(className);
+            logFunc("Class vtable name: '%s'\n", classVTableName.c_str());
+
+            uint64_t classVTableVMAddr = 0;
+            const DylibSymbols& dylibSymbols = dylibsToSymbols[dylibID];
+            {
+                std::string namespacedVTableName;
+                SymbolLocation symbolLocation = findVTablePatchingSymbol(classVTableName, dylibSymbols);
+                if ( !symbolLocation.found() ) {
+                    // If we didn't find a name then try again with namespaces
+                    namespacedVTableName = std::string(vtablePrefix) + "N" + std::string(className) + "E";
+                    logFunc("Class namespaced vtable name: '%s'\n", namespacedVTableName.c_str());
+                    symbolLocation = findVTablePatchingSymbol(namespacedVTableName, dylibSymbols);
+                }
+                if ( symbolLocation.found() ) {
+                    classVTableVMAddr = symbolLocation.vmAddr;
+                } else {
+                    diags.error("Class vtables '%s' or '%s' is not exported from '%s'",
+                                       classVTableName.c_str(), namespacedVTableName.c_str(), dylibID);
+                    stop = true;
+                    return;
+                }
+            }
+
+            logFunc("Class vtable vmAddr: '0x%llx'\n", classVTableVMAddr);
+            const uint8_t* classVTableLoc = kernelBasePointer + (classVTableVMAddr - kernelBaseAddress);
+
+            // 4 - Follow the super MetaClass pointer to get the address of the super MetaClass's symbol
+            uint64_t superMetaclassSymbolAddress = 0;
+            auto existingKernelCollectionFixupLocIt = existingCollectionFixupLocations.find(fixupLoc);
+            if ( existingKernelCollectionFixupLocIt != existingCollectionFixupLocations.end() ) {
+                if ( ma->isKextBundle() || !kernelUsesClassicRelocs ) {
+                    auto* chainedFixupLoc = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)fixupLoc;
+                    uint64_t vmOffset = 0;
+                    bool isRebase = chainedFixupLoc->isRebase(chainedPointerFormat, kernelBaseAddress, vmOffset);
+                    assert(isRebase);
+                    superMetaclassSymbolAddress = kernelBaseAddress + vmOffset;
+                } else {
+                    // The classic reloc is already the vmAddr so nothing special to do here.
+                    assert(is64);
+                    superMetaclassSymbolAddress = *(uint64_t*)fixupLoc;
+                }
+            }
+
+            logFunc("Super MetaClass's symbol address: '0x%llx'\n", superMetaclassSymbolAddress);
+
+            if ( superMetaclassSymbolAddress == 0 ) {
+                if ( classVTableName == "__ZTV8OSObject" ) {
+                    // This is the base class of all objects, so it doesn't have a super class
+                    // We add it as a placeholder and set it to 'true' to show its already been processed
+                    VTable& vtable = vtables[classVTableLoc];
+                    vtable.ma                   = ma;
+                    vtable.dylib                = &dylibSymbols;
+                    vtable.fromParentCollection = true;
+                    vtable.patched              = true;
+                    vtable.name                 = classVTableName;
+                    return;
+                }
+            }
+
+            // 5 - Look up the super MetaClass symbol by address
+            // FIXME: VTable patching the auxKC with the superclass in the baseKC
+            uint8_t superclassFixupLevel = kernelLevel;
+
+            auto& metaclassDefinitions = collections[superclassFixupLevel].metaclassDefinitions;
+            auto metaclassIt = metaclassDefinitions.find(superMetaclassSymbolAddress);
+            if ( metaclassIt == metaclassDefinitions.end() ) {
+                diags.error("Cannot find symbol for metaclass pointed to by '%s' in '%s'",
+                            symbolName, dylibID);
+                stop = true;
+                return;
+            }
+
+            // 6 - Derive the super class's name from the super MetaClass name
+            std::string_view superClassName = extractString(metaclassIt->second, osObjPrefix, metaclassToken);
+            // If the string isn't prefixed/suffixed appropriately, then give up on this one
+            if ( superClassName.empty() ) {
+                logFunc("Unsupported vtable superclass name\n");
+                return;
+            }
+            logFunc("Superclass name: '%s'\n", std::string(superClassName).c_str());
+
+            // 7 - Derive the super class's vtable from the super class's name
+            std::string superclassVTableName = std::string(vtablePrefix) + std::string(superClassName);
+
+            // We support namespaces, so first try the superclass without the namespace, then again with it
+            const uint8_t* superclassVTableLoc = nullptr;
+            for (unsigned i = 0; i != 2; ++i) {
+                if ( i == 1 ) {
+                    superclassVTableName = std::string(vtablePrefix) + + "N" + std::string(superClassName) + "E";
+                }
+                logFunc("Superclass vtable name: '%s'\n", superclassVTableName.c_str());
+
+                if ( ma->isKextBundle() ) {
+                    // First check if the superclass vtable comes from a dependent kext
+                    auto it = kextDependencies.find(dylibID);
+                    assert(it != kextDependencies.end());
+                    const std::vector<std::string>& dependencies = *it->second;
+                    for (const std::string& dependencyID : dependencies) {
+                        auto depIt = dylibsToSymbols.find(dependencyID);
+                        if (depIt == dylibsToSymbols.end()) {
+                            diags.error("Failed to bind '%s' in '%s' as could not find a kext with '%s' bundle-id",
+                                        symbolName, dylibID, dependencyID.c_str());
+                            stop = true;
+                            return;
+                        }
+
+                        const DylibSymbols& dylibSymbols = depIt->second;
+                        SymbolLocation symbolLocation = findVTablePatchingSymbol(superclassVTableName, dylibSymbols);
+                        if ( !symbolLocation.found() )
+                            continue;
+
+                        uint64_t superclassVTableVMAddr = symbolLocation.vmAddr;
+                        logFunc("Superclass vtable vmAddr: '0x%llx'\n", superclassVTableVMAddr);
+                        superclassVTableLoc = collections[dylibSymbols.dylibLevel].basePointer + (superclassVTableVMAddr - collections[dylibSymbols.dylibLevel].baseAddress);
+                        break;
+                    }
+                }
+                if ( superclassVTableLoc == nullptr ) {
+                    auto depIt = dylibsToSymbols.find(dylibID);
+                    if (depIt == dylibsToSymbols.end()) {
+                        diags.error("Failed to bind '%s' in '%s' as could not find a binary with '%s' bundle-id",
+                                    symbolName, dylibID, dylibID);
+                        stop = true;
+                        return;
+                    }
+
+                    const DylibSymbols& dylibSymbols = depIt->second;
+                    SymbolLocation symbolLocation = findVTablePatchingSymbol(superclassVTableName, dylibSymbols);
+                    if ( symbolLocation.found() ) {
+                        uint64_t superclassVTableVMAddr = symbolLocation.vmAddr;
+                        logFunc("Superclass vtable vmAddr: '0x%llx'\n", superclassVTableVMAddr);
+                        superclassVTableLoc = collections[dylibSymbols.dylibLevel].basePointer + (superclassVTableVMAddr - collections[dylibSymbols.dylibLevel].baseAddress);
+                    }
+                }
+
+                if ( superclassVTableLoc != nullptr )
+                    break;
+            }
+
+            if ( superclassVTableLoc == nullptr ) {
+                superclassVTableName = std::string(vtablePrefix) + std::string(superClassName);
+                diags.error("Superclass vtable '%s' is not exported from '%s' or its dependencies",
+                            superclassVTableName.c_str(), dylibID);
+                stop = true;
+                return;
+            }
+
+            // Add an entry for this vtable
+            VTable& vtable = vtables[classVTableLoc];
+            vtable.superVTable = superclassVTableLoc;
+            vtable.ma                   = ma;
+            vtable.dylib                = &dylibSymbols;
+            vtable.fromParentCollection = true;
+            vtable.patched              = true;
+            vtable.name                 = classVTableName;
+
+            // And an entry for the superclass vtable
+            VTable& supervtable = vtables[superclassVTableLoc];
+            supervtable.fromParentCollection    = true;
+            supervtable.patched                 = true;
+            supervtable.name                    = superclassVTableName;
+        };
+
+        ma->forEachGlobalSymbol(diags, ^(const char *symbolName, uint64_t n_value, uint8_t n_type,
+                                         uint8_t n_sect, uint16_t n_desc, bool &stop) {
+            visitBaseKernelCollectionSymbols(symbolName, n_value);
+        });
+
+        if ( diags.hasError() ) {
+            stop = true;
+            return;
+        }
+
+        ma->forEachLocalSymbol(diags, ^(const char *symbolName, uint64_t n_value, uint8_t n_type,
+                                        uint8_t n_sect, uint16_t n_desc, bool &stop) {
+            visitBaseKernelCollectionSymbols(symbolName, n_value);
+        });
+
+        if ( diags.hasError() ) {
+            stop = true;
+            return;
+        }
+    });
+}
+
+void VTablePatcher::findPageableKernelVTables(Diagnostics& diags, const dyld3::MachOAppCache* pageableKernelCollection,
+                                              std::map<std::string, DylibSymbols>& dylibsToSymbols)
+{
+    uint8_t collectionLevel = getFixupLevel(AppCacheBuilder::Options::AppCacheKind::pageableKC);
+    uint64_t collectionBaseAddress = collections[collectionLevel].baseAddress;
+    const uint8_t* collectionBasePointer = collections[collectionLevel].basePointer;
+
+    // Map from dylibID to list of dependencies
+    std::map<std::string, const std::vector<std::string>*> kextDependencies;
+    for (VTableDylib& dylib : dylibs) {
+        if ( dylib.cacheLevel != collectionLevel )
+            continue;
+        kextDependencies[dylib.dylibID] = &dylib.dependencies;
+    }
+
+    pageableKernelCollection->forEachDylib(diags, ^(const dyld3::MachOAnalyzer *ma, const char *dylibID, bool &stop) {
+        uint64_t loadAddress = ma->preferredLoadAddress();
+        auto visitPageableKernelCollectionSymbols = ^(const char *symbolName, uint64_t n_value) {
+            if ( strstr(symbolName, superMetaclassPointerToken) == nullptr )
+                return;
+            uint8_t* fixupLoc = (uint8_t*)ma + (n_value - loadAddress);
+            logFunc("Found superclass pointer with name '%s' in '%s' at %p\n", symbolName, dylibID, fixupLoc);
+
+            // 2 - Derive the name of the class from the super MetaClass pointer.
+            std::string_view className = extractString(symbolName, osObjPrefix, superMetaclassPointerToken);
+            // If the string isn't prefixed/suffixed appropriately, then give up on this one
+            if ( className.empty() ) {
+                logFunc("Unsupported vtable superclass name\n");
+                return;
+            }
+            logFunc("Class name: '%s'\n", std::string(className).c_str());
+
+            // 3 - Derive the name of the class's vtable from the name of the class
+            // We support namespaces too which means adding an N before the class name and E after
+            std::string classVTableName = std::string(vtablePrefix) + std::string(className);
+            logFunc("Class vtable name: '%s'\n", classVTableName.c_str());
+
+            uint64_t classVTableVMAddr = 0;
+            const DylibSymbols& dylibSymbols = dylibsToSymbols[dylibID];
+            {
+                std::string namespacedVTableName;
+                SymbolLocation symbolLocation = findVTablePatchingSymbol(classVTableName, dylibSymbols);
+                if ( !symbolLocation.found() ) {
+                    // If we didn't find a name then try again with namespaces
+                    namespacedVTableName = std::string(vtablePrefix) + "N" + std::string(className) + "E";
+                    logFunc("Class namespaced vtable name: '%s'\n", namespacedVTableName.c_str());
+                    symbolLocation = findVTablePatchingSymbol(namespacedVTableName, dylibSymbols);
+                }
+                if ( symbolLocation.found() ) {
+                    classVTableVMAddr = symbolLocation.vmAddr;
+                } else {
+                    diags.error("Class vtables '%s' or '%s' is not exported from '%s'",
+                                classVTableName.c_str(), namespacedVTableName.c_str(), dylibID);
+                    stop = true;
+                    return;
+                }
+            }
+
+            logFunc("Class vtable vmAddr: '0x%llx'\n", classVTableVMAddr);
+            const uint8_t* classVTableLoc = collectionBasePointer + (classVTableVMAddr - collectionBaseAddress);
+
+            // 4 - Follow the super MetaClass pointer to get the address of the super MetaClass's symbol
+            uint8_t superclassFixupLevel = (uint8_t)~0U;
+            uint64_t superMetaclassSymbolAddress = 0;
+            auto existingKernelCollectionFixupLocIt = existingCollectionFixupLocations.find(fixupLoc);
+            if ( existingKernelCollectionFixupLocIt != existingCollectionFixupLocations.end() ) {
+                auto* chainedFixupLoc = (dyld3::MachOLoaded::ChainedFixupPointerOnDisk*)fixupLoc;
+                uint64_t vmOffset = 0;
+                bool isRebase = chainedFixupLoc->isRebase(DYLD_CHAINED_PTR_64_KERNEL_CACHE, 0, vmOffset);
+                assert(isRebase);
+                // The superclass could be in the baseKC, while we are analysing the pageableKC, so we need to get the correct level
+                // from the fixup
+                superclassFixupLevel = chainedFixupLoc->kernel64.cacheLevel;
+                superMetaclassSymbolAddress = collections[superclassFixupLevel].baseAddress + vmOffset;
+            }
+
+            logFunc("Super MetaClass's symbol address: '0x%llx'\n", superMetaclassSymbolAddress);
+
+            if ( superMetaclassSymbolAddress == 0 ) {
+                if ( classVTableName == "__ZTV8OSObject" ) {
+                    // This is the base class of all objects, so it doesn't have a super class
+                    // We add it as a placeholder and set it to 'true' to show its already been processed
+                    VTable& vtable = vtables[classVTableLoc];
+                    vtable.ma                   = ma;
+                    vtable.dylib                = &dylibSymbols;
+                    vtable.fromParentCollection = true;
+                    vtable.patched              = true;
+                    vtable.name                 = classVTableName;
+                    return;
+                }
+            }
+
+            // 5 - Look up the super MetaClass symbol by address
+            auto& metaclassDefinitions = collections[superclassFixupLevel].metaclassDefinitions;
+            auto metaclassIt = metaclassDefinitions.find(superMetaclassSymbolAddress);
+            if ( metaclassIt == metaclassDefinitions.end() ) {
+                diags.error("Cannot find symbol for metaclass pointed to by '%s' in '%s'",
+                            symbolName, dylibID);
+                stop = true;
+                return;
+            }
+
+            // 6 - Derive the super class's name from the super MetaClass name
+            std::string_view superClassName = extractString(metaclassIt->second, osObjPrefix, metaclassToken);
+            // If the string isn't prefixed/suffixed appropriately, then give up on this one
+            if ( superClassName.empty() ) {
+                logFunc("Unsupported vtable superclass name\n");
+                return;
+            }
+            logFunc("Superclass name: '%s'\n", std::string(superClassName).c_str());
+
+            // 7 - Derive the super class's vtable from the super class's name
+            std::string superclassVTableName = std::string(vtablePrefix) + std::string(superClassName);
+
+            // We support namespaces, so first try the superclass without the namespace, then again with it
+            const uint8_t* superclassVTableLoc = nullptr;
+            for (unsigned i = 0; i != 2; ++i) {
+                if ( i == 1 ) {
+                    superclassVTableName = std::string(vtablePrefix) + + "N" + std::string(superClassName) + "E";
+                }
+                logFunc("Superclass vtable name: '%s'\n", superclassVTableName.c_str());
+
+                if ( ma->isKextBundle() ) {
+                    // First check if the superclass vtable comes from a dependent kext
+                    auto it = kextDependencies.find(dylibID);
+                    assert(it != kextDependencies.end());
+                    const std::vector<std::string>& dependencies = *it->second;
+                    for (const std::string& dependencyID : dependencies) {
+                        auto depIt = dylibsToSymbols.find(dependencyID);
+                        if (depIt == dylibsToSymbols.end()) {
+                            diags.error("Failed to bind '%s' in '%s' as could not find a kext with '%s' bundle-id",
+                                        symbolName, dylibID, dependencyID.c_str());
+                            stop = true;
+                            return;
+                        }
+
+                        const DylibSymbols& dylibSymbols = depIt->second;
+                        SymbolLocation symbolLocation = findVTablePatchingSymbol(superclassVTableName, dylibSymbols);
+                        if ( !symbolLocation.found() )
+                            continue;
+
+                        uint64_t superclassVTableVMAddr = symbolLocation.vmAddr;
+                        logFunc("Superclass vtable vmAddr: '0x%llx'\n", superclassVTableVMAddr);
+                        superclassVTableLoc = collections[dylibSymbols.dylibLevel].basePointer + (superclassVTableVMAddr - collections[dylibSymbols.dylibLevel].baseAddress);
+                        break;
+                    }
+                }
+                if ( superclassVTableLoc == nullptr ) {
+                    auto depIt = dylibsToSymbols.find(dylibID);
+                    if (depIt == dylibsToSymbols.end()) {
+                        diags.error("Failed to bind '%s' in '%s' as could not find a binary with '%s' bundle-id",
+                                    symbolName, dylibID, dylibID);
+                        stop = true;
+                        return;
+                    }
+
+                    const DylibSymbols& dylibSymbols = depIt->second;
+                    SymbolLocation symbolLocation = findVTablePatchingSymbol(superclassVTableName, dylibSymbols);
+                    if ( symbolLocation.found() ) {
+                        uint64_t superclassVTableVMAddr = symbolLocation.vmAddr;
+                        logFunc("Superclass vtable vmAddr: '0x%llx'\n", superclassVTableVMAddr);
+                        superclassVTableLoc = collections[dylibSymbols.dylibLevel].basePointer + (superclassVTableVMAddr - collections[dylibSymbols.dylibLevel].baseAddress);
+                    }
+                }
+
+                if ( superclassVTableLoc != nullptr )
+                    break;
+            }
+
+            if ( superclassVTableLoc == nullptr ) {
+                superclassVTableName = std::string(vtablePrefix) + std::string(superClassName);
+                diags.error("Superclass vtable '%s' is not exported from '%s' or its dependencies",
+                            superclassVTableName.c_str(), dylibID);
+                stop = true;
+                return;
+            }
+
+            // Add an entry for this vtable
+            VTable& vtable = vtables[classVTableLoc];
+            vtable.superVTable = superclassVTableLoc;
+            vtable.ma                   = ma;
+            vtable.dylib                = &dylibSymbols;
+            vtable.fromParentCollection = true;
+            vtable.patched              = true;
+            vtable.name                 = classVTableName;
+
+            // And an entry for the superclass vtable
+            VTable& supervtable = vtables[superclassVTableLoc];
+            supervtable.fromParentCollection    = true;
+            supervtable.patched                 = true;
+            supervtable.name                    = superclassVTableName;
+        };
+
+        ma->forEachGlobalSymbol(diags, ^(const char *symbolName, uint64_t n_value, uint8_t n_type,
+                                         uint8_t n_sect, uint16_t n_desc, bool &stop) {
+            visitPageableKernelCollectionSymbols(symbolName, n_value);
+        });
+
+        if ( diags.hasError() ) {
+            stop = true;
+            return;
+        }
+
+        ma->forEachLocalSymbol(diags, ^(const char *symbolName, uint64_t n_value, uint8_t n_type,
+                                        uint8_t n_sect, uint16_t n_desc, bool &stop) {
+            visitPageableKernelCollectionSymbols(symbolName, n_value);
+        });
+
+        if ( diags.hasError() ) {
+            stop = true;
+            return;
+        }
+    });
+}
+
+void VTablePatcher::findVTables(uint8_t currentLevel, const dyld3::MachOAnalyzer* kernelMA,
+                                std::map<std::string, DylibSymbols>& dylibsToSymbols,
+                                const AppCacheBuilder::ASLR_Tracker& aslrTracker,
+                                const std::map<const uint8_t*, const VTableBindSymbol>& missingBindLocations)
+{
+    const bool is64 = pointerSize == 8;
+
+    uint64_t collectionBaseAddress = collections[currentLevel].baseAddress;
+    const uint8_t* collectionBasePointer = collections[currentLevel].basePointer;
+
+    // VTable patching algorithm (for each symbol...):
+    // - To find the address of a class vtable:
+    //  - Take symbols with '10superClassE' in their name, eg, __ZN10IOMachPort10superClassE
+    //  - Work out the name of the class from that symbol name, eg, 10IOMachPort
+    //  - Work out the name of the VTable from that class name, eg, __ZTV10IOMachPort
+    //  - Find the address for the export with that vtable name
+    // - To find the superclass for a given class
+    //  - Take the symbol with '10superClassE' in their name, eg, __ZN10IOMachPort10superClassE
+    //  - Take its address and dereference it as "__ZN10IOMachPort10superClassE = &__ZN8OSObject10gMetaClassE"
+    //  - Find the name of the symbol at this address, eg, work out we have a symbol called __ZN8OSObject10gMetaClassE
+    //  - Get the superclassic from that symbol name, eg, 8OSObject
+    //  - Get the VTable name from that symbol, eg, __ZTV8OSObject
+    //  - Find the superclass vtable address from that name by searching the image and dependents for __ZTV8OSObject
+    for (VTableDylib& dylib : dylibs) {
+        // Only process dylibs in the level we are building
+        // Existing collections were handled elsewhere
+        if ( dylib.cacheLevel != currentLevel )
+            continue;
+
+        const dyld3::MachOAnalyzer* ma                  = dylib.ma;
+        const std::string&          dylibID             = dylib.dylibID;
+        Diagnostics&                dylibDiags          = *dylib.diags;
+        const std::vector<std::string>& dependencies    = dylib.dependencies;
+
+        uint64_t loadAddress = ma->preferredLoadAddress();
+        bool alreadyPatched = (ma == kernelMA);
+        auto visitSymbols = ^(const char *symbolName, uint64_t n_value) {
+            if ( strstr(symbolName, superMetaclassPointerToken) == nullptr )
+                return;
+
+            uint8_t* fixupLoc = (uint8_t*)ma + (n_value - loadAddress);
+            logFunc("Found superclass pointer with name '%s' in '%s' at %p\n", symbolName, dylibID.c_str(), fixupLoc);
+
+            // 2 - Derive the name of the class from the super MetaClass pointer.
+            std::string_view className = extractString(symbolName, osObjPrefix, superMetaclassPointerToken);
+            // If the string isn't prefixed/suffixed appropriately, then give up on this one
+            if ( className.empty() ) {
+                logFunc("Unsupported vtable superclass name\n");
+                return;
+            }
+            logFunc("Class name: '%s'\n", std::string(className).c_str());
+
+            // 3 - Derive the name of the class's vtable from the name of the class
+            // We support namespaces too which means adding an N before the class name and E after
+            std::string classVTableName = std::string(vtablePrefix) + std::string(className);
+            logFunc("Class vtable name: '%s'\n", classVTableName.c_str());
+
+            uint64_t classVTableVMAddr = 0;
+            const DylibSymbols& dylibSymbols = dylibsToSymbols[dylibID];
+            {
+                std::string namespacedVTableName;
+                SymbolLocation symbolLocation = findVTablePatchingSymbol(classVTableName, dylibSymbols);
+                if ( !symbolLocation.found() ) {
+                    // If we didn't find a name then try again with namespaces
+                    namespacedVTableName = std::string(vtablePrefix) + "N" + std::string(className) + "E";
+                    logFunc("Class namespaced vtable name: '%s'\n", namespacedVTableName.c_str());
+                    symbolLocation = findVTablePatchingSymbol(namespacedVTableName, dylibSymbols);
+                }
+                if ( symbolLocation.found() ) {
+                    classVTableVMAddr = symbolLocation.vmAddr;
+                } else {
+                    dylibDiags.error("Class vtables '%s' or '%s' is not an exported symbol",
+                                     classVTableName.c_str(), namespacedVTableName.c_str());
+                    return;
+                }
+            }
+
+            logFunc("Class vtable vmAddr: '0x%llx'\n", classVTableVMAddr);
+            const uint8_t* classVTableLoc = (uint8_t*)ma + (classVTableVMAddr - loadAddress);
+
+            // 4 - Follow the super MetaClass pointer to get the address of the super MetaClass's symbol
+            uint64_t superMetaclassSymbolAddress = 0;
+            {
+                uint32_t vmAddr32 = 0;
+                uint64_t vmAddr64 = 0;
+                if ( aslrTracker.hasRebaseTarget32(fixupLoc, &vmAddr32) ) {
+                    superMetaclassSymbolAddress = vmAddr32;
+                } else if ( aslrTracker.hasRebaseTarget64(fixupLoc, &vmAddr64) ) {
+                    superMetaclassSymbolAddress = vmAddr64;
+                } else {
+                    assert(is64);
+                    superMetaclassSymbolAddress = *(uint64_t*)fixupLoc;
+                }
+                uint8_t highByte = 0;
+                if ( aslrTracker.hasHigh8(fixupLoc, &highByte) ) {
+                    uint64_t tbi = (uint64_t)highByte << 56;
+                    superMetaclassSymbolAddress |= tbi;
+                }
+            }
+            logFunc("Super MetaClass's symbol address: '0x%llx'\n", superMetaclassSymbolAddress);
+
+            if ( superMetaclassSymbolAddress == 0 ) {
+                if ( classVTableName == "__ZTV8OSObject" ) {
+                    // This is the base class of all objects, so it doesn't have a super class
+                    // We add it as a placeholder and set it to 'true' to show its already been processed
+                    VTable& vtable = vtables[classVTableLoc];
+                    vtable.ma                   = ma;
+                    vtable.dylib                = &dylibSymbols;
+                    vtable.fromParentCollection = false;
+                    vtable.patched              = true;
+                    vtable.name                 = classVTableName;
+                    return;
+                }
+            }
+
+            // 5 - Look up the super MetaClass symbol by address
+            // FIXME: VTable patching the auxKC with the superclass in the baseKC
+            uint8_t superclassFixupLevel = currentLevel;
+            aslrTracker.has(fixupLoc, &superclassFixupLevel);
+
+            auto& metaclassDefinitions = collections[superclassFixupLevel].metaclassDefinitions;
+            auto metaclassIt = metaclassDefinitions.find(superMetaclassSymbolAddress);
+            if ( metaclassIt == metaclassDefinitions.end() ) {
+                auto bindIt = missingBindLocations.find(fixupLoc);
+                if ( bindIt != missingBindLocations.end() ) {
+                    dylibDiags.error("Cannot find symbol for metaclass pointed to by '%s'.  "
+                                     "Expected symbol '%s' to be defined in another kext",
+                                     symbolName, bindIt->second.symbolName.c_str());
+                } else {
+                    dylibDiags.error("Cannot find symbol for metaclass pointed to by '%s'",
+                                     symbolName);
+                }
+                return;
+            }
+
+            // 6 - Derive the super class's name from the super MetaClass name
+            std::string_view superClassName = extractString(metaclassIt->second, osObjPrefix, metaclassToken);
+            // If the string isn't prefixed/suffixed appropriately, then give up on this one
+            if ( superClassName.empty() ) {
+                logFunc("Unsupported vtable superclass name\n");
+                return;
+            }
+            logFunc("Superclass name: '%s'\n", std::string(superClassName).c_str());
+
+            // 7 - Derive the super class's vtable from the super class's name
+            std::string superclassVTableName = std::string(vtablePrefix) + std::string(superClassName);
+
+            // We support namespaces, so first try the superclass without the namespace, then again with it
+            const uint8_t* superclassVTableLoc = nullptr;
+            bool superVTableIsInParentCollection = false;
+            for (unsigned i = 0; i != 2; ++i) {
+                if ( i == 1 ) {
+                    superclassVTableName = std::string(vtablePrefix) + + "N" + std::string(superClassName) + "E";
+                }
+                logFunc("Superclass vtable name: '%s'\n", superclassVTableName.c_str());
+
+                {
+                    // First check if the superclass vtable comes from a dependent kext
+                    for (const std::string& dependencyID : dependencies) {
+                        auto depIt = dylibsToSymbols.find(dependencyID);
+                        if (depIt == dylibsToSymbols.end()) {
+                            dylibDiags.error("Failed to bind '%s' as could not find a kext with '%s' bundle-id",
+                                             symbolName, dependencyID.c_str());
+                            return;
+                        }
+
+                        const DylibSymbols& dylibSymbols = depIt->second;
+                        SymbolLocation symbolLocation = findVTablePatchingSymbol(superclassVTableName, dylibSymbols);
+                        if ( !symbolLocation.found() )
+                            continue;
+
+                        uint64_t superclassVTableVMAddr = symbolLocation.vmAddr;
+                        logFunc("Superclass vtable vmAddr: '0x%llx'\n", superclassVTableVMAddr);
+                        superclassVTableLoc = collections[dylibSymbols.dylibLevel].basePointer + (superclassVTableVMAddr - collections[dylibSymbols.dylibLevel].baseAddress);
+                        superVTableIsInParentCollection = dylibSymbols.dylibLevel != currentLevel;
+                        break;
+                    }
+
+                    if ( superclassVTableLoc == nullptr ) {
+                        SymbolLocation symbolLocation = findVTablePatchingSymbol(superclassVTableName, dylibSymbols);
+                        if ( symbolLocation.found() ) {
+                            uint64_t superclassVTableVMAddr = symbolLocation.vmAddr;
+                            superclassVTableLoc = (uint8_t*)collectionBasePointer + (superclassVTableVMAddr - collectionBaseAddress);
+                            superVTableIsInParentCollection = false;
+                        }
+                    }
+                }
+
+                if ( superclassVTableLoc != nullptr )
+                    break;
+            }
+
+            if ( superclassVTableLoc == nullptr ) {
+                superclassVTableName = std::string(vtablePrefix) + std::string(superClassName);
+                dylibDiags.error("Superclass vtable '%s' is not exported from kext or its dependencies",
+                                 superclassVTableName.c_str());
+                return;
+            }
+
+            // Add an entry for this vtable
+            VTable& vtable = vtables[classVTableLoc];
+            vtable.superVTable = superclassVTableLoc;
+            vtable.ma                   = ma;
+            vtable.dylib                = &dylibSymbols;
+            vtable.fromParentCollection = false;
+            vtable.patched              |= alreadyPatched;
+            vtable.name                 = classVTableName;
+
+            // And an entry for the superclass vtable
+            VTable& supervtable = vtables[superclassVTableLoc];
+            supervtable.fromParentCollection    = superVTableIsInParentCollection;
+            supervtable.patched                 |= alreadyPatched;
+            supervtable.name                    = superclassVTableName;
+
+            // Also calculate the metaclass vtable name so that we can patch it
+            std::string metaclassVTableName = std::string(metaclassVTablePrefix) + std::string(className) + metaclassVTableSuffix;
+            logFunc("Metaclass vtable name: '%s'\n", metaclassVTableName.c_str());
+
+            {
+                // Note its safe to just ignore missing metaclass symbols if we can't find them
+                // If the binary links then kxld would have let it run
+                SymbolLocation symbolLocation = findVTablePatchingSymbol(metaclassVTableName, dylibSymbols);
+                if ( symbolLocation.found() ) {
+                    uint64_t metaclassVTableVMAddr = symbolLocation.vmAddr;
+
+                    logFunc("Metaclass vtable vmAddr: '0x%llx'\n", metaclassVTableVMAddr);
+                    uint8_t* metaclassVTableLoc = (uint8_t*)ma + (metaclassVTableVMAddr - loadAddress);
+
+                    // Add an entry for this vtable
+                    VTable& vtable = vtables[metaclassVTableLoc];
+                    vtable.superVTable          = baseMetaClassVTableLoc;
+                    vtable.ma                   = ma;
+                    vtable.dylib                = &dylibSymbols;
+                    vtable.fromParentCollection = false;
+                    vtable.patched              |= alreadyPatched;
+                    vtable.name                 = metaclassVTableName;
+                }
+            }
+        };
+
+        ma->forEachGlobalSymbol(dylibDiags, ^(const char *symbolName, uint64_t n_value, uint8_t n_type,
+                                             uint8_t n_sect, uint16_t n_desc, bool &stop) {
+            visitSymbols(symbolName, n_value);
+        });
+
+        ma->forEachLocalSymbol(dylibDiags, ^(const char *symbolName, uint64_t n_value, uint8_t n_type,
+                                            uint8_t n_sect, uint16_t n_desc, bool &stop) {
+            visitSymbols(symbolName, n_value);
+        });
+    }
+}
+
+void VTablePatcher::calculateSymbols() {
+    for (VTableDylib& dylib : dylibs) {
+        auto& symbolNames = collections[dylib.cacheLevel].symbolNames;
+        dylib.ma->forEachGlobalSymbol(*dylib.diags, ^(const char *symbolName, uint64_t n_value, uint8_t n_type,
+                                                      uint8_t n_sect, uint16_t n_desc, bool &stop) {
+            symbolNames[n_value] = symbolName;
+        });
+        dylib.ma->forEachLocalSymbol(*dylib.diags, ^(const char *symbolName, uint64_t n_value, uint8_t n_type,
+                                                     uint8_t n_sect, uint16_t n_desc, bool &stop) {
+            symbolNames[n_value] = symbolName;
+        });
+    }
+}
+
+void VTablePatcher::patchVTables(Diagnostics& diags,
+                                 std::map<const uint8_t*, const VTableBindSymbol>& missingBindLocations,
+                                 AppCacheBuilder::ASLR_Tracker& aslrTracker,
+                                 uint8_t currentLevel)
+{
+    const bool is64 = pointerSize == 8;
+
+    // If we have vtables to patch, then make sure we found the OSMetaClass symbol to patch against
+    if ( (baseMetaClassVTableLoc == nullptr) && !vtables.empty() ) {
+        diags.error("Could not find OSMetaClass vtable in kernel binary");
+        return;
+    }
+
+    calculateSymbols();
+
+    auto calculateVTableEntries = ^(const uint8_t* vtableLoc, VTable& vtable) {
+        assert(vtable.patched);
+        logFunc("Calculating vtable: '%s'\n", vtable.name.c_str());
+
+        // The first entry we want to patch is 2 pointers from the start of the vtable
+        const uint8_t* relocLoc = vtableLoc + (2 * pointerSize);
+
+        if ( vtable.fromParentCollection ) {
+            auto it = existingCollectionFixupLocations.find(relocLoc);
+            while ( it != existingCollectionFixupLocations.end() ) {
+                const Fixup& fixup = it->second;
+                uint64_t targetVMAddr   = fixup.targetVMAddr;
+                uint16_t diversity      = fixup.diversity;
+                bool     hasAddrDiv     = fixup.hasAddrDiv;
+                uint8_t  key            = fixup.key;
+                bool     hasPointerAuth = fixup.hasPointerAuth;
+                uint32_t  cacheLevel    = fixup.cacheLevel;
+                vtable.entries.push_back({ relocLoc, targetVMAddr, cacheLevel, diversity, hasAddrDiv, key, hasPointerAuth });
+                relocLoc += pointerSize;
+                it = existingCollectionFixupLocations.find(relocLoc);
+            }
+        } else {
+            while ( aslrTracker.has((void*)relocLoc) ||
+                   (missingBindLocations.find(relocLoc) != missingBindLocations.end()) ) {
+
+                uint16_t diversity      = 0;
+                bool     hasAddrDiv     = false;
+                uint8_t  key            = 0;
+                bool     hasPointerAuth = false;
+                uint8_t cacheLevel     = currentLevel;
+
+                if ( aslrTracker.has((void*)relocLoc, &cacheLevel) ) {
+                    hasPointerAuth = aslrTracker.hasAuthData((void*)relocLoc, &diversity, &hasAddrDiv, &key);
+                }
+
+                uint64_t targetVMAddr = 0;
+                {
+                    uint32_t vmAddr32 = 0;
+                    uint64_t vmAddr64 = 0;
+                    if ( aslrTracker.hasRebaseTarget32((void*)relocLoc, &vmAddr32) ) {
+                        targetVMAddr = vmAddr32;
+                    } else if ( aslrTracker.hasRebaseTarget64((void*)relocLoc, &vmAddr64) ) {
+                        targetVMAddr = vmAddr64;
+                    } else {
+                        assert(is64);
+                        targetVMAddr = *(uint64_t*)relocLoc;
+                    }
+                    uint8_t highByte = 0;
+                    if ( aslrTracker.hasHigh8((void*)relocLoc, &highByte) ) {
+                        uint64_t tbi = (uint64_t)highByte << 56;
+                        targetVMAddr |= tbi;
+                    }
+                }
+
+                vtable.entries.push_back({ relocLoc, targetVMAddr, cacheLevel, diversity, hasAddrDiv, key, hasPointerAuth });
+                relocLoc += pointerSize;
+            }
+        }
+
+        logFunc("Found %d vtable items: '%s'\n", vtable.entries.size(), vtable.name.c_str());
+    };
+
+    // Map from MachO to diagnostics to emit for that file
+    std::unordered_map<const dyld3::MachOAnalyzer*, Diagnostics*> diagsMap;
+    for (VTableDylib& dylib : dylibs)
+        diagsMap[dylib.ma] = dylib.diags;
+
+    uint32_t numPatchedVTables = 0;
+    for (auto& vtableEntry : vtables) {
+        if ( vtableEntry.second.patched ) {
+            calculateVTableEntries(vtableEntry.first, vtableEntry.second);
+            ++numPatchedVTables;
+        }
+    }
+    while ( numPatchedVTables != vtables.size() ) {
+        typedef std::pair<const uint8_t*, VTable*> VTableEntry;
+        std::vector<VTableEntry> toBePatched;
+        for (auto& vtableEntry : vtables) {
+            if ( vtableEntry.second.patched )
+                continue;
+            auto superIt = vtables.find(vtableEntry.second.superVTable);
+            assert(superIt != vtables.end());
+            if ( !superIt->second.patched )
+                continue;
+            logFunc("Found unpatched vtable: '%s' with patched superclass '%s'\n",
+                    vtableEntry.second.name.c_str(), superIt->second.name.c_str());
+            toBePatched.push_back({ vtableEntry.first, &vtableEntry.second });
+        }
+
+        if ( toBePatched.empty() ) {
+            // If we can't find anything to patch, then print out what we have left
+            for (const auto& vtableEntry : vtables) {
+                if ( vtableEntry.second.patched )
+                    continue;
+                auto superIt = vtables.find(vtableEntry.second.superVTable);
+                assert(superIt != vtables.end());
+                diags.error("Found unpatched vtable: '%s' with unpatched superclass '%s'\n",
+                            vtableEntry.second.name.c_str(), superIt->second.name.c_str());
+            }
+            break;
+        }
+
+        for (VTableEntry& vtableEntry : toBePatched) {
+            VTable& vtable = *vtableEntry.second;
+
+            // We can immediately mark this as patched as then calculateVTableEntries can make
+            // sure we never ask for vtables which aren't ready yet
+            vtable.patched = true;
+            ++numPatchedVTables;
+
+            auto superIt = vtables.find(vtable.superVTable);
+            logFunc("Processing unpatched vtable: '%s' with patched superclass '%s'\n",
+                    vtable.name.c_str(), superIt->second.name.c_str());
+
+            calculateVTableEntries(vtableEntry.first, vtable);
+
+            const VTable& supervtable = superIt->second;
+            if ( vtable.entries.size() < supervtable.entries.size() ) {
+                // Try emit the error to a per dylib diagnostic object if we can find one
+                auto diagIt = diagsMap.find(vtable.ma);
+                Diagnostics* diag = (diagIt != diagsMap.end()) ? diagIt->second : &diags;
+                diag->error("Malformed vtable.  Super class '%s' has %lu entries vs subclass '%s' with %lu entries",
+                            supervtable.name.c_str(), supervtable.entries.size(),
+                            vtable.name.c_str(), vtable.entries.size());
+                return;
+            }
+
+            const std::unordered_map<const uint8_t*, VTableBindSymbol>& resolvedBindLocations = vtable.dylib->resolvedBindLocations;
+            for (uint64_t entryIndex = 0; entryIndex != supervtable.entries.size(); ++entryIndex) {
+                logFuncVerbose("Processing entry %lld: super[0x%llx] vs subclass[0x%llx]\n", entryIndex,
+                               *(uint64_t*)supervtable.entries[entryIndex].location,
+                               *(uint64_t*)vtable.entries[entryIndex].location);
+
+                VTable::Entry& vtableEntry = vtable.entries[entryIndex];
+                const VTable::Entry& superVTableEntry = supervtable.entries[entryIndex];
+
+                const uint8_t* patchLoc = vtableEntry.location;
+                uint64_t targetVMAddr = superVTableEntry.targetVMAddr;
+
+                // 1) If the symbol is defined locally, do not patch
+                // This corresponds to a rebase not a bind, so if we have a match in our bind set
+                // we were bound to another image, and should see if that bind should be overridden by a
+                // better vtable patch.
+                auto resolvedBindIt = resolvedBindLocations.find(patchLoc);
+                auto unresolvedBindIt = missingBindLocations.find(patchLoc);
+                if ( (resolvedBindIt == resolvedBindLocations.end()) && (unresolvedBindIt == missingBindLocations.end()) )
+                    continue;
+
+                // Find the child and parent symbols, if any
+                const char* childSymbolName = nullptr;
+                const char* parentSymbolName = nullptr;
+
+                if ( resolvedBindIt != resolvedBindLocations.end() ) {
+                    childSymbolName = resolvedBindIt->second.symbolName.c_str();
+                } else {
+                    assert(unresolvedBindIt != missingBindLocations.end());
+                    childSymbolName = unresolvedBindIt->second.symbolName.c_str();
+                }
+
+                auto& symbolNames = collections[superVTableEntry.targetCacheLevel].symbolNames;
+                auto parentNameIt = symbolNames.find(superVTableEntry.targetVMAddr);
+                if ( parentNameIt != symbolNames.end() )
+                    parentSymbolName = parentNameIt->second;
+
+                // The child entry can be NULL when a locally-defined, non-external
+                // symbol is stripped.  We wouldn't patch this entry anyway, so we just skip it.
+                if ( childSymbolName == nullptr ) {
+                    continue;
+                }
+
+                // It's possible for the patched parent entry not to have a symbol
+                // (e.g. when the definition is inlined).  We can't patch this entry no
+                // matter what, so we'll just skip it and die later if it's a problem
+                // (which is not likely).
+                if ( parentSymbolName == nullptr ) {
+                    continue;
+                }
+
+                logFuncVerbose("Processing entry %lld: super[%s] vs subclass[%s]\n", entryIndex,
+                               parentSymbolName, childSymbolName);
+
+                // 2) If the child is a pure virtual function, do not patch.
+                // In general, we want to proceed with patching when the symbol is
+                // externally defined because pad slots fall into this category.
+                // The pure virtual function symbol is special case, as the pure
+                // virtual property itself overrides the parent's implementation.
+                if ( !strcmp(childSymbolName, "___cxa_pure_virtual") ) {
+                    continue;
+                }
+
+                // 3) If the symbols are the same, do not patch
+                // Note that if the symbol was a missing bind, then we'll still patch
+                // This is the case where the vtable entry itself was a local symbol
+                // so we had originally failed to bind to it as it wasn't exported, but it
+                // has the same name as the parent name
+                if ( !strcmp(childSymbolName, parentSymbolName) && (unresolvedBindIt == missingBindLocations.end()) ) {
+                    continue;
+                }
+
+#if 0
+                // FIXME: Implement this
+
+                // 4) If the parent vtable entry is a pad slot, and the child does not
+                // match it, then the child was built against a newer version of the
+                // libraries, so it is binary-incompatible.
+                require_action(!kxld_sym_name_is_padslot(parent_entry->patched.name),
+                    finish, rval = KERN_FAILURE;
+                    kxld_log(kKxldLogPatching, kKxldLogErr,
+                    kKxldLogParentOutOfDate,
+                    kxld_demangle(super_vtable->name, &demangled_name1,
+                    &demangled_length1),
+                    kxld_demangle(vtable->name, &demangled_name2,
+                    &demangled_length2)));
+#endif
+
+                logFunc("Patching entry '%s' in '%s' to point to '%s' in superclass '%s'\n",
+                        childSymbolName, vtable.name.c_str(), parentSymbolName, supervtable.name.c_str());
+
+                if ( is64 ) {
+                    *((uint64_t*)patchLoc) = targetVMAddr;
+                } else {
+                    *((uint32_t*)patchLoc) = (uint32_t)targetVMAddr;
+                }
+
+                // FIXME: When we support a baseKC, pageableKC, and auxKC, the supervtable cache level
+                // may no longer be correct here as we may be:
+                // - patching a vtable in auxKC
+                // - where the supervtable is in pageableKC
+                // - but the entry slot points to baseKC
+                aslrTracker.add((void*)patchLoc, superVTableEntry.targetCacheLevel);
+
+                // Add pointer auth if the super vtable had it
+                if ( superVTableEntry.hasPointerAuth )
+                    aslrTracker.setAuthData((void*)patchLoc, superVTableEntry.diversity,
+                                            superVTableEntry.hasAddrDiv, superVTableEntry.key);
+
+                // Update this vtable entry in case there are any subclasses which then need to use it
+                // to be patched themselves
+                vtableEntry.targetVMAddr        = superVTableEntry.targetVMAddr;
+                vtableEntry.targetCacheLevel    = superVTableEntry.targetCacheLevel;
+                vtableEntry.diversity           = superVTableEntry.diversity;
+                vtableEntry.hasAddrDiv          = superVTableEntry.hasAddrDiv;
+                vtableEntry.key                 = superVTableEntry.key;
+                vtableEntry.hasPointerAuth      = superVTableEntry.hasPointerAuth;
+
+                missingBindLocations.erase(patchLoc);
+            }
+        }
+    }
+}
+
+typedef std::pair<uint8_t, uint64_t> CacheOffset;
+
+struct DylibSymbolLocation {
+    const DylibSymbols* dylibSymbols;
+    uint64_t            symbolVMAddr;
+    bool                isKPI;
+};
+
+struct DylibFixups {
+    void processFixups(const std::map<std::string, DylibSymbols>& dylibsToSymbols,
+                       const std::unordered_map<std::string_view, std::vector<DylibSymbolLocation>>& symbolMap,
+                       const std::string& kernelID, const CacheBuilder::ASLR_Tracker& aslrTracker);
+
+    // Inputs
+    const dyld3::MachOAnalyzer*     ma              = nullptr;
+    DylibSymbols&                   dylibSymbols;
+    Diagnostics&                    dylibDiag;
+    const std::vector<std::string>& dependencies;
+
+    // Outputs
+    struct AuthData {
+        uint16_t    diversity;
+        bool        addrDiv;
+        uint8_t     key;
+    };
+    struct BranchStubData {
+        CacheOffset targetCacheOffset;
+        const void* fixupLoc;
+        uint64_t    fixupVMOffset;
+    };
+    std::unordered_map<const uint8_t*, VTableBindSymbol>        missingBindLocations;
+    std::unordered_map<void*, uint8_t>                          fixupLocs;
+    std::unordered_map<void*, uint8_t>                          fixupHigh8s;
+    std::unordered_map<void*, AuthData>                         fixupAuths;
+    std::vector<BranchStubData>                                 branchStubs;
+};
+
+void DylibFixups::processFixups(const std::map<std::string, DylibSymbols>& dylibsToSymbols,
+                                const std::unordered_map<std::string_view, std::vector<DylibSymbolLocation>>& symbolMap,
+                                const std::string& kernelID, const CacheBuilder::ASLR_Tracker& aslrTracker) {
+    auto& resolvedBindLocations = dylibSymbols.resolvedBindLocations;
+    const std::string& dylibID = dylibSymbols.dylibName;
+
+    const bool _is64 = true;
+    const bool isThirdPartyKext = (dylibID.find("com.apple") != 0);
+
+    // The magic symbol for missing weak imports
+    const char* missingWeakImportSymbolName = "_gOSKextUnresolved";
+
+    struct SymbolDefinition {
+        uint64_t    symbolVMAddr;
+        uint32_t    kernelCollectionLevel;
+    };
+    auto findDependencyWithSymbol = [&symbolMap, &isThirdPartyKext](const char* symbolName,
+                                                 const std::vector<std::string>& dependencies) {
+        auto symbolMapIt = symbolMap.find(symbolName);
+        if ( symbolMapIt == symbolMap.end() )
+            return (SymbolDefinition){ ~0ULL, 0 };
+        // Find the first dependency in the list
+        const std::vector<DylibSymbolLocation>& dylibSymbols = symbolMapIt->second;
+        // The massively common case is 1 or 2 definitions of a given symbol, so a basic searhc should be
+        // fine
+        for (const std::string& dependency : dependencies) {
+            for (const DylibSymbolLocation& dylibSymbol : dylibSymbols) {
+                if ( dependency == dylibSymbol.dylibSymbols->dylibName ) {
+                    // If the Apple kext we are linking has a symbol set, and the user is a third-party kext,
+                    // then only allow the  third party kext to see symbols in the kext export list, if it has one
+                    const bool isAppleKext = (dependency.find("com.apple") == 0);
+                    if ( isThirdPartyKext && isAppleKext && !dylibSymbol.isKPI )
+                        continue;
+                    return (SymbolDefinition){ dylibSymbol.symbolVMAddr, dylibSymbol.dylibSymbols->dylibLevel };
+                }
+            }
+        }
+        return (SymbolDefinition){ ~0ULL, 0 };
+    };
+
+    if (ma->hasChainedFixups()) {
+        // build array of targets
+        struct BindTarget {
+            const VTableBindSymbol              bindSymbol;
+            uint64_t                            vmAddr;
+            uint32_t                            dylibLevel;
+            bool                                isMissingWeakImport;
+            bool                                isMissingSymbol;
+        };
+        __block std::vector<BindTarget> bindTargets;
+        __block bool foundMissingWeakImport = false;
+        ma->forEachChainedFixupTarget(dylibDiag, ^(int libOrdinal, const char* symbolName, uint64_t addend,
+                                                   bool weakImport, bool& stop) {
+            if ( (libOrdinal != BIND_SPECIAL_DYLIB_FLAT_LOOKUP) && (libOrdinal != BIND_SPECIAL_DYLIB_WEAK_LOOKUP) ) {
+                dylibDiag.error("All chained binds should be flat namespace or weak lookups");
+                stop = true;
+                return;
+            }
+
+            if ( addend != 0 ) {
+                dylibDiag.error("Chained bind addends are not supported right now");
+                stop = true;
+                return;
+            }
+
+            VTableBindSymbol bindSymbol = { dylibID, symbolName };
+            bool isMissingSymbol = false;
+
+            for (const std::string& dependencyID : dependencies) {
+                auto depIt = dylibsToSymbols.find(dependencyID);
+                if (depIt == dylibsToSymbols.end()) {
+                    dylibDiag.error("Failed to bind '%s' as could not find a kext with '%s' bundle-id",
+                                       symbolName, dependencyID.c_str());
+                    stop = true;
+                    return;
+                }
+
+                const DylibSymbols& dylibSymbols = depIt->second;
+                auto exportIt = dylibSymbols.globals.find(symbolName);
+                if ( exportIt == dylibSymbols.globals.end() )
+                    continue;
+
+                isMissingSymbol = false;
+                bindTargets.push_back({ bindSymbol, exportIt->second, dylibSymbols.dylibLevel, false, isMissingSymbol });
+                return;
+            }
+
+            // If the symbol is weak, and we didn't find it in our listed
+            // dependencies, then use our own definition
+            if ( libOrdinal == BIND_SPECIAL_DYLIB_WEAK_LOOKUP ) {
+                auto dylibIt = dylibsToSymbols.find(dylibID);
+                if (dylibIt == dylibsToSymbols.end()) {
+                    dylibDiag.error("Failed to bind weak '%s' as could not find a define in self",
+                                    symbolName);
+                    stop = true;
+                    return;
+                }
+                const DylibSymbols& dylibSymbols = dylibIt->second;
+                auto exportIt = dylibSymbols.globals.find(symbolName);
+                if ( exportIt != dylibSymbols.globals.end() ) {
+                    isMissingSymbol = false;
+                    bindTargets.push_back({ bindSymbol, exportIt->second, dylibSymbols.dylibLevel, false, isMissingSymbol });
+                    return;
+                }
+            }
+
+            if ( weakImport ) {
+                // Find _gOSKextUnresolved in the kernel
+                // Weak imports are not compared against null, but instead against the address of that symbol
+                auto kernelSymbolsIt = dylibsToSymbols.find(kernelID);
+                assert(kernelSymbolsIt != dylibsToSymbols.end());
+                const DylibSymbols& kernelSymbols = kernelSymbolsIt->second;
+                auto exportIt = kernelSymbols.globals.find(missingWeakImportSymbolName);
+                if (exportIt != kernelSymbols.globals.end()) {
+                    foundMissingWeakImport = true;
+                    isMissingSymbol = false;
+                    bindTargets.push_back({ bindSymbol, exportIt->second, kernelSymbols.dylibLevel, true, isMissingSymbol });
+                    return;
+                }
+                dylibDiag.error("Weak bind symbol '%s' not found in kernel", missingWeakImportSymbolName);
+                return;
+            }
+
+            // Store missing binds for later.  They may be fixed by vtable patching
+            isMissingSymbol = true;
+            bindTargets.push_back({ bindSymbol, 0, 0, false, isMissingSymbol });
+        });
+        if ( dylibDiag.hasError() )
+            return;
+
+        if( foundMissingWeakImport ) {
+            // If we found a missing weak import, then we need to check that the user did
+            // something like "if ( &foo == &gOSKextUnresolved )"
+            // If they didn't use gOSKextUnresolved at all, then there's no way they could be doing that check
+            auto kernelSymbolsIt = dylibsToSymbols.find(kernelID);
+            assert(kernelSymbolsIt != dylibsToSymbols.end());
+            const DylibSymbols& kernelSymbols = kernelSymbolsIt->second;
+            auto exportIt = kernelSymbols.globals.find(missingWeakImportSymbolName);
+            assert(exportIt != kernelSymbols.globals.end());
+            bool foundUseOfMagicSymbol = false;
+            for (const BindTarget& bindTarget : bindTargets) {
+                // Skip the missing weak imports
+                if ( bindTarget.isMissingWeakImport || bindTarget.isMissingSymbol )
+                    continue;
+                // Skip anything which isn't the symbol we are looking for
+                if ( (bindTarget.dylibLevel != 0) && (bindTarget.vmAddr != exportIt->second) )
+                    continue;
+                foundUseOfMagicSymbol = true;
+                break;
+            }
+
+            if ( !foundUseOfMagicSymbol ) {
+                dylibDiag.error("Has weak references but does not test for them.  "
+                                "Test for weak references with OSKextSymbolIsResolved().");
+                return;
+            }
+        }
+
+        // uint64_t baseAddress = ma->preferredLoadAddress();
+
+        ma->withChainStarts(dylibDiag, 0, ^(const dyld_chained_starts_in_image* starts) {
+            ma->forEachFixupInAllChains(dylibDiag, starts, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
+                switch (segInfo->pointer_format) {
+                    case DYLD_CHAINED_PTR_64_OFFSET:
+                        if ( fixupLoc->generic64.bind.bind ) {
+                            uint64_t bindOrdinal = fixupLoc->generic64.bind.ordinal;
+                            if ( bindOrdinal >= bindTargets.size() ) {
+                                dylibDiag.error("Bind ordinal %lld out of range %lu", bindOrdinal, bindTargets.size());
+                                stop = true;
+                                return;
+                            }
+
+                            const BindTarget& bindTarget = bindTargets[bindOrdinal];
+                            if ( bindTarget.isMissingSymbol ) {
+                                // Track this missing bind for later
+                                // For now we bind it to null and don't slide it.
+                                fixupLoc->raw64 = 0;
+                                missingBindLocations[(const uint8_t*)fixupLoc] = bindTarget.bindSymbol;
+                            } else {
+                                fixupLoc->raw64 = bindTarget.vmAddr;
+                                fixupLocs[fixupLoc] = bindTarget.dylibLevel;
+                                resolvedBindLocations[(const uint8_t*)fixupLoc] = bindTarget.bindSymbol;
+                            }
+                        }
+                        else {
+                            // convert rebase chain entry to raw pointer to target vmaddr
+                            uint64_t targetVMAddr = fixupLoc->generic64.rebase.target;
+                            uint64_t sideTableAddr = 0;
+                            if ( aslrTracker.hasRebaseTarget64(fixupLoc, &sideTableAddr) )
+                                targetVMAddr = sideTableAddr;
+                            // store high8 in side table
+                            if ( fixupLoc->generic64.rebase.high8 )
+                                fixupHigh8s[fixupLoc] = fixupLoc->generic64.rebase.high8;
+                            fixupLoc->raw64 = targetVMAddr;
+                        }
+                        break;
+                    case DYLD_CHAINED_PTR_ARM64E_KERNEL:
+                        if ( fixupLoc->arm64e.bind.bind ) {
+                            uint64_t bindOrdinal = fixupLoc->arm64e.bind.ordinal;
+                            if ( bindOrdinal >= bindTargets.size() ) {
+                                dylibDiag.error("Bind ordinal %lld out of range %lu", bindOrdinal, bindTargets.size());
+                                stop = true;
+                                return;
+                            }
+
+                            const BindTarget& bindTarget = bindTargets[bindOrdinal];
+                            uint64_t targetVMAddr = bindTarget.vmAddr;
+
+                            if ( fixupLoc->arm64e.authBind.auth ) {
+                                // store auth data in side table
+                                fixupAuths[fixupLoc] = {
+                                    (uint16_t)fixupLoc->arm64e.authBind.diversity,
+                                    (bool)fixupLoc->arm64e.authBind.addrDiv,
+                                    (uint8_t)fixupLoc->arm64e.authBind.key
+                                };
+                            }
+                            else {
+                                // plain binds can have addend in chain
+                                targetVMAddr += fixupLoc->arm64e.bind.addend;
+                            }
+                            // change location from a chain ptr into a raw pointer to the target vmaddr
+                            if ( bindTarget.isMissingSymbol ) {
+                                // Track this missing bind for later
+                                // For now we bind it to null and don't slide it.
+                                fixupLoc->raw64 = 0;
+                                missingBindLocations[(const uint8_t*)fixupLoc] = bindTarget.bindSymbol;
+                            } else {
+                                fixupLoc->raw64 = targetVMAddr;
+                                fixupLocs[fixupLoc] = bindTarget.dylibLevel;
+                                resolvedBindLocations[(const uint8_t*)fixupLoc] = bindTarget.bindSymbol;
+                            }
+                        }
+                        else {
+                            // convert rebase chain entry to raw pointer to target vmaddr
+                            if ( fixupLoc->arm64e.rebase.auth ) {
+                                // store auth data in side table
+                                fixupAuths[fixupLoc] = {
+                                    (uint16_t)fixupLoc->arm64e.authRebase.diversity,
+                                    (bool)fixupLoc->arm64e.authRebase.addrDiv,
+                                    (uint8_t)fixupLoc->arm64e.authRebase.key
+                                };
+                                uint64_t targetVMAddr = fixupLoc->arm64e.authRebase.target;
+                                fixupLoc->raw64 = targetVMAddr;
+                            }
+                            else {
+                                uint64_t targetVMAddr = fixupLoc->arm64e.rebase.target;
+                                uint64_t sideTableAddr;
+                                if ( aslrTracker.hasRebaseTarget64(fixupLoc, &sideTableAddr) )
+                                    targetVMAddr = sideTableAddr;
+                                // store high8 in side table
+                                if ( fixupLoc->arm64e.rebase.high8 )
+                                    fixupHigh8s[fixupLoc] = fixupLoc->arm64e.rebase.high8;
+                                fixupLoc->raw64 = targetVMAddr;
+                            }
+                        }
+                        break;
+                    default:
+                        fprintf(stderr, "unknown pointer type %d\n", segInfo->pointer_format);
+                        break;
+                }
+             });
+        });
+        return;
+    }
+
+    // If we have any missing imports, then they should check for the kernel symbol
+    // Grab a hold of that now if it exists so we can check it later
+    __block bool foundUseOfMagicSymbol = false;
+    __block bool foundMissingWeakImport = false;
+
+    const uint64_t loadAddress = ma->preferredLoadAddress();
+    ma->forEachBind(dylibDiag, ^(uint64_t runtimeOffset, int libOrdinal, uint8_t bindType,
+                                 const char *symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool &stop) {
+        // printf("Bind at 0x%llx to '%s'\n", runtimeOffset, symbolName);
+        // Kext binds are a flat namespace so walk until we find the symbol we need
+        bool foundSymbol = false;
+        VTableBindSymbol bindSymbol = { dylibID, symbolName };
+        if (SymbolDefinition symbolDef = findDependencyWithSymbol(symbolName, dependencies); symbolDef.symbolVMAddr != ~0ULL) {
+            // Set the bind to the target address since we found it
+            uint8_t* fixupLoc = (uint8_t*)ma+runtimeOffset;
+            if ( bindType == BIND_TYPE_POINTER ) {
+                if ( _is64 )
+                    *((uint64_t*)fixupLoc) = symbolDef.symbolVMAddr;
+                else
+                    *((uint32_t*)fixupLoc) = (uint32_t)symbolDef.symbolVMAddr;
+
+                // Only track regular fixups for ASLR, not branch fixups
+                fixupLocs[fixupLoc] = symbolDef.kernelCollectionLevel;
+                resolvedBindLocations[(const uint8_t*)fixupLoc] = bindSymbol;
+            } else if ( bindType == BIND_TYPE_TEXT_PCREL32 ) {
+                // The value to store is the difference between the bind target
+                // and the value of the PC after this instruction
+                uint64_t targetAddress = 0;
+                if ( dylibSymbols.dylibLevel != symbolDef.kernelCollectionLevel ) {
+                    // Record this for later as we want to create stubs serially
+                    CacheOffset targetCacheOffset = { symbolDef.kernelCollectionLevel, symbolDef.symbolVMAddr };
+                    branchStubs.emplace_back((BranchStubData){
+                        .targetCacheOffset = targetCacheOffset,
+                        .fixupLoc = fixupLoc,
+                        .fixupVMOffset = runtimeOffset
+                    });
+                } else {
+                    targetAddress = symbolDef.symbolVMAddr;
+                    uint64_t diffValue = targetAddress - (loadAddress + runtimeOffset + 4);
+                    *((uint32_t*)fixupLoc) = (uint32_t)diffValue;
+                }
+            } else {
+                dylibDiag.error("Unexpected bind type: %d", bindType);
+                stop = true;
+                return;
+            }
+
+            foundSymbol = true;
+        }
+
+        if ( foundSymbol && !foundUseOfMagicSymbol ) {
+            foundUseOfMagicSymbol = (strcmp(symbolName, missingWeakImportSymbolName) == 0);
+        }
+
+        if (!foundSymbol) {
+            for (const std::string& dependencyID : dependencies) {
+                auto depIt = dylibsToSymbols.find(dependencyID);
+                if (depIt == dylibsToSymbols.end()) {
+                    dylibDiag.error("Failed to bind '%s' as could not find a kext with '%s' bundle-id",
+                                    symbolName, dependencyID.c_str());
+                    stop = true;
+                    return;
+                }
+
+                const DylibSymbols& dylibSymbols = depIt->second;
+                auto exportIt = dylibSymbols.globals.find(symbolName);
+                if ( exportIt == dylibSymbols.globals.end() )
+                    continue;
+                findDependencyWithSymbol(symbolName, dependencies);
+                break;
+            }
+        }
+
+        // If the symbol is weak, and we didn't find it in our listed
+        // dependencies, then use our own definition
+        if ( !foundSymbol && (libOrdinal == BIND_SPECIAL_DYLIB_WEAK_LOOKUP) ) {
+            auto dylibIt = dylibsToSymbols.find(dylibID);
+            if (dylibIt == dylibsToSymbols.end()) {
+                dylibDiag.error("Failed to bind weak '%s' as could not find a define in self",
+                                symbolName);
+                stop = true;
+                return;
+            }
+
+            const DylibSymbols& dylibSymbols = dylibIt->second;
+            auto exportIt = dylibSymbols.globals.find(symbolName);
+            if ( exportIt != dylibSymbols.globals.end() ) {
+                // Set the bind to the target address since we found it
+                uint8_t* fixupLoc = (uint8_t*)ma+runtimeOffset;
+                if ( bindType == BIND_TYPE_POINTER ) {
+                    if ( _is64 )
+                        *((uint64_t*)fixupLoc) = exportIt->second;
+                    else
+                        *((uint32_t*)fixupLoc) = (uint32_t)exportIt->second;
+
+                    // Only track regular fixups for ASLR, not branch fixups
+                    fixupLocs[fixupLoc] = dylibSymbols.dylibLevel;
+                    resolvedBindLocations[(const uint8_t*)fixupLoc] = bindSymbol;
+                } else if ( bindType == BIND_TYPE_TEXT_PCREL32 ) {
+                    // We should never have a branch to a weak bind as we should have had a GOT for these
+                    dylibDiag.error("Unexpected weak bind type: %d", bindType);
+                    stop = true;
+                    return;
+                } else {
+                    dylibDiag.error("Unexpected bind type: %d", bindType);
+                    stop = true;
+                    return;
+                }
+
+                foundSymbol = true;
+            }
+        }
+
+        if ( !foundSymbol && weakImport ) {
+            if ( bindType != BIND_TYPE_POINTER ) {
+                dylibDiag.error("Unexpected bind type: %d", bindType);
+                stop = true;
+                return;
+            }
+            // Find _gOSKextUnresolved in the kernel
+            // Weak imports are not compared against null, but instead against the address of that symbol
+            auto kernelSymbolsIt = dylibsToSymbols.find(kernelID);
+            assert(kernelSymbolsIt != dylibsToSymbols.end());
+            const DylibSymbols& kernelSymbols = kernelSymbolsIt->second;
+            auto exportIt = kernelSymbols.globals.find(missingWeakImportSymbolName);
+            if (exportIt != kernelSymbols.globals.end()) {
+                foundMissingWeakImport = true;
+
+                uint8_t* fixupLoc = (uint8_t*)ma+runtimeOffset;
+                if ( _is64 )
+                    *((uint64_t*)fixupLoc) = exportIt->second;
+                else
+                    *((uint32_t*)fixupLoc) = (uint32_t)exportIt->second;
+
+                // Only track regular fixups for ASLR, not branch fixups
+                fixupLocs[fixupLoc] = kernelSymbols.dylibLevel;
+                return;
+            }
+            dylibDiag.error("Weak bind symbol '%s' not found in kernel", missingWeakImportSymbolName);
+            return;
+        }
+
+        if ( !foundSymbol ) {
+            // Store missing binds for later.  They may be fixed by vtable patching
+            const uint8_t* fixupLoc = (uint8_t*)ma+runtimeOffset;
+            missingBindLocations[fixupLoc] = bindSymbol;
+        }
+    }, ^(const char *symbolName) {
+        dylibDiag.error("Strong binds are not supported right now");
+    });
+
+    if ( foundMissingWeakImport && !foundUseOfMagicSymbol ) {
+        dylibDiag.error("Has weak references but does not test for them.  "
+                        "Test for weak references with OSKextSymbolIsResolved().");
+        return;
+    }
+
+    ma->forEachRebase(dylibDiag, false, ^(uint64_t runtimeOffset, bool &stop) {
+        uint8_t* fixupLoc = (uint8_t*)ma+runtimeOffset;
+        fixupLocs[fixupLoc] = (uint8_t)~0U;
+    });
+}
+
+// A helper to automatically call CFRelease when we go out of scope
+struct AutoReleaseTypeRef {
+    AutoReleaseTypeRef() = default;
+    ~AutoReleaseTypeRef() {
+        if ( ref != nullptr ) {
+            CFRelease(ref);
+        }
+    }
+    void setRef(CFTypeRef typeRef) {
+        assert(ref == nullptr);
+        ref = typeRef;
+    }
+
+    CFTypeRef ref = nullptr;
+};
+
+static std::unique_ptr<std::unordered_set<std::string>> getKPI(Diagnostics& diags, const dyld3::MachOAnalyzer* ma,
+                                                               std::string_view dylibID) {
+    bool isAppleKext = (dylibID.find("com.apple") == 0);
+    if ( !isAppleKext )
+        return {};
+
+    __block std::list<std::string> nonASCIIStrings;
+    auto getString = ^(Diagnostics& diags, CFStringRef symbolNameRef) {
+        const char* symbolName = CFStringGetCStringPtr(symbolNameRef, kCFStringEncodingUTF8);
+        if ( symbolName != nullptr )
+            return symbolName;
+
+        CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(symbolNameRef), kCFStringEncodingUTF8);
+        char buffer[len + 1];
+        if ( !CFStringGetCString(symbolNameRef, buffer, len, kCFStringEncodingUTF8) ) {
+            diags.error("Could not convert string to ASCII");
+            return (const char*)nullptr;
+        }
+        buffer[len] = '\0';
+        nonASCIIStrings.push_back(buffer);
+        return nonASCIIStrings.back().c_str();
+    };
+
+    uint64_t symbolSetsSize = 0;
+    const void* symbolSetsContent = ma->findSectionContent("__LINKINFO", "__symbolsets", symbolSetsSize);
+    if ( symbolSetsContent == nullptr )
+        return {};
+
+    AutoReleaseTypeRef dataRefReleaser;
+    AutoReleaseTypeRef plistRefReleaser;
+
+    std::unordered_set<std::string> symbols;
+    CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)symbolSetsContent, symbolSetsSize, kCFAllocatorNull);
+    if ( dataRef == nullptr ) {
+        diags.error("Could not create data ref for kpi");
+        return {};
+    }
+    dataRefReleaser.setRef(dataRef);
+
+    CFErrorRef errorRef = nullptr;
+    CFPropertyListRef plistRef = CFPropertyListCreateWithData(kCFAllocatorDefault, dataRef, kCFPropertyListImmutable, nullptr, &errorRef);
+    if (errorRef != nullptr) {
+        CFStringRef errorString = CFErrorCopyDescription(errorRef);
+        diags.error("Could not load plist because :%s", CFStringGetCStringPtr(errorString, kCFStringEncodingASCII));
+        CFRelease(errorRef);
+        return {};
+    }
+    if ( plistRef == nullptr ) {
+        diags.error("Could not create plist ref for kpi");
+        return {};
+    }
+    plistRefReleaser.setRef(plistRef);
+
+    if ( CFGetTypeID(plistRef) != CFDictionaryGetTypeID() ) {
+        diags.error("kpi plist should be a dictionary");
+        return {};
+    }
+
+    CFDictionaryRef symbolSetsDictRef = (CFDictionaryRef)plistRef;
+
+    // CFBundleIdentifier
+    CFStringRef bundleIDRef = (CFStringRef)CFDictionaryGetValue(symbolSetsDictRef, CFSTR("CFBundleIdentifier"));
+    if ( (bundleIDRef == nullptr) || (CFGetTypeID(bundleIDRef) != CFStringGetTypeID()) ) {
+        diags.error("kpi bundle ID should be a string");
+        return {};
+    }
+
+    const char* bundleID = getString(diags, bundleIDRef);
+    if ( bundleID == nullptr )
+        return {};
+
+    if ( dylibID != bundleID ) {
+        diags.error("kpi bundle ID doesn't match kext");
+        return {};
+    }
+
+    CFArrayRef symbolsArrayRef = (CFArrayRef)CFDictionaryGetValue(symbolSetsDictRef, CFSTR("Symbols"));
+    if ( symbolsArrayRef != nullptr ) {
+        if ( CFGetTypeID(symbolsArrayRef) != CFArrayGetTypeID() ) {
+            diags.error("Symbols value should be an array");
+            return {};
+        }
+        for (CFIndex symbolSetIndex = 0; symbolSetIndex != CFArrayGetCount(symbolsArrayRef); ++symbolSetIndex) {
+            CFStringRef symbolNameRef = (CFStringRef)CFArrayGetValueAtIndex(symbolsArrayRef, symbolSetIndex);
+            if ( (symbolNameRef == nullptr) || (CFGetTypeID(symbolNameRef) != CFStringGetTypeID()) ) {
+                diags.error("Symbol name should be a string");
+                return {};
+            }
+
+            const char* symbolName = getString(diags, symbolNameRef);
+            if ( symbolName == nullptr )
+                return {};
+            symbols.insert(symbolName);
+        }
+    }
+
+    return std::make_unique<std::unordered_set<std::string>>(std::move(symbols));
+}
+
+void AppCacheBuilder::processFixups()
+{
+    auto dylibsToSymbolsOwner = std::make_unique<std::map<std::string, DylibSymbols>>();
+    std::map<std::string, DylibSymbols>& dylibsToSymbols = *dylibsToSymbolsOwner.get();
+
+    auto vtablePatcherOwner = std::make_unique<VTablePatcher>(numFixupLevels);
+    VTablePatcher& vtablePatcher = *vtablePatcherOwner.get();
+
+    const uint32_t kernelLevel = 0;
+    uint8_t currentLevel = getCurrentFixupLevel();
+
+    // Keep track of missing binds until later.  They may be "resolved" by vtable patching
+    std::map<const uint8_t*, const VTableBindSymbol> missingBindLocations;
+
+    __block std::string kernelID;
+    __block const dyld3::MachOAnalyzer* kernelMA = nullptr;
+    if ( appCacheOptions.cacheKind == Options::AppCacheKind::kernel ) {
+        kernelMA = getKernelStaticExecutableFromCache();
+        forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID,
+                            DylibStripMode stripMode, const std::vector<std::string> &dependencies,
+                            Diagnostics& dylibDiag,
+                            bool &stop) {
+            if ( ma == kernelMA ) {
+                kernelID = dylibID;
+                stop = true;
+            }
+        });
+        assert(!kernelID.empty());
+    } else {
+        assert(existingKernelCollection != nullptr);
+        existingKernelCollection->forEachDylib(_diagnostics, ^(const dyld3::MachOAnalyzer *ma, const char *name, bool &stop) {
+            if ( ma->isStaticExecutable() ) {
+                kernelMA = ma;
+                kernelID = name;
+            }
+        });
+        if ( kernelMA == nullptr ) {
+            _diagnostics.error("Could not find kernel in kernel collection");
+            return;
+        }
+    }
+
+    auto getGlobals = [](Diagnostics& diags, const dyld3::MachOAnalyzer *ma) -> std::map<std::string_view, uint64_t> {
+        // Note we don't put __block on the variable directly as then it gets copied in to the return value
+        std::map<std::string_view, uint64_t> exports;
+        __block std::map<std::string_view, uint64_t>& exportsRef = exports;
+        ma->forEachGlobalSymbol(diags, ^(const char *symbolName, uint64_t n_value,
+                                         uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) {
+            exportsRef[symbolName] = n_value;
+        });
+        return exports;
+    };
+
+    auto getLocals = [](Diagnostics& diags, const dyld3::MachOAnalyzer *ma) -> std::map<std::string_view, uint64_t> {
+        // Note we don't put __block on the variable directly as then it gets copied in to the return value
+        std::map<std::string_view, uint64_t> exports;
+        __block std::map<std::string_view, uint64_t>& exportsRef = exports;
+        ma->forEachLocalSymbol(diags, ^(const char *symbolName, uint64_t n_value,
+                                         uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) {
+            exportsRef[symbolName] = n_value;
+        });
+        return exports;
+    };
+
+    dylibsToSymbols[kernelID] = {
+        getGlobals(_diagnostics, kernelMA),
+        getLocals(_diagnostics, kernelMA),
+        nullptr,
+        kernelLevel,
+        std::string(kernelID)
+    };
+
+    // Add all the codeless kext's as kext's can list them as dependencies
+    // Note we add placeholders here which can be legitimately replaced by symbol sets
+    for (const InputDylib& dylib : codelessKexts) {
+        dylibsToSymbols[dylib.dylibID] = { };
+    }
+
+    // Similarly, add placeholders for codeless kexts in the baseKC
+    if ( existingKernelCollection != nullptr ) {
+        existingKernelCollection->forEachPrelinkInfoLibrary(_diagnostics,
+                                                            ^(const char *bundleName, const char* relativePath,
+                                                              const std::vector<const char *> &deps) {
+            dylibsToSymbols[bundleName] = { };
+        });
+    }
+
+    // And placeholders for codeless kexts in the pageableKC
+    if ( pageableKernelCollection != nullptr ) {
+        pageableKernelCollection->forEachPrelinkInfoLibrary(_diagnostics,
+                                                            ^(const char *bundleName, const char* relativePath,
+                                                              const std::vector<const char *> &deps) {
+            dylibsToSymbols[bundleName] = { };
+        });
+    }
+
+    // Get the symbol sets
+    AutoReleaseTypeRef dataRefReleaser;
+    AutoReleaseTypeRef plistRefReleaser;
+
+    __block std::list<std::string> nonASCIIStrings;
+    auto getString = ^(Diagnostics& diags, CFStringRef symbolNameRef) {
+        const char* symbolName = CFStringGetCStringPtr(symbolNameRef, kCFStringEncodingUTF8);
+        if ( symbolName != nullptr )
+            return symbolName;
+
+        CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(symbolNameRef), kCFStringEncodingUTF8);
+        char buffer[len + 1];
+        if ( !CFStringGetCString(symbolNameRef, buffer, len, kCFStringEncodingUTF8) ) {
+            diags.error("Could not convert string to ASCII");
+            return (const char*)nullptr;
+        }
+        buffer[len] = '\0';
+        nonASCIIStrings.push_back(buffer);
+        return nonASCIIStrings.back().c_str();
+    };
+
+    uint64_t symbolSetsSize = 0;
+    const void* symbolSetsContent = kernelMA->findSectionContent("__LINKINFO", "__symbolsets", symbolSetsSize);
+    if ( symbolSetsContent != nullptr ) {
+        const DylibSymbols& kernelSymbols = dylibsToSymbols[kernelID];
+
+        CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const uint8_t*)symbolSetsContent, symbolSetsSize, kCFAllocatorNull);
+        if ( dataRef == nullptr ) {
+            _diagnostics.error("Could not create data ref for symbol sets");
+            return;
+        }
+        dataRefReleaser.setRef(dataRef);
+
+        CFErrorRef errorRef = nullptr;
+        CFPropertyListRef plistRef = CFPropertyListCreateWithData(kCFAllocatorDefault, dataRef, kCFPropertyListImmutable, nullptr, &errorRef);
+        if (errorRef != nullptr) {
+            CFStringRef errorString = CFErrorCopyDescription(errorRef);
+            _diagnostics.error("Could not load plist because :%s",
+                               CFStringGetCStringPtr(errorString, kCFStringEncodingASCII));
+            CFRelease(errorRef);
+            return;
+        }
+        if ( plistRef == nullptr ) {
+            _diagnostics.error("Could not create plist ref for symbol sets");
+            return;
+        }
+        plistRefReleaser.setRef(plistRef);
+
+        if ( CFGetTypeID(plistRef) != CFDictionaryGetTypeID() ) {
+            _diagnostics.error("Symbol set plist should be a dictionary");
+            return;
+        }
+        CFDictionaryRef symbolSetsDictRef = (CFDictionaryRef)plistRef;
+        CFArrayRef symbolSetArrayRef = (CFArrayRef)CFDictionaryGetValue(symbolSetsDictRef, CFSTR("SymbolsSets"));
+        if ( symbolSetArrayRef != nullptr ) {
+            if ( CFGetTypeID(symbolSetArrayRef) != CFArrayGetTypeID() ) {
+                _diagnostics.error("SymbolsSets value should be an array");
+                return;
+            }
+            for (CFIndex symbolSetIndex = 0; symbolSetIndex != CFArrayGetCount(symbolSetArrayRef); ++symbolSetIndex) {
+                CFDictionaryRef symbolSetDictRef = (CFDictionaryRef)CFArrayGetValueAtIndex(symbolSetArrayRef, symbolSetIndex);
+                if ( CFGetTypeID(symbolSetDictRef) != CFDictionaryGetTypeID() ) {
+                    _diagnostics.error("Symbol set element should be a dictionary");
+                    return;
+                }
+
+                // CFBundleIdentifier
+                CFStringRef bundleIDRef = (CFStringRef)CFDictionaryGetValue(symbolSetDictRef, CFSTR("CFBundleIdentifier"));
+                if ( (bundleIDRef == nullptr) || (CFGetTypeID(bundleIDRef) != CFStringGetTypeID()) ) {
+                    _diagnostics.error("Symbol set bundle ID should be a string");
+                    return;
+                }
+
+                // Symbols
+                CFArrayRef symbolsArrayRef = (CFArrayRef)CFDictionaryGetValue(symbolSetDictRef, CFSTR("Symbols"));
+                if ( (symbolsArrayRef == nullptr) || (CFGetTypeID(symbolsArrayRef) != CFArrayGetTypeID()) ) {
+                    _diagnostics.error("Symbol set symbols should be an array");
+                    return;
+                }
+
+                std::map<std::string_view, uint64_t> symbolSetGlobals;
+                std::map<std::string_view, uint64_t> symbolSetLocals;
+                for (CFIndex symbolIndex = 0; symbolIndex != CFArrayGetCount(symbolsArrayRef); ++symbolIndex) {
+                    CFDictionaryRef symbolDictRef = (CFDictionaryRef)CFArrayGetValueAtIndex(symbolsArrayRef, symbolIndex);
+                    if ( CFGetTypeID(symbolDictRef) != CFDictionaryGetTypeID() ) {
+                        _diagnostics.error("Symbols array element should be a dictionary");
+                        return;
+                    }
+
+                    // SymbolPrefix
+                    CFStringRef symbolPrefixRef = (CFStringRef)CFDictionaryGetValue(symbolDictRef, CFSTR("SymbolPrefix"));
+                    if ( symbolPrefixRef != nullptr ) {
+                        if ( CFGetTypeID(symbolPrefixRef) != CFStringGetTypeID() ) {
+                            _diagnostics.error("Symbol prefix should be a string");
+                            return;
+                        }
+
+                        const char* symbolPrefix = getString(_diagnostics, symbolPrefixRef);
+                        if ( symbolPrefix == nullptr )
+                            return;
+                        size_t symbolPrefixLen = strlen(symbolPrefix);
+
+                        // FIXME: Brute force might not be the best thing here
+                        for (std::pair<std::string_view, uint64_t> kernelGlobal : kernelSymbols.globals) {
+                            if ( strncmp(kernelGlobal.first.data(), symbolPrefix, symbolPrefixLen) == 0 ) {
+                                symbolSetGlobals[kernelGlobal.first] = kernelGlobal.second;
+                            }
+                        }
+                        for (std::pair<std::string_view, uint64_t> kernelLocal : kernelSymbols.locals) {
+                            if ( strncmp(kernelLocal.first.data(), symbolPrefix, symbolPrefixLen) == 0 ) {
+                                symbolSetLocals[kernelLocal.first] = kernelLocal.second;
+                            }
+                        }
+                        continue;
+                    }
+
+                    // SymbolName
+                    CFStringRef symbolNameRef = (CFStringRef)CFDictionaryGetValue(symbolDictRef, CFSTR("SymbolName"));
+                    if ( (symbolNameRef == nullptr) || (CFGetTypeID(symbolNameRef) != CFStringGetTypeID()) ) {
+                        _diagnostics.error("Symbol name should be a string");
+                        return;
+                    }
+
+                    // AliasTarget [Optional]
+                    CFStringRef aliasTargetRef = (CFStringRef)CFDictionaryGetValue(symbolDictRef, CFSTR("AliasTarget"));
+                    if ( aliasTargetRef == nullptr ) {
+                        // No alias
+                        const char* symbolName = getString(_diagnostics, symbolNameRef);
+                        if ( symbolName == nullptr )
+                            return;
+
+                        // Find the symbol in xnu
+                        auto globalIt = kernelSymbols.globals.find(symbolName);
+                        if (globalIt != kernelSymbols.globals.end()) {
+                            symbolSetGlobals[symbolName] = globalIt->second;
+                        }
+
+                        auto localIt = kernelSymbols.locals.find(symbolName);
+                        if (localIt != kernelSymbols.locals.end()) {
+                            symbolSetLocals[symbolName] = localIt->second;
+                        }
+                    } else {
+                        // We have an alias
+                        if ( CFGetTypeID(aliasTargetRef) != CFStringGetTypeID() ) {
+                            _diagnostics.error("Alias should be a string");
+                            return;
+                        }
+
+                        const char* symbolName = getString(_diagnostics, symbolNameRef);
+                        if ( symbolName == nullptr )
+                            return;
+                        const char* aliasTargetName = getString(_diagnostics, aliasTargetRef);
+                        if ( aliasTargetName == nullptr )
+                            return;
+
+                        // Find the alias symbol in xnu
+                        auto globalIt = kernelSymbols.globals.find(aliasTargetName);
+                        if (globalIt != kernelSymbols.globals.end()) {
+                            symbolSetGlobals[symbolName] = globalIt->second;
+                        } else {
+                            _diagnostics.error("Alias '%s' not found in kernel", aliasTargetName);
+                            return;
+                        }
+
+                        auto localIt = kernelSymbols.locals.find(aliasTargetName);
+                        if (localIt != kernelSymbols.locals.end()) {
+                            symbolSetLocals[symbolName] = localIt->second;
+                        } else {
+                            // This is not an error, as aliases from symbol sets from the kernel
+                            // are only for vtable patching, not general binding
+                        }
+                    }
+                }
+                const char* dylibID = getString(_diagnostics, bundleIDRef);
+                if ( dylibID == nullptr )
+                    return;
+
+                // HACK: kxld aliases __ZN15OSMetaClassBase25_RESERVEDOSMetaClassBase3Ev to __ZN15OSMetaClassBase8DispatchE5IORPC
+                auto metaclassHackIt = symbolSetGlobals.find("__ZN15OSMetaClassBase8DispatchE5IORPC");
+                if ( metaclassHackIt != symbolSetGlobals.end() )
+                    symbolSetGlobals["__ZN15OSMetaClassBase25_RESERVEDOSMetaClassBase3Ev"] = metaclassHackIt->second;
+                dylibsToSymbols[dylibID] = {
+                    std::move(symbolSetGlobals),
+                    std::move(symbolSetLocals),
+                    nullptr,
+                    kernelLevel,
+                    dylibID
+                };
+            }
+        }
+    }
+
+    auto processBinary = ^(Diagnostics& dylibDiags, const dyld3::MachOAnalyzer *ma,
+                           const std::string& dylibID, uint32_t dylibLevel) {
+        // We dont support export trie's for now
+        uint32_t unusedExportTrieOffset = 0;
+        uint32_t unusedExportTrieSize = 0;
+        if (ma->hasExportTrie(unusedExportTrieOffset, unusedExportTrieSize))
+            assert(false);
+
+        // Already done the kernel before.
+        if ( ma == kernelMA )
+            return;
+
+        // Regular kext.
+        dylibsToSymbols[dylibID] = {
+            getGlobals(dylibDiags, ma),
+            getLocals(dylibDiags, ma),
+            getKPI(dylibDiags, ma, dylibID),
+            dylibLevel,
+            dylibID };
+    };
+
+    // Process binary symbols in parallel
+    {
+        struct DylibData {
+            const dyld3::MachOAnalyzer*     ma              = nullptr;
+            Diagnostics&                    dylibDiag;
+            const std::string&              dylibID;
+        };
+
+        __block std::vector<DylibData> dylibDatas;
+        dylibDatas.reserve(sortedDylibs.size());
+        forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID, DylibStripMode stripMode,
+                            const std::vector<std::string> &dependencies, Diagnostics &dylibDiag, bool &stop) {
+            // Already done the kernel before.
+            if ( ma == kernelMA )
+                return;
+
+            // Make space for all the map entries so that we know they are there when we write their values later
+            dylibsToSymbols[dylibID] = { };
+            dylibDatas.emplace_back((DylibData){ ma, dylibDiag, dylibID });
+        });
+
+        dispatch_apply(dylibDatas.size(), DISPATCH_APPLY_AUTO, ^(size_t index) {
+            DylibData& dylibData = dylibDatas[index];
+            processBinary(dylibData.dylibDiag, dylibData.ma, dylibData.dylibID, currentLevel);
+        });
+    }
+
+    // Add exports from the kernel collection if we have it
+    if ( existingKernelCollection != nullptr ) {
+        uint8_t fixupLevel = getFixupLevel(Options::AppCacheKind::kernel);
+        existingKernelCollection->forEachDylib(_diagnostics, ^(const dyld3::MachOAnalyzer *ma, const char *name, bool &stop) {
+            processBinary(_diagnostics, ma, name, fixupLevel);
+        });
+    }
+
+    // Add exports from the pageable collection if we have it
+    if ( pageableKernelCollection != nullptr ) {
+        uint8_t fixupLevel = getFixupLevel(Options::AppCacheKind::pageableKC);
+        pageableKernelCollection->forEachDylib(_diagnostics, ^(const dyld3::MachOAnalyzer *ma, const char *name, bool &stop) {
+            processBinary(_diagnostics, ma, name, fixupLevel);
+        });
+    }
+
+    // Map from an offset in to a KC to a synthesized stub which branches to that offset
+    struct CacheOffsetHash
+    {
+        size_t operator() (const CacheOffset& cacheOffset) const
+        {
+            return std::hash<uint32_t>{}(cacheOffset.first) ^ std::hash<uint64_t>{}(cacheOffset.second);
+        }
+    };
+    std::unordered_map<CacheOffset, uint64_t, CacheOffsetHash> branchStubs;
+
+    // Clear the branch regions sizes so that we fill them up to their buffer sizes as we go
+    branchStubsRegion.sizeInUse = 0;
+    branchGOTsRegion.sizeInUse = 0;
+
+    {
+        // Map from each symbol to the list of dylibs which export it
+        auto symbolMapOwner = std::make_unique<std::unordered_map<std::string_view, std::vector<DylibSymbolLocation>>>();
+        __block auto& symbolMap = *symbolMapOwner.get();
+        for (const auto& dylibNameAndSymbols : dylibsToSymbols) {
+            const DylibSymbols& dylibSymbols = dylibNameAndSymbols.second;
+            for (const auto& symbolNameAndAddress : dylibSymbols.globals) {
+                // By default, everything i KPI, ie, can be linked by third parties.
+                // If a symbol is is provided, even an empty one, then it can override this
+                bool isKPI = true;
+                if ( dylibSymbols.dylibName == "com.apple.kpi.private" ) {
+                    // com.apple.kpi.private is always hidden from third parties.  They shouldn't even list it as a dependency
+                    isKPI = false;
+                } else if ( dylibSymbols.kpiSymbols ) {
+                    const std::unordered_set<std::string>* kpiSymbols = dylibSymbols.kpiSymbols.get();
+                    if ( kpiSymbols->count(symbolNameAndAddress.first.data()) == 0 )
+                        isKPI = false;
+                }
+                symbolMap[symbolNameAndAddress.first].push_back({ &dylibSymbols, symbolNameAndAddress.second, isKPI });
+            }
+        }
+
+        auto dylibFixupsOwner = std::make_unique<std::vector<DylibFixups>>();
+        __block auto& dylibFixups = *dylibFixupsOwner.get();
+        dylibFixups.reserve(sortedDylibs.size());
+        forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID, DylibStripMode stripMode,
+                            const std::vector<std::string> &dependencies, Diagnostics &dylibDiag, bool &stop) {
+
+            auto dylibSymbolsIt = dylibsToSymbols.find(dylibID);
+            assert(dylibSymbolsIt != dylibsToSymbols.end());
+
+            dylibFixups.emplace_back((DylibFixups){
+                .ma = ma,
+                .dylibSymbols = dylibSymbolsIt->second,
+                .dylibDiag = dylibDiag,
+                .dependencies = dependencies
+            });
+        });
+
+        dispatch_apply(dylibFixups.size(), DISPATCH_APPLY_AUTO, ^(size_t index) {
+            DylibFixups& dylibFixup = dylibFixups[index];
+            dylibFixup.processFixups(dylibsToSymbols, symbolMap, kernelID, _aslrTracker);
+        });
+
+        // Merge all the dylib results in serial
+        for (DylibFixups& dylibFixup : dylibFixups) {
+            // Skip bad dylibs
+            if ( dylibFixup.dylibDiag.hasError() ) {
+                if ( !_diagnostics.hasError() ) {
+                    _diagnostics.error("One or more binaries has an error which prevented linking.  See other errors.");
+                }
+                return;
+            }
+
+            if ( !dylibFixup.missingBindLocations.empty() ) {
+                missingBindLocations.insert(dylibFixup.missingBindLocations.begin(),
+                                            dylibFixup.missingBindLocations.end());
+            }
+
+            if ( !dylibFixup.fixupLocs.empty() ) {
+                for (auto fixupLocAndLevel : dylibFixup.fixupLocs) {
+                    _aslrTracker.add(fixupLocAndLevel.first, fixupLocAndLevel.second);
+                }
+            }
+
+            if ( !dylibFixup.fixupHigh8s.empty() ) {
+                for (auto fixupLocAndHigh8 : dylibFixup.fixupHigh8s) {
+                    _aslrTracker.setHigh8(fixupLocAndHigh8.first, fixupLocAndHigh8.second);
+                }
+            }
+
+            if ( !dylibFixup.fixupAuths.empty() ) {
+                for (auto fixupLocAndAuth : dylibFixup.fixupAuths) {
+                    _aslrTracker.setAuthData(fixupLocAndAuth.first, fixupLocAndAuth.second.diversity,
+                                             fixupLocAndAuth.second.addrDiv, fixupLocAndAuth.second.key);
+                }
+            }
+
+            // Emit branch stubs
+            const uint64_t loadAddress = dylibFixup.ma->preferredLoadAddress();
+            for (const DylibFixups::BranchStubData& branchData : dylibFixup.branchStubs) {
+                // Branching from the auxKC to baseKC.  ld64 doesn't emit a stub in x86_64 kexts
+                // so we need to synthesize one now
+                uint64_t targetAddress = 0;
+                const CacheOffset& targetCacheOffset = branchData.targetCacheOffset;
+                auto itAndInserted = branchStubs.insert({ targetCacheOffset, 0 });
+                if ( itAndInserted.second ) {
+                    // We inserted the branch location, so we need to create new stubs and GOTs
+                    if ( branchStubsRegion.sizeInUse == branchStubsRegion.bufferSize ) {
+                        _diagnostics.error("Overflow in branch stubs region");
+                        return;
+                    }
+                    if ( branchGOTsRegion.sizeInUse == branchGOTsRegion.bufferSize ) {
+                        _diagnostics.error("Overflow in branch GOTs region");
+                        return;
+                    }
+                    uint64_t stubAddress = branchStubsRegion.unslidLoadAddress + branchStubsRegion.sizeInUse;
+                    uint8_t* stubBuffer = branchStubsRegion.buffer + branchStubsRegion.sizeInUse;
+                    uint64_t gotAddress = branchGOTsRegion.unslidLoadAddress + branchGOTsRegion.sizeInUse;
+                    uint8_t* gotBuffer = branchGOTsRegion.buffer + branchGOTsRegion.sizeInUse;
+
+                    // Write the stub
+                    // ff 25 aa bb cc dd    jmpq    *0xddccbbaa(%rip)
+                    uint64_t diffValue = gotAddress - (stubAddress + 6);
+                    stubBuffer[0] = 0xFF;
+                    stubBuffer[1] = 0x25;
+                    memcpy(&stubBuffer[2], &diffValue, sizeof(uint32_t));
+
+                    // And write the GOT
+                    uint8_t symbolCacheLevel = targetCacheOffset.first;
+                    uint64_t symbolVMAddr = targetCacheOffset.second;
+                    if ( _is64 )
+                        *((uint64_t*)gotBuffer) = symbolVMAddr;
+                    else
+                        *((uint32_t*)gotBuffer) = (uint32_t)symbolVMAddr;
+                    _aslrTracker.add(gotBuffer, symbolCacheLevel);
+
+                    branchStubsRegion.sizeInUse += 6;
+                    branchGOTsRegion.sizeInUse += 8;
+                    targetAddress = stubAddress;
+                    itAndInserted.first->second = targetAddress;
+                } else {
+                    // The stub already existed, so use it
+                    targetAddress = itAndInserted.first->second;
+                }
+                uint64_t diffValue = targetAddress - (loadAddress + branchData.fixupVMOffset + 4);
+                *((uint32_t*)branchData.fixupLoc) = (uint32_t)diffValue;
+            }
+        }
+
+        // FIXME: We could move symbolOwner and dylibFixupsOwner to a worker thread to be destroyed
+    }
+
+    // Now that we've processes all rebases/binds, patch all the vtables
+
+    // Add all the collections to the vtable patcher
+    if ( existingKernelCollection != nullptr ) {
+        // The baseKC for x86_64 has __HIB mapped first , so we need to get either the __DATA or __TEXT depending on what is earliest
+        // The kernel base address is still __TEXT, even if __DATA or __HIB is mapped prior to that.
+        // The loader may have loaded something before __TEXT, but the existingKernelCollection pointer still corresponds to __TEXT
+        __block uint64_t baseAddress = ~0ULL;
+        existingKernelCollection->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) {
+            baseAddress = std::min(baseAddress, info.vmAddr);
+        });
+
+        // The existing collection is a pointer to the mach_header for the baseKC, but __HIB and other segments may be before that
+        // Offset those here
+        uint64_t basePointerOffset = existingKernelCollection->preferredLoadAddress() - baseAddress;
+        const uint8_t* basePointer = (uint8_t*)existingKernelCollection - basePointerOffset;
+
+        vtablePatcher.addKernelCollection(existingKernelCollection, Options::AppCacheKind::kernel,
+                                          basePointer, baseAddress);
+    }
+
+    if ( pageableKernelCollection != nullptr ) {
+        // The baseKC for x86_64 has __HIB mapped first , so we need to get either the __DATA or __TEXT depending on what is earliest
+        // The kernel base address is still __TEXT, even if __DATA or __HIB is mapped prior to that.
+        // The loader may have loaded something before __TEXT, but the existingKernelCollection pointer still corresponds to __TEXT
+        __block uint64_t baseAddress = ~0ULL;
+        pageableKernelCollection->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) {
+            baseAddress = std::min(baseAddress, info.vmAddr);
+        });
+
+        // The existing collection is a pointer to the mach_header for the baseKC, but __HIB and other segments may be before that
+        // Offset those here
+        uint64_t basePointerOffset = pageableKernelCollection->preferredLoadAddress() - baseAddress;
+        const uint8_t* basePointer = (uint8_t*)pageableKernelCollection - basePointerOffset;
+
+        vtablePatcher.addKernelCollection(pageableKernelCollection, Options::AppCacheKind::pageableKC,
+                                          basePointer, baseAddress);
+    }
+
+    // Also add our KC
+    vtablePatcher.addKernelCollection((const dyld3::MachOAppCache*)cacheHeader.header, appCacheOptions.cacheKind,
+                                      (const uint8_t*)_fullAllocatedBuffer, cacheBaseAddress);
+
+    // Add all the dylibs to the patcher
+    {
+        if ( existingKernelCollection != nullptr ) {
+            uint8_t fixupLevel = getFixupLevel(Options::AppCacheKind::kernel);
+
+            __block std::map<std::string, std::vector<std::string>> kextDependencies;
+            kextDependencies[kernelID] = {};
+            existingKernelCollection->forEachPrelinkInfoLibrary(_diagnostics,
+                                                                ^(const char *bundleName, const char* relativePath,
+                                                                  const std::vector<const char *> &deps) {
+                std::vector<std::string>& dependencies = kextDependencies[bundleName];
+                dependencies.insert(dependencies.end(), deps.begin(), deps.end());
+            });
+
+            existingKernelCollection->forEachDylib(_diagnostics, ^(const dyld3::MachOAnalyzer *ma, const char *dylibID, bool &stop) {
+                auto depsIt = kextDependencies.find(dylibID);
+                assert(depsIt != kextDependencies.end());
+                vtablePatcher.addDylib(_diagnostics, ma, dylibID, depsIt->second, fixupLevel);
+            });
+        }
+
+        if ( pageableKernelCollection != nullptr ) {
+            uint8_t fixupLevel = getFixupLevel(Options::AppCacheKind::pageableKC);
+
+            __block std::map<std::string, std::vector<std::string>> kextDependencies;
+            pageableKernelCollection->forEachPrelinkInfoLibrary(_diagnostics,
+                                                                ^(const char *bundleName, const char* relativePath,
+                                                                  const std::vector<const char *> &deps) {
+                std::vector<std::string>& dependencies = kextDependencies[bundleName];
+                dependencies.insert(dependencies.end(), deps.begin(), deps.end());
+            });
+
+            pageableKernelCollection->forEachDylib(_diagnostics, ^(const dyld3::MachOAnalyzer *ma, const char *dylibID, bool &stop) {
+                auto depsIt = kextDependencies.find(dylibID);
+                assert(depsIt != kextDependencies.end());
+                vtablePatcher.addDylib(_diagnostics, ma, dylibID, depsIt->second, fixupLevel);
+            });
+        }
+
+        forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID, DylibStripMode stripMode,
+                            const std::vector<std::string> &dependencies, Diagnostics& dylibDiag, bool &stop) {
+            vtablePatcher.addDylib(dylibDiag, ma, dylibID, dependencies, currentLevel);
+        });
+    }
+
+    vtablePatcher.findMetaclassDefinitions(dylibsToSymbols, kernelID, kernelMA, appCacheOptions.cacheKind);
+    vtablePatcher.findExistingFixups(_diagnostics, existingKernelCollection, pageableKernelCollection);
+    if ( _diagnostics.hasError() )
+        return;
+
+    // Add vtables from the base KC if we have one
+    if ( existingKernelCollection != nullptr ) {
+        vtablePatcher.findBaseKernelVTables(_diagnostics, existingKernelCollection, dylibsToSymbols);
+        if ( _diagnostics.hasError() )
+            return;
+    }
+
+    // Add vtables from the pageable KC if we have one
+    if ( pageableKernelCollection != nullptr ) {
+        vtablePatcher.findPageableKernelVTables(_diagnostics, pageableKernelCollection, dylibsToSymbols);
+        if ( _diagnostics.hasError() )
+            return;
+    }
+
+    // Add vables from our level
+    vtablePatcher.findVTables(currentLevel, kernelMA, dylibsToSymbols, _aslrTracker, missingBindLocations);
+
+    // Don't run the patcher if we have a failure finding the vtables
+    if ( vtablePatcher.hasError() ) {
+        _diagnostics.error("One or more binaries has an error which prevented linking.  See other errors.");
+        return;
+    }
+
+    // Now patch all of the vtables.
+    vtablePatcher.patchVTables(_diagnostics, missingBindLocations, _aslrTracker, currentLevel);
+    if ( _diagnostics.hasError() )
+        return;
+
+    if ( vtablePatcher.hasError() ) {
+        _diagnostics.error("One or more binaries has an error which prevented linking.  See other errors.");
+        return;
+    }
+
+    // FIXME: We could move vtablePatcherOwner to a worker thread to be destroyed
+    vtablePatcherOwner.reset();
+
+    // Also error out if we have an error on any of the dylib diagnostic objects
+
+    // Log any binds which are still missing
+    for (const auto& missingLocationAndBind : missingBindLocations) {
+        const uint8_t* missingBindLoc = missingLocationAndBind.first;
+        const VTableBindSymbol& missingBind = missingLocationAndBind.second;
+
+        // Work out which segment and section this missing bind was in
+        __block bool reportedError = false;
+        forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID, DylibStripMode stripMode,
+                            const std::vector<std::string> &dependencies, Diagnostics& dylibDiag, bool &stopDylib) {
+            intptr_t slide = ma->getSlide();
+            ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo &sectInfo,
+                                 bool malformedSectionRange, bool &stopSection) {
+                const uint8_t* content  = (uint8_t*)(sectInfo.sectAddr + slide);
+                const uint8_t* start    = (uint8_t*)content;
+                const uint8_t* end      = start + sectInfo.sectSize;
+                if ( (missingBindLoc >= start) && (missingBindLoc < end) ) {
+                    std::string segmentName = sectInfo.segInfo.segName;
+                    std::string sectionName = sectInfo.sectName;
+                    uint64_t sectionOffset = (missingBindLoc - start);
+
+                    dylibDiag.error("Failed to bind '%s' in '%s' (at offset 0x%llx in %s, %s) as "
+                                    "could not find a kext which exports this symbol",
+                                    missingBind.symbolName.c_str(), missingBind.binaryID.data(),
+                                    sectionOffset, segmentName.c_str(), sectionName.c_str());
+
+                    reportedError = true;
+                    stopSection = true;
+                    stopDylib = true;
+                }
+            });
+        });
+
+        if ( !reportedError ) {
+            _diagnostics.error("Failed to bind '%s' in '%s' as could not find a kext which exports this symbol",
+                               missingBind.symbolName.c_str(), missingBind.binaryID.data());
+        }
+    }
+
+    // If we had missing binds and reported no other errors, then generate an error to give the diagnostics something to track
+    if ( !missingBindLocations.empty() && _diagnostics.noError() ) {
+        _diagnostics.error("One or more binaries has an error which prevented linking.  See other errors.");
+    }
+
+    // FIXME: We could move dylibsToSymbolsOwner to a worker thread to be destroyed
+}
+
+namespace {
+
+class ByteBuffer {
+public:
+    ByteBuffer(uint8_t* storage, uintptr_t allocCount) {
+        buffer.setInitialStorage(storage, allocCount);
+    }
+
+    uint8_t* makeSpace(size_t bytesNeeded) {
+        // Make space in the buffer
+        for (size_t i = 0; i != bytesNeeded; ++i)
+            buffer.default_constuct_back();
+
+        // Grab a pointer to our position in the buffer
+        uint8_t* data = buffer.begin();
+
+        // Move the buffer to start after our data
+        dyld3::Array<uint8_t> newBuffer(buffer.end(), buffer.freeCount(), 0);
+        buffer = newBuffer;
+
+        return data;
+    };
+
+    const uint8_t* begin() const {
+        return buffer.begin();
+    }
+
+    const uint8_t* end() const {
+        return buffer.end();
+    }
+
+private:
+    dyld3::Array<uint8_t> buffer;
+};
+
+}
+
+void AppCacheBuilder::writeFixups()
+{
+    if ( fixupsSubRegion.sizeInUse == 0 )
+        return;
+
+    __block ByteBuffer byteBuffer(fixupsSubRegion.buffer, fixupsSubRegion.bufferSize);
+
+    // Keep track of where we put the fixups
+    const uint8_t* classicRelocsBufferStart = nullptr;
+    const uint8_t* classicRelocsBufferEnd = nullptr;
+
+    // If the kernel needs classic relocs, emit those first
+    CacheHeader64& header = cacheHeader;
+    if ( header.dynSymbolTable != nullptr ) {
+        classicRelocsBufferStart = byteBuffer.begin();
+
+        dyld3::MachOAnalyzer* cacheMA = (dyld3::MachOAnalyzer*)header.header;
+        __block uint64_t localRelocBaseAddress = 0;
+        cacheMA->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
+            if ( info.protections & VM_PROT_WRITE ) {
+                localRelocBaseAddress = info.vmAddr;
+                stop = true;
+            }
+        });
+
+        const std::vector<void*> allRebaseTargets = _aslrTracker.getRebaseTargets();
+
+        const dyld3::MachOAnalyzer* kernelMA = getKernelStaticExecutableFromCache();
+        kernelMA->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
+            std::vector<void*> segmentRebaseTargets;
+            uint64_t segmentVMOffset = info.vmAddr - cacheBaseAddress;
+            const uint8_t* segmentStartAddr = (const uint8_t*)(_fullAllocatedBuffer + segmentVMOffset);
+            const uint8_t* segmentEndAddr = (const uint8_t*)(segmentStartAddr + info.vmSize);
+            for (void* target : allRebaseTargets) {
+                if ( (target >= segmentStartAddr) && (target < segmentEndAddr) ) {
+                    segmentRebaseTargets.push_back(target);
+                }
+            }
+            std::sort(segmentRebaseTargets.begin(), segmentRebaseTargets.end());
+
+            for (void* target : segmentRebaseTargets) {
+                uint64_t targetSegmentOffset = (uint64_t)target - (uint64_t)segmentStartAddr;
+                //printf("Target: %s + 0x%llx: %p\n", info.segName, targetSegmentOffset, target);
+
+                uint64_t offsetFromBaseAddress = (info.vmAddr + targetSegmentOffset) - localRelocBaseAddress;
+                relocation_info* reloc = (relocation_info*)byteBuffer.makeSpace(sizeof(relocation_info));
+                reloc->r_address = (uint32_t)offsetFromBaseAddress;
+                reloc->r_symbolnum = 0;
+                reloc->r_pcrel = false;
+                reloc->r_length = 0;
+                reloc->r_extern = 0;
+                reloc->r_type = 0;
+
+                uint32_t vmAddr32 = 0;
+                uint64_t vmAddr64 = 0;
+                if ( _aslrTracker.hasRebaseTarget32(target, &vmAddr32) ) {
+                    reloc->r_length = 2;
+                    *(uint32_t*)target = vmAddr32;
+                } else if ( _aslrTracker.hasRebaseTarget64(target, &vmAddr64) ) {
+                    reloc->r_length = 3;
+                    *(uint64_t*)target = vmAddr64;
+                }
+            }
+
+            // Remove these fixups so that we don't also emit chained fixups for them
+            for (void* target : segmentRebaseTargets)
+                _aslrTracker.remove(target);
+        });
+
+        classicRelocsBufferEnd = byteBuffer.begin();
+    }
+
+    // TODO: 32-bit pointer format
+    assert(_is64);
+    const uint8_t currentLevel = getCurrentFixupLevel();
+
+    // We can have up to 4 levels in the fixup format.  These are the base addresses from
+    // which each level starts
+    BLOCK_ACCCESSIBLE_ARRAY(uint64_t, levelBaseAddresses, 4);
+    for (unsigned i = 0; i != numFixupLevels; ++i)
+        levelBaseAddresses[i] = 0;
+
+    levelBaseAddresses[currentLevel] = cacheBaseAddress;
+    if ( appCacheOptions.cacheKind != Options::AppCacheKind::kernel ) {
+        assert(existingKernelCollection != nullptr);
+        // The auxKC is mapped with __DATA first, so we need to get either the __DATA or __TEXT depending on what is earliest
+        __block uint64_t baseAddress = ~0ULL;
+        existingKernelCollection->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) {
+            baseAddress = std::min(baseAddress, info.vmAddr);
+        });
+        levelBaseAddresses[0] = baseAddress;
+    }
+
+    if ( pageableKernelCollection != nullptr ) {
+        // We may have __DATA first, so we need to get either the __DATA or __TEXT depending on what is earliest
+        __block uint64_t baseAddress = ~0ULL;
+        pageableKernelCollection->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) {
+            baseAddress = std::min(baseAddress, info.vmAddr);
+        });
+        uint8_t fixupLevel = getFixupLevel(Options::AppCacheKind::pageableKC);
+        levelBaseAddresses[fixupLevel] = baseAddress;
+    }
+
+    // We have a dyld_chained_starts_in_segment plus an offset for each page
+    struct SegmentFixups {
+        //const Region* region                            = nullptr;
+        uint8_t*                        segmentBuffer       = nullptr;
+        uint64_t                        segmentIndex        = 0;
+        uint64_t                        unslidLoadAddress   = 0;
+        uint64_t                        sizeInUse           = 0;
+        dyld_chained_starts_in_segment* starts              = nullptr;
+        uint64_t                        startsByteSize      = 0;
+        uint64_t                        numPagesToFixup     = 0;
+    };
+
+    auto buildChainedFixups = ^(uint64_t baseAddress, uint64_t segmentCount, std::vector<SegmentFixups>& startsInSegments) {
+
+        const uint8_t* chainedFixupsBufferStart = nullptr;
+        const uint8_t* chainedFixupsBufferEnd = nullptr;
+
+        chainedFixupsBufferStart = byteBuffer.begin();
+
+        // Start with dyld_chained_fixups_header which is fixed size
+        dyld_chained_fixups_header* fixupsHeader = (dyld_chained_fixups_header*)byteBuffer.makeSpace(sizeof(dyld_chained_fixups_header));
+
+        // We have a dyld_chained_starts_in_image plus an offset for each segment
+        dyld_chained_starts_in_image* startsInImage = (dyld_chained_starts_in_image*)byteBuffer.makeSpace(sizeof(dyld_chained_starts_in_image) + (segmentCount * sizeof(uint32_t)));
+
+        const uint8_t* endOfStarts = nullptr;
+        for (SegmentFixups& segmentFixups : startsInSegments) {
+            uint64_t startsInSegmentByteSize = sizeof(dyld_chained_starts_in_segment) + (segmentFixups.numPagesToFixup * sizeof(uint16_t));
+            dyld_chained_starts_in_segment* startsInSegment = (dyld_chained_starts_in_segment*)byteBuffer.makeSpace(startsInSegmentByteSize);
+            endOfStarts = (const uint8_t*)startsInSegment + startsInSegmentByteSize;
+
+            segmentFixups.starts            = startsInSegment;
+            segmentFixups.startsByteSize    = startsInSegmentByteSize;
+        }
+
+        // Starts in image
+        startsInImage->seg_count        = (uint32_t)segmentCount;
+        for (uint32_t segmentIndex = 0; segmentIndex != segmentCount; ++segmentIndex) {
+            startsInImage->seg_info_offset[segmentIndex] = 0;
+        }
+        for (const SegmentFixups& segmentFixups : startsInSegments) {
+            dyld_chained_starts_in_segment* startsInSegment = segmentFixups.starts;
+            uint64_t segmentIndex = segmentFixups.segmentIndex;
+            assert(segmentIndex < segmentCount);
+            assert(startsInImage->seg_info_offset[segmentIndex] == 0);
+            startsInImage->seg_info_offset[segmentIndex] = (uint32_t)((uint8_t*)startsInSegment - (uint8_t*)startsInImage);
+        }
+
+        const unsigned chainedPointerStride = dyld3::MachOAnalyzer::ChainedFixupPointerOnDisk::strideSize(chainedPointerFormat);
+
+        // Starts in segment
+        for (const SegmentFixups& segmentFixups : startsInSegments) {
+            dyld_chained_starts_in_segment* startsInSegment = segmentFixups.starts;
+            startsInSegment->size               = (uint32_t)segmentFixups.startsByteSize;
+            startsInSegment->page_size          = fixupsPageSize();
+            startsInSegment->pointer_format     = chainedPointerFormat;
+            startsInSegment->segment_offset     = segmentFixups.unslidLoadAddress - baseAddress;
+            startsInSegment->max_valid_pointer  = 0; // FIXME: Needed in 32-bit only
+            startsInSegment->page_count         = (segmentFixups.sizeInUse + startsInSegment->page_size - 1) / startsInSegment->page_size;
+            for (uint64_t pageIndex = 0; pageIndex != startsInSegment->page_count; ++pageIndex) {
+                startsInSegment->page_start[pageIndex] = DYLD_CHAINED_PTR_START_NONE;
+                uint8_t* lastLoc = nullptr;
+                // Note we always walk in 1-byte at a time as x86_64 has unaligned fixups
+                for (uint64_t pageOffset = 0; pageOffset != startsInSegment->page_size; pageOffset += 1) {
+                    uint8_t* fixupLoc = segmentFixups.segmentBuffer + (pageIndex * startsInSegment->page_size) + pageOffset;
+                    uint8_t fixupLevel = currentLevel;
+                    if ( !_aslrTracker.has(fixupLoc, &fixupLevel) )
+                        continue;
+                    assert((pageOffset % chainedPointerStride) == 0);
+                    if ( lastLoc ) {
+                        // Patch last loc to point here
+                        assert(_is64);
+                        dyld_chained_ptr_64_kernel_cache_rebase* lastLocBits = (dyld_chained_ptr_64_kernel_cache_rebase*)lastLoc;
+                        assert(lastLocBits->next == 0);
+                        uint64_t next = (fixupLoc - lastLoc) / chainedPointerStride;
+                        lastLocBits->next = next;
+                        assert(lastLocBits->next == next && "next location truncated");
+                    } else {
+                        // First fixup on this page
+                        startsInSegment->page_start[pageIndex] = pageOffset;
+                    }
+                    lastLoc = fixupLoc;
+
+                    uint64_t targetVMAddr = *(uint64_t*)fixupLoc;
+
+                    uint8_t highByte = 0;
+                    if ( _aslrTracker.hasHigh8(fixupLoc, &highByte) ) {
+                        uint64_t tbi = (uint64_t)highByte << 56;
+                        targetVMAddr |= tbi;
+                    }
+
+                    assert(fixupLevel < numFixupLevels);
+                    uint64_t targetVMOffset = targetVMAddr - levelBaseAddresses[fixupLevel];
+
+                    // Pack the vmAddr on this location in to the fixup format
+                    dyld_chained_ptr_64_kernel_cache_rebase* locBits = (dyld_chained_ptr_64_kernel_cache_rebase*)fixupLoc;
+
+                    uint16_t diversity;
+                    bool     hasAddrDiv;
+                    uint8_t  key;
+                    if ( _aslrTracker.hasAuthData(fixupLoc, &diversity, &hasAddrDiv, &key) ) {
+                        locBits->target         = targetVMOffset;
+                        locBits->cacheLevel     = fixupLevel;
+                        locBits->diversity      = diversity;
+                        locBits->addrDiv        = hasAddrDiv;
+                        locBits->key            = key;
+                        locBits->next           = 0;
+                        locBits->isAuth         = 1;
+                        assert(locBits->target == targetVMOffset && "target truncated");
+                    }
+                    else {
+                        locBits->target         = targetVMOffset;
+                        locBits->cacheLevel     = fixupLevel;
+                        locBits->diversity      = 0;
+                        locBits->addrDiv        = 0;
+                        locBits->key            = 0;
+                        locBits->next           = 0;
+                        locBits->isAuth         = 0;
+                        assert(locBits->target == targetVMOffset && "target truncated");
+                    }
+                }
+            }
+        }
+
+        chainedFixupsBufferEnd = byteBuffer.begin();
+
+        // Header
+        fixupsHeader->fixups_version    = 0;
+        fixupsHeader->starts_offset     = (uint32_t)((uint8_t*)startsInImage - (uint8_t*)fixupsHeader);
+        fixupsHeader->imports_offset    = (uint32_t)((uint8_t*)chainedFixupsBufferEnd - (uint8_t*)fixupsHeader);
+        fixupsHeader->symbols_offset    = fixupsHeader->imports_offset;
+        fixupsHeader->imports_count     = 0;
+        fixupsHeader->imports_format    = DYLD_CHAINED_IMPORT; // The validate code wants a value here
+        fixupsHeader->symbols_format    = 0;
+
+        return std::make_pair(chainedFixupsBufferStart, chainedFixupsBufferEnd);
+    };
+
+    if ( fixupsArePerKext() ) {
+        // The pageableKC (and sometimes auxKC) has one LC_DYLD_CHAINED_FIXUPS per kext, not 1 total
+        forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID,
+                            DylibStripMode stripMode, const std::vector<std::string> &dependencies,
+                            Diagnostics& dylibDiag, bool &stop) {
+            uint64_t loadAddress = ma->preferredLoadAddress();
+
+            __block uint64_t                    numSegments = 0;
+            __block std::vector<SegmentFixups>  segmentFixups;
+            ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
+                // Third party kexts have writable __TEXT, so we need to add starts for all segments
+                // other than LINKEDIT
+                bool segmentCanHaveFixups = false;
+                if ( appCacheOptions.cacheKind == Options::AppCacheKind::pageableKC ) {
+                    segmentCanHaveFixups = (info.protections & VM_PROT_WRITE) != 0;
+                } else {
+                    // auxKC
+                    segmentCanHaveFixups = (strcmp(info.segName, "__LINKEDIT") != 0);
+                }
+
+                if ( segmentCanHaveFixups) {
+                    SegmentFixups segmentToFixup;
+                    segmentToFixup.segmentBuffer        = (uint8_t*)ma + (info.vmAddr - loadAddress);
+                    segmentToFixup.segmentIndex         = info.segIndex;
+                    segmentToFixup.unslidLoadAddress    = info.vmAddr;
+                    segmentToFixup.sizeInUse            = info.vmSize;
+                    segmentToFixup.starts               = nullptr;
+                    segmentToFixup.startsByteSize       = 0;
+                    segmentToFixup.numPagesToFixup      = numWritablePagesToFixup(info.vmSize);
+                    segmentFixups.push_back(segmentToFixup);
+                }
+
+                ++numSegments;
+            });
+
+
+            std::pair<const uint8_t*, const uint8_t*> chainedFixupsRange = buildChainedFixups(loadAddress,
+                                                                                              numSegments, segmentFixups);
+            const uint8_t* chainedFixupsBufferStart = chainedFixupsRange.first;
+            const uint8_t* chainedFixupsBufferEnd = chainedFixupsRange.second;
+
+            if ( chainedFixupsBufferStart != chainedFixupsBufferEnd ) {
+                // Add the load command to our file
+
+                uint64_t fixupsOffset = (uint64_t)chainedFixupsBufferStart - (uint64_t)fixupsSubRegion.buffer;
+                uint64_t fixupsSize = (uint64_t)chainedFixupsBufferEnd - (uint64_t)chainedFixupsBufferStart;
+
+                // 64-bit
+                assert(_is64);
+                typedef Pointer64<LittleEndian> P;
+
+                uint32_t freeSpace = ma->loadCommandsFreeSpace();
+                assert(freeSpace >= sizeof(macho_linkedit_data_command<P>));
+                uint8_t* endOfLoadCommands = (uint8_t*)ma + sizeof(macho_header<P>) + ma->sizeofcmds;
+
+                // update mach_header to account for new load commands
+                macho_header<P>* mh = (macho_header<P>*)ma;
+                mh->set_sizeofcmds(mh->sizeofcmds() + sizeof(macho_linkedit_data_command<P>));
+                mh->set_ncmds(mh->ncmds() + 1);
+
+                // Add the new load command
+                macho_linkedit_data_command<P>* cmd = (macho_linkedit_data_command<P>*)endOfLoadCommands;
+                cmd->set_cmd(LC_DYLD_CHAINED_FIXUPS);
+                cmd->set_cmdsize(sizeof(linkedit_data_command));
+                cmd->set_dataoff((uint32_t)(_readOnlyRegion.cacheFileOffset + _readOnlyRegion.sizeInUse + fixupsOffset));
+                cmd->set_datasize((uint32_t)fixupsSize);
+            }
+        });
+
+        // Also build chained fixups on the top level for the branch stub GOTs
+        // FIXME: We don't need numRegions() here, but instead just up to an including the RW region
+        uint64_t segmentCount = numRegions();
+        __block std::vector<SegmentFixups> segmentFixups;
+
+        if ( branchGOTsRegion.sizeInUse != 0 ) {
+            SegmentFixups segmentToFixup;
+            segmentToFixup.segmentBuffer        = branchGOTsRegion.buffer;
+            segmentToFixup.segmentIndex         = branchGOTsRegion.index;
+            segmentToFixup.unslidLoadAddress    = branchGOTsRegion.unslidLoadAddress;
+            segmentToFixup.sizeInUse            = branchGOTsRegion.sizeInUse;
+            segmentToFixup.starts               = nullptr;
+            segmentToFixup.startsByteSize       = 0;
+            segmentToFixup.numPagesToFixup      = numWritablePagesToFixup(branchGOTsRegion.bufferSize);
+            segmentFixups.push_back(segmentToFixup);
+        }
+
+        std::pair<const uint8_t*, const uint8_t*> chainedFixupsRange = buildChainedFixups(cacheHeaderRegion.unslidLoadAddress,
+                                                                                          segmentCount, segmentFixups);
+        const uint8_t* chainedFixupsBufferStart = chainedFixupsRange.first;
+        const uint8_t* chainedFixupsBufferEnd = chainedFixupsRange.second;
+
+        if ( chainedFixupsBufferStart != chainedFixupsBufferEnd ) {
+            uint64_t fixupsOffset = (uint64_t)chainedFixupsBufferStart - (uint64_t)fixupsSubRegion.buffer;
+            uint64_t fixupsSize = (uint64_t)chainedFixupsBufferEnd - (uint64_t)chainedFixupsBufferStart;
+            header.chainedFixups->dataoff = (uint32_t)_readOnlyRegion.cacheFileOffset + (uint32_t)_readOnlyRegion.sizeInUse + (uint32_t)fixupsOffset;;
+            header.chainedFixups->datasize = (uint32_t)fixupsSize;
+        }
+    } else {
+        // Build the chained fixups for just the kernel collection itself
+        // FIXME: We don't need numRegions() here, but instead just up to an including the RW region
+        uint64_t segmentCount = numRegions();
+        __block std::vector<SegmentFixups> segmentFixups;
+
+        auto addSegmentStarts = ^(const Region& region) {
+            SegmentFixups segmentToFixup;
+            segmentToFixup.segmentBuffer        = region.buffer;
+            segmentToFixup.segmentIndex         = region.index;
+            segmentToFixup.unslidLoadAddress    = region.unslidLoadAddress;
+            segmentToFixup.sizeInUse            = region.sizeInUse;
+            segmentToFixup.starts               = nullptr;
+            segmentToFixup.startsByteSize       = 0;
+            segmentToFixup.numPagesToFixup      = numWritablePagesToFixup(region.bufferSize);
+            segmentFixups.push_back(segmentToFixup);
+        };
+
+        if ( dataConstRegion.sizeInUse != 0 )
+            addSegmentStarts(dataConstRegion);
+        if ( branchGOTsRegion.sizeInUse != 0 )
+            addSegmentStarts(branchGOTsRegion);
+        if ( readWriteRegion.sizeInUse != 0 )
+            addSegmentStarts(readWriteRegion);
+        if ( hibernateRegion.sizeInUse != 0 )
+            addSegmentStarts(hibernateRegion);
+        for (const Region& region : nonSplitSegRegions) {
+            // Assume writable regions have fixups to emit
+            // Note, third party kext's have __TEXT fixups, so assume all of these have fixups
+            // LINKEDIT is already elsewhere
+            addSegmentStarts(region);
+        }
+
+        std::pair<const uint8_t*, const uint8_t*> chainedFixupsRange = buildChainedFixups(cacheHeaderRegion.unslidLoadAddress,
+                                                                                          segmentCount, segmentFixups);
+        const uint8_t* chainedFixupsBufferStart = chainedFixupsRange.first;
+        const uint8_t* chainedFixupsBufferEnd = chainedFixupsRange.second;
+
+        if ( chainedFixupsBufferStart != chainedFixupsBufferEnd ) {
+            uint64_t fixupsOffset = (uint64_t)chainedFixupsBufferStart - (uint64_t)fixupsSubRegion.buffer;
+            uint64_t fixupsSize = (uint64_t)chainedFixupsBufferEnd - (uint64_t)chainedFixupsBufferStart;
+            header.chainedFixups->dataoff = (uint32_t)_readOnlyRegion.cacheFileOffset + (uint32_t)_readOnlyRegion.sizeInUse + (uint32_t)fixupsOffset;;
+            header.chainedFixups->datasize = (uint32_t)fixupsSize;
+        }
+    }
+
+    // Move the fixups to the end of __LINKEDIT
+    if ( classicRelocsBufferStart != classicRelocsBufferEnd ) {
+        uint64_t fixupsOffset = (uint64_t)classicRelocsBufferStart - (uint64_t)fixupsSubRegion.buffer;
+        uint64_t fixupsSize = (uint64_t)classicRelocsBufferEnd - (uint64_t)classicRelocsBufferStart;
+        header.dynSymbolTable->locreloff = (uint32_t)_readOnlyRegion.cacheFileOffset + (uint32_t)_readOnlyRegion.sizeInUse + (uint32_t)fixupsOffset;
+        header.dynSymbolTable->nlocrel = (uint32_t)fixupsSize / sizeof(fixupsSize);
+    }
+
+    uint64_t fixupsSpace = (uint64_t)byteBuffer.end() - (uint64_t)fixupsSubRegion.buffer;
+
+    uint8_t* linkeditEnd = _readOnlyRegion.buffer + _readOnlyRegion.sizeInUse;
+    memcpy(linkeditEnd, fixupsSubRegion.buffer, fixupsSpace);
+    uint8_t* fixupsEnd = linkeditEnd + fixupsSpace;
+
+    _readOnlyRegion.sizeInUse += align(fixupsSpace, _is64 ? 3 : 2);
+    _readOnlyRegion.sizeInUse = align(_readOnlyRegion.sizeInUse, 14);
+    _readOnlyRegion.bufferSize = _readOnlyRegion.sizeInUse;
+
+    // Zero the alignment gap, just in case there's any unoptimized LINKEDIT in there
+    uint8_t* alignedBufferEnd = _readOnlyRegion.buffer + _readOnlyRegion.sizeInUse;
+    if ( fixupsEnd != alignedBufferEnd ){
+        memset(fixupsEnd, 0, alignedBufferEnd - fixupsEnd);
+    }
+
+#if 0
+    dyld3::MachOAnalyzer* cacheMA = (dyld3::MachOAnalyzer*)header.header;
+    uint64_t cachePreferredLoadAddress = cacheMA->preferredLoadAddress();
+    cacheMA->forEachRebase(_diagnostics, false, ^(uint64_t runtimeOffset, bool &stop) {
+        printf("Rebase: 0x%llx = 0x%llx\n", runtimeOffset, runtimeOffset + cachePreferredLoadAddress);
+    });
+#endif
+}
+
+void AppCacheBuilder::allocateBuffer()
+{
+    // Whether to order the regions __TEXT, __DATA, __LINKEDIT or __DATA, __TEXT, __LINKEDIT in VM address order
+    bool dataRegionFirstInVMOrder = false;
+    bool hibernateRegionFirstInVMOrder = false;
+    switch (appCacheOptions.cacheKind) {
+        case Options::AppCacheKind::none:
+            assert(0 && "Cache kind should have been set");
+            break;
+        case Options::AppCacheKind::kernel:
+            if ( hibernateAddress != 0 )
+                hibernateRegionFirstInVMOrder = true;
+            break;
+        case Options::AppCacheKind::pageableKC:
+            // There's no interesting ordering for the pageableKC
+            break;
+        case Options::AppCacheKind::kernelCollectionLevel2:
+            assert(0 && "Unimplemented");
+            break;
+        case Options::AppCacheKind::auxKC:
+            dataRegionFirstInVMOrder = true;
+            break;
+    }
+
+    // Count how many bytes we need from all our regions
+    __block uint64_t numRegionFileBytes = 0;
+    __block uint64_t numRegionVMBytes = 0;
+
+    std::vector<std::pair<Region*, uint64_t>> regions;
+    std::vector<std::pair<Region*, uint64_t>> regionsVMOrder;
+    std::map<const Region*, uint32_t> sectionsToAddToRegions;
+
+    if ( hibernateRegionFirstInVMOrder ) {
+        regionsVMOrder.push_back({ &hibernateRegion, numRegionVMBytes });
+        // Pad out the VM offset so that the cache header starts where the base address
+        // really should be
+        uint64_t paddedSize = cacheBaseAddress - hibernateAddress;
+        if ( hibernateRegion.bufferSize > paddedSize ) {
+            _diagnostics.error("Could not lay out __HIB segment");
+            return;
+        }
+        numRegionVMBytes = paddedSize;
+        // Set the base address to the hibernate address so that we actually put the
+        // hibernate segment there
+        cacheBaseAddress = hibernateAddress;
+
+        // Add a section too
+        sectionsToAddToRegions[&hibernateRegion] = 1;
+    } else if ( dataRegionFirstInVMOrder ) {
+        if ( prelinkInfoDict != nullptr ) {
+            numRegionVMBytes = align(numRegionVMBytes, 14);
+            regionsVMOrder.push_back({ &prelinkInfoRegion, numRegionVMBytes });
+            numRegionVMBytes += prelinkInfoRegion.bufferSize;
+        }
+        if ( readWriteRegion.sizeInUse != 0 ) {
+            numRegionVMBytes = align(numRegionVMBytes, 14);
+            regionsVMOrder.push_back({ &readWriteRegion, numRegionVMBytes });
+            numRegionVMBytes += readWriteRegion.bufferSize;
+        }
+    }
+
+    // Cache header
+    numRegionVMBytes = align(numRegionVMBytes, 14);
+    regions.push_back({ &cacheHeaderRegion, 0 });
+    regionsVMOrder.push_back({ &cacheHeaderRegion, numRegionVMBytes });
+
+    // Split seg __TEXT
+    {
+        // File offset
+        readOnlyTextRegion.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += readOnlyTextRegion.bufferSize;
+        regions.push_back({ &readOnlyTextRegion, 0 });
+        // VM offset
+        numRegionVMBytes = align(numRegionVMBytes, 14);
+        regionsVMOrder.push_back({ &readOnlyTextRegion, numRegionVMBytes });
+        numRegionVMBytes += readOnlyTextRegion.bufferSize;
+
+        // Add a section too
+        sectionsToAddToRegions[&readOnlyTextRegion] = 1;
+    }
+
+    // Split seg __TEXT_EXEC
+    if ( readExecuteRegion.sizeInUse != 0 ) {
+        // File offset
+        readExecuteRegion.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += readExecuteRegion.bufferSize;
+        regions.push_back({ &readExecuteRegion, 0 });
+        // VM offset
+        numRegionVMBytes = align(numRegionVMBytes, 14);
+        regionsVMOrder.push_back({ &readExecuteRegion, numRegionVMBytes });
+        numRegionVMBytes += readExecuteRegion.bufferSize;
+    }
+
+    // __BRANCH_STUBS
+    if ( branchStubsRegion.bufferSize != 0 ) {
+        // File offset
+        branchStubsRegion.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += branchStubsRegion.bufferSize;
+        regions.push_back({ &branchStubsRegion, 0 });
+        // VM offset
+        numRegionVMBytes = align(numRegionVMBytes, 14);
+        regionsVMOrder.push_back({ &branchStubsRegion, numRegionVMBytes });
+        numRegionVMBytes += branchStubsRegion.bufferSize;
+    }
+
+    // __DATA_CONST
+    if ( dataConstRegion.sizeInUse != 0 ) {
+        // File offset
+        dataConstRegion.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += dataConstRegion.bufferSize;
+        regions.push_back({ &dataConstRegion, 0 });
+        // VM offset
+        numRegionVMBytes = align(numRegionVMBytes, 14);
+        regionsVMOrder.push_back({ &dataConstRegion, numRegionVMBytes });
+        numRegionVMBytes += dataConstRegion.bufferSize;
+    }
+
+    // __BRANCH_GOTS
+    if ( branchGOTsRegion.bufferSize != 0 ) {
+        // File offset
+        branchGOTsRegion.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += branchGOTsRegion.bufferSize;
+        regions.push_back({ &branchGOTsRegion, 0 });
+        // VM offset
+        numRegionVMBytes = align(numRegionVMBytes, 14);
+        regionsVMOrder.push_back({ &branchGOTsRegion, numRegionVMBytes });
+        numRegionVMBytes += branchGOTsRegion.bufferSize;
+    }
+
+    // -sectcreate
+    // Align to 16k before we lay out all contiguous regions
+    numRegionFileBytes = align(numRegionFileBytes, 14);
+    for (CustomSegment& customSegment : customSegments) {
+        Region& region = *customSegment.parentRegion;
+
+        region.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += region.bufferSize;
+        regions.push_back({ &region, 0 });
+        // VM offset
+        // Note we can't align the vm offset in here
+        assert( (numRegionVMBytes % 4096) == 0);
+        regionsVMOrder.push_back({ &region, numRegionVMBytes });
+        numRegionVMBytes += region.bufferSize;
+
+        // Maybe add sections too
+        uint32_t sectionsToAdd = 0;
+        if ( customSegment.sections.size() > 1 ) {
+            // More than one section, so they all need names
+            sectionsToAdd = (uint32_t)customSegment.sections.size();
+        } else if ( !customSegment.sections.front().sectionName.empty() ) {
+            // Only one section, but it has a name
+            sectionsToAdd = 1;
+        }
+        sectionsToAddToRegions[&region] = sectionsToAdd;
+    }
+    numRegionVMBytes = align(numRegionVMBytes, 14);
+
+    // __PRELINK_INFO
+    // Align to 16k
+    numRegionFileBytes = align(numRegionFileBytes, 14);
+    if ( prelinkInfoDict != nullptr )
+    {
+        // File offset
+        prelinkInfoRegion.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += prelinkInfoRegion.bufferSize;
+        regions.push_back({ &prelinkInfoRegion, 0 });
+
+        if ( !dataRegionFirstInVMOrder ) {
+            // VM offset
+            numRegionVMBytes = align(numRegionVMBytes, 14);
+            regionsVMOrder.push_back({ &prelinkInfoRegion, numRegionVMBytes });
+            numRegionVMBytes += prelinkInfoRegion.bufferSize;
+        }
+
+        // Add a section too
+        sectionsToAddToRegions[&prelinkInfoRegion] = 1;
+    }
+
+    // Split seg __DATA
+    // Align to 16k
+    numRegionFileBytes = align(numRegionFileBytes, 14);
+    if ( readWriteRegion.sizeInUse != 0 ) {
+        // File offset
+        readWriteRegion.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += readWriteRegion.bufferSize;
+        regions.push_back({ &readWriteRegion, 0 });
+
+        if ( !dataRegionFirstInVMOrder ) {
+            // VM offset
+            numRegionVMBytes = align(numRegionVMBytes, 14);
+            regionsVMOrder.push_back({ &readWriteRegion, numRegionVMBytes });
+            numRegionVMBytes += readWriteRegion.bufferSize;
+        }
+    }
+
+    // Split seg __HIB
+    // Align to 16k
+    numRegionFileBytes = align(numRegionFileBytes, 14);
+    if ( hibernateRegion.sizeInUse != 0 ) {
+        // File offset
+        hibernateRegion.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += hibernateRegion.bufferSize;
+        regions.push_back({ &hibernateRegion, 0 });
+
+        // VM offset was already handled earlier
+    }
+
+    // Non split seg regions
+    // Align to 16k before we lay out all contiguous regions
+    numRegionFileBytes = align(numRegionFileBytes, 14);
+    for (Region& region : nonSplitSegRegions) {
+        region.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += region.bufferSize;
+        regions.push_back({ &region, 0 });
+        // VM offset
+        // Note we can't align the vm offset in here
+        assert( (numRegionVMBytes % 4096) == 0);
+        regionsVMOrder.push_back({ &region, numRegionVMBytes });
+        numRegionVMBytes += region.bufferSize;
+    }
+    numRegionVMBytes = align(numRegionVMBytes, 14);
+
+    // __LINKEDIT
+    // Align to 16k
+    // File offset
+    numRegionFileBytes = align(numRegionFileBytes, 14);
+    _readOnlyRegion.cacheFileOffset = numRegionFileBytes;
+    numRegionFileBytes += _readOnlyRegion.bufferSize;
+    regions.push_back({ &_readOnlyRegion, 0 });
+    // VM offset
+    numRegionVMBytes = align(numRegionVMBytes, 14);
+    regionsVMOrder.push_back({ &_readOnlyRegion, numRegionVMBytes });
+    numRegionVMBytes += _readOnlyRegion.bufferSize;
+
+    // __LINKEDIT fixups sub region
+    // Align to 16k
+    numRegionFileBytes = align(numRegionFileBytes, 14);
+    if ( fixupsSubRegion.sizeInUse != 0 ) {
+        fixupsSubRegion.cacheFileOffset = numRegionFileBytes;
+        numRegionFileBytes += fixupsSubRegion.bufferSize;
+        //regions.push_back({ &fixupsSubRegion, 0 });
+
+        // VM offset
+        regionsVMOrder.push_back({ &fixupsSubRegion, numRegionVMBytes });
+        numRegionVMBytes += fixupsSubRegion.bufferSize;
+    }
+
+    const thread_command* unixThread = nullptr;
+    if (const DylibInfo* dylib = getKernelStaticExecutableInputFile()) {
+        unixThread = dylib->input->mappedFile.mh->unixThreadLoadCommand();
+    }
+
+    if (_is64) {
+
+        const uint64_t cacheHeaderSize  = sizeof(mach_header_64);
+        uint64_t cacheLoadCommandsSize  = 0;
+        uint64_t cacheNumLoadCommands   = 0;
+
+        // UUID
+        ++cacheNumLoadCommands;
+        uint64_t uuidOffset = cacheHeaderSize + cacheLoadCommandsSize;
+        cacheLoadCommandsSize += sizeof(uuid_command);
+
+        // BUILD VERSION
+        ++cacheNumLoadCommands;
+        uint64_t buildVersionOffset = cacheHeaderSize + cacheLoadCommandsSize;
+        cacheLoadCommandsSize += sizeof(build_version_command);
+
+        // UNIX THREAD
+        uint64_t unixThreadOffset = 0;
+        if ( unixThread != nullptr ) {
+            ++cacheNumLoadCommands;
+            unixThreadOffset = cacheHeaderSize + cacheLoadCommandsSize;
+            cacheLoadCommandsSize += unixThread->cmdsize;
+        }
+
+        // SYMTAB and DYSYMTAB
+        uint64_t symbolTableOffset = 0;
+        uint64_t dynSymbolTableOffset = 0;
+        if (const DylibInfo* dylib = getKernelStaticExecutableInputFile()) {
+            if ( dylib->input->mappedFile.mh->usesClassicRelocationsInKernelCollection() ) {
+                // SYMTAB
+                ++cacheNumLoadCommands;
+                symbolTableOffset = cacheHeaderSize + cacheLoadCommandsSize;
+                cacheLoadCommandsSize += sizeof(symtab_command);
+
+                // DYSYMTAB
+                ++cacheNumLoadCommands;
+                dynSymbolTableOffset = cacheHeaderSize + cacheLoadCommandsSize;
+                cacheLoadCommandsSize += sizeof(dysymtab_command);
+            }
+        }
+
+        // LC_DYLD_CHAINED_FIXUPS
+        // The pageableKC has one LC_DYLD_CHAINED_FIXUPS per kext, and 1 more on the top-level
+        // for the branch GOTs
+        uint64_t chainedFixupsOffset = 0;
+        if ( fixupsSubRegion.bufferSize != 0 ) {
+            ++cacheNumLoadCommands;
+            chainedFixupsOffset = cacheHeaderSize + cacheLoadCommandsSize;
+            cacheLoadCommandsSize += sizeof(linkedit_data_command);
+        }
+
+        // Add an LC_SEGMENT_64 for each region
+        for (auto& regionAndOffset : regions) {
+            ++cacheNumLoadCommands;
+            regionAndOffset.second = cacheHeaderSize + cacheLoadCommandsSize;
+            cacheLoadCommandsSize += sizeof(segment_command_64);
+
+            // Add space for any sections too
+            auto sectionIt = sectionsToAddToRegions.find(regionAndOffset.first);
+            if ( sectionIt != sectionsToAddToRegions.end() ) {
+                uint32_t numSections = sectionIt->second;
+                cacheLoadCommandsSize += sizeof(section_64) * numSections;
+            }
+        }
+
+        // Add an LC_FILESET_ENTRY for each dylib
+        std::vector<std::pair<const DylibInfo*, uint64_t>> dylibs;
+        for (const auto& dylib : sortedDylibs) {
+            ++cacheNumLoadCommands;
+            const char* dylibID = dylib.dylibID.c_str();
+            dylibs.push_back({ &dylib, cacheHeaderSize + cacheLoadCommandsSize });
+            uint64_t size = align(sizeof(fileset_entry_command) + strlen(dylibID) + 1, 3);
+            cacheLoadCommandsSize += size;
+        }
+
+        uint64_t cacheHeaderRegionSize = cacheHeaderSize + cacheLoadCommandsSize;
+
+        // Align the app cache header before the rest of the bytes
+        cacheHeaderRegionSize = align(cacheHeaderRegionSize, 14);
+
+        assert(numRegionFileBytes <= numRegionVMBytes);
+
+        _allocatedBufferSize = cacheHeaderRegionSize + numRegionVMBytes;
+
+        // The fixup format cannot handle a KC over 1GB (64MB for arm64e auxKC).  Error out if we exceed that
+        uint64_t cacheLimit = 1 << 30;
+        if ( (appCacheOptions.cacheKind == Options::AppCacheKind::auxKC) && (_options.archs == &dyld3::GradedArchs::arm64e) )
+            cacheLimit = 64 * (1 << 20);
+        if ( _allocatedBufferSize >= cacheLimit ) {
+            _diagnostics.error("kernel collection size exceeds maximum size of %lld vs actual size of %lld",
+                               cacheLimit, _allocatedBufferSize);
+            return;
+        }
+
+        if ( vm_allocate(mach_task_self(), &_fullAllocatedBuffer, _allocatedBufferSize, VM_FLAGS_ANYWHERE) != 0 ) {
+            _diagnostics.error("could not allocate buffer");
+            return;
+        }
+
+        // Assign region vm and buffer addresses now that we know the size of
+        // the cache header
+        {
+            // All vm offsets prior to the cache header are already correct
+            // All those after the cache header need to be shifted by the cache
+            // header size
+            bool seenCacheHeader = false;
+            for (const auto& regionAndVMOffset : regionsVMOrder) {
+                Region* region = regionAndVMOffset.first;
+                uint64_t vmOffset = regionAndVMOffset.second;
+                region->unslidLoadAddress = cacheBaseAddress + vmOffset;
+                if ( seenCacheHeader ) {
+                    // Shift by the cache header size
+                    region->unslidLoadAddress += cacheHeaderRegionSize;
+                } else {
+                    // The offset is correct but add in the base address
+                    seenCacheHeader = (region == &cacheHeaderRegion);
+                }
+                region->buffer = (uint8_t*)_fullAllocatedBuffer + (region->unslidLoadAddress - cacheBaseAddress);
+            }
+        }
+
+        // Cache header
+        cacheHeaderRegion.bufferSize            = cacheHeaderRegionSize;
+        cacheHeaderRegion.sizeInUse             = cacheHeaderRegion.bufferSize;
+        cacheHeaderRegion.cacheFileOffset       = 0;
+        cacheHeaderRegion.permissions           = VM_PROT_READ;
+        cacheHeaderRegion.name                  = "__TEXT";
+
+#if 0
+        for (const auto& regionAndVMOffset : regionsVMOrder) {
+            printf("0x%llx : %s\n", regionAndVMOffset.first->unslidLoadAddress, regionAndVMOffset.first->name.c_str());
+        }
+#endif
+
+        CacheHeader64& header = cacheHeader;
+        header.header = (mach_header_64*)cacheHeaderRegion.buffer;
+        header.numLoadCommands = cacheNumLoadCommands;
+        header.loadCommandsSize = cacheLoadCommandsSize;
+        header.uuid = (uuid_command*)(cacheHeaderRegion.buffer + uuidOffset);
+        header.buildVersion = (build_version_command*)(cacheHeaderRegion.buffer + buildVersionOffset);
+        if ( unixThread != nullptr ) {
+            header.unixThread = (thread_command*)(cacheHeaderRegion.buffer + unixThreadOffset);
+            // Copy the contents here while we have the source pointer available
+            memcpy(header.unixThread, unixThread, unixThread->cmdsize);
+        }
+
+        if ( symbolTableOffset != 0 ) {
+            header.symbolTable = (symtab_command*)(cacheHeaderRegion.buffer + symbolTableOffset);
+        }
+
+        if ( dynSymbolTableOffset != 0 ) {
+            header.dynSymbolTable = (dysymtab_command*)(cacheHeaderRegion.buffer + dynSymbolTableOffset);
+        }
+
+        if ( chainedFixupsOffset != 0 ) {
+            header.chainedFixups = (linkedit_data_command*)(cacheHeaderRegion.buffer + chainedFixupsOffset);
+        }
+
+        for (auto& regionAndOffset : regions) {
+            assert(regionAndOffset.first->permissions != 0);
+            segment_command_64* loadCommand = (segment_command_64*)(cacheHeaderRegion.buffer + regionAndOffset.second);
+            header.segments.push_back({ loadCommand, regionAndOffset.first });
+        }
+        for (const auto& dylibAndOffset : dylibs) {
+            fileset_entry_command* loadCommand = (fileset_entry_command*)(cacheHeaderRegion.buffer + dylibAndOffset.second);
+            header.dylibs.push_back({ loadCommand, dylibAndOffset.first });
+        }
+
+        // Move the offsets of all the other regions
+        // Split seg __TEXT
+        readOnlyTextRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+
+        // Split seg __TEXT_EXEC
+        readExecuteRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+
+        // __BRANCH_STUBS
+        branchStubsRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+
+        // Split seg __DATA_CONST
+        dataConstRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+
+        // __BRANCH_GOTS
+        branchGOTsRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+
+        // Split seg __DATA
+        readWriteRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+
+        // Split seg __HIB
+        hibernateRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+
+        // -sectcreate
+        for (Region& region : customDataRegions) {
+            region.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+        }
+
+        // Non split seg regions
+        for (Region& region : nonSplitSegRegions) {
+            region.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+        }
+
+        // __PRELINK_INFO
+        prelinkInfoRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+
+        // __LINKEDIT
+        _readOnlyRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+
+        // __LINKEDIT fixups sub region
+        fixupsSubRegion.cacheFileOffset += cacheHeaderRegion.sizeInUse;
+    } else {
+        assert(false);
+    }
+}
+
+void AppCacheBuilder::generateCacheHeader() {
+    if ( !_is64 )
+        assert(0 && "Unimplemented");
+
+    {
+        // 64-bit
+        typedef Pointer64<LittleEndian> P;
+        CacheHeader64& header = cacheHeader;
+
+        // Write the header
+        macho_header<P>* mh = (macho_header<P>*)header.header;
+        mh->set_magic(MH_MAGIC_64);
+        mh->set_cputype(_options.archs->_orderedCpuTypes[0].type);
+        mh->set_cpusubtype(_options.archs->_orderedCpuTypes[0].subtype);
+        mh->set_filetype(MH_FILESET);
+        mh->set_ncmds((uint32_t)header.numLoadCommands);
+        mh->set_sizeofcmds((uint32_t)header.loadCommandsSize);
+        mh->set_flags(0);
+        mh->set_reserved(0);
+
+        // FIXME: Move this to writeAppCacheHeader
+        {
+            macho_uuid_command<P>* cmd = (macho_uuid_command<P>*)header.uuid;
+            cmd->set_cmd(LC_UUID);
+            cmd->set_cmdsize(sizeof(uuid_command));
+            cmd->set_uuid((uuid_t){});
+        }
+
+        // FIXME: Move this to writeAppCacheHeader
+        {
+            macho_build_version_command<P>* cmd = (macho_build_version_command<P>*)header.buildVersion;
+            cmd->set_cmd(LC_BUILD_VERSION);
+            cmd->set_cmdsize(sizeof(build_version_command));
+            cmd->set_platform((uint32_t)_options.platform);
+            cmd->set_minos(0);
+            cmd->set_sdk(0);
+            cmd->set_ntools(0);
+        }
+
+        // FIXME: Move this to writeAppCacheHeader
+        // LC_UNIXTHREAD was already memcpy()'ed from the source dylib when we allocated space for it
+        // We still need to slide its PC value here before we lose the information about the slide
+        if ( header.unixThread != nullptr ) {
+            const DylibInfo* dylib = getKernelStaticExecutableInputFile();
+            const dyld3::MachOAnalyzer* ma = dylib->input->mappedFile.mh;
+            ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
+                uint64_t startAddress = dylib->input->mappedFile.mh->entryAddrFromThreadCmd(header.unixThread);
+                if ( (startAddress < info.vmAddr) || (startAddress >= (info.vmAddr + info.vmSize)) )
+                    return;
+
+                uint64_t segSlide = dylib->cacheLocation[info.segIndex].dstCacheUnslidAddress - info.vmAddr;
+                startAddress += segSlide;
+
+                macho_thread_command<P>* cmd = (macho_thread_command<P>*)header.unixThread;
+                cmd->set_thread_register(ma->entryAddrRegisterIndexForThreadCmd(), startAddress);
+
+                stop = true;
+            });
+        }
+
+        if ( header.symbolTable != nullptr ) {
+            macho_symtab_command<P>* cmd = (macho_symtab_command<P>*)header.symbolTable;
+            cmd->set_cmd(LC_SYMTAB);
+            cmd->set_cmdsize(sizeof(symtab_command));
+            cmd->set_symoff(0);
+            cmd->set_nsyms(0);
+            cmd->set_stroff(0);
+            cmd->set_strsize(0);
+        }
+
+        if ( header.dynSymbolTable != nullptr ) {
+            macho_dysymtab_command<P>* cmd = (macho_dysymtab_command<P>*)header.dynSymbolTable;
+            cmd->set_cmd(LC_DYSYMTAB);
+            cmd->set_cmdsize(sizeof(dysymtab_command));
+            cmd->set_ilocalsym(0);
+            cmd->set_nlocalsym(0);
+            cmd->set_iextdefsym(0);
+            cmd->set_nextdefsym(0);
+            cmd->set_iundefsym(0);
+            cmd->set_nundefsym(0);
+            cmd->set_tocoff(0);
+            cmd->set_ntoc(0);
+            cmd->set_modtaboff(0);
+            cmd->set_nmodtab(0);
+            cmd->set_extrefsymoff(0);
+            cmd->set_nextrefsyms(0);
+            cmd->set_indirectsymoff(0);
+            cmd->set_nindirectsyms(0);
+            cmd->set_extreloff(0);
+            cmd->set_nextrel(0);
+            cmd->set_locreloff(0);
+            cmd->set_nlocrel(0);
+        }
+
+        if ( header.chainedFixups != nullptr ) {
+            macho_linkedit_data_command<P>* cmd = (macho_linkedit_data_command<P>*)header.chainedFixups;
+            cmd->set_cmd(LC_DYLD_CHAINED_FIXUPS);
+            cmd->set_cmdsize(sizeof(linkedit_data_command));
+            cmd->set_dataoff(0);
+            cmd->set_datasize(0);
+        }
+
+        // FIXME: Move this to writeAppCacheHeader
+        uint64_t segmentIndex = 0;
+        for (CacheHeader64::SegmentCommandAndRegion& cmdAndInfo : header.segments) {
+            macho_segment_command<P>* cmd = (macho_segment_command<P>*)cmdAndInfo.first;
+            Region* region = cmdAndInfo.second;
+            region->index = segmentIndex;
+            ++segmentIndex;
+
+            assert(region->permissions != 0);
+
+            const char* name = region->name.c_str();
+
+            cmd->set_cmd(LC_SEGMENT_64);
+            cmd->set_cmdsize(sizeof(segment_command_64));
+            cmd->set_segname(name);
+            cmd->set_vmaddr(region->unslidLoadAddress);
+            cmd->set_vmsize(region->sizeInUse);
+            cmd->set_fileoff(region->cacheFileOffset);
+            cmd->set_filesize(region->sizeInUse);
+            cmd->set_maxprot(region->permissions);
+            cmd->set_initprot(region->permissions);
+            cmd->set_nsects(0);
+            cmd->set_flags(0);
+
+            if ( region == &readOnlyTextRegion ) {
+                // __PRELINK_TEXT should also get a section
+                cmd->set_cmdsize(cmd->cmdsize() + sizeof(section_64));
+                cmd->set_nsects(1);
+
+                macho_section<P>* section = (macho_section<P>*)((uint64_t)cmd + sizeof(*cmd));
+                section->set_sectname("__text");
+                section->set_segname(name);
+                section->set_addr(region->unslidLoadAddress);
+                section->set_size(region->sizeInUse);
+                section->set_offset((uint32_t)region->cacheFileOffset);
+                section->set_align(0);
+                section->set_reloff(0);
+                section->set_nreloc(0);
+                section->set_flags(S_REGULAR | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS);
+                section->set_reserved1(0);
+                section->set_reserved2(0);
+            } else if ( region == &prelinkInfoRegion ) {
+                // __PRELINK_INFO should also get a section
+                cmd->set_cmdsize(cmd->cmdsize() + sizeof(section_64));
+                cmd->set_nsects(1);
+
+                macho_section<P>* section = (macho_section<P>*)((uint64_t)cmd + sizeof(*cmd));
+                section->set_sectname("__info");
+                section->set_segname(name);
+                section->set_addr(region->unslidLoadAddress);
+                section->set_size(region->sizeInUse);
+                section->set_offset((uint32_t)region->cacheFileOffset);
+                section->set_align(0);
+                section->set_reloff(0);
+                section->set_nreloc(0);
+                section->set_flags(S_REGULAR);
+                section->set_reserved1(0);
+                section->set_reserved2(0);
+            } else if ( region == &hibernateRegion ) {
+                // __HIB should also get a section
+                cmd->set_cmdsize(cmd->cmdsize() + sizeof(section_64));
+                cmd->set_nsects(1);
+
+                macho_section<P>* section = (macho_section<P>*)((uint64_t)cmd + sizeof(*cmd));
+                section->set_sectname("__text");
+                section->set_segname(name);
+                section->set_addr(region->unslidLoadAddress);
+                section->set_size(region->sizeInUse);
+                section->set_offset((uint32_t)region->cacheFileOffset);
+                section->set_align(0);
+                section->set_reloff(0);
+                section->set_nreloc(0);
+                section->set_flags(S_REGULAR | S_ATTR_SOME_INSTRUCTIONS);
+                section->set_reserved1(0);
+                section->set_reserved2(0);
+            } else {
+                // Custom segments may have sections
+                for (CustomSegment &customSegment : customSegments) {
+                    if ( region != customSegment.parentRegion )
+                        continue;
+
+                    // Found a segment for this region.  Now work out how many sections to emit
+                    // Maybe add sections too
+                    uint32_t sectionsToAdd = 0;
+                    if ( customSegment.sections.size() > 1 ) {
+                        // More than one section, so they all need names
+                        sectionsToAdd = (uint32_t)customSegment.sections.size();
+                    } else if ( !customSegment.sections.front().sectionName.empty() ) {
+                        // Only one section, but it has a name
+                        sectionsToAdd = 1;
+                    } else {
+                        // Only 1 section, and it has no name, so don't add a section
+                        continue;
+                    }
+
+                    cmd->set_cmdsize(cmd->cmdsize() + (sizeof(section_64) * sectionsToAdd));
+                    cmd->set_nsects(sectionsToAdd);
+                    uint8_t* bufferPos = (uint8_t*)cmd + sizeof(*cmd);
+                    for (const CustomSegment::CustomSection& customSection : customSegment.sections) {
+                        macho_section<P>* section = (macho_section<P>*)bufferPos;
+                        section->set_sectname(customSection.sectionName.c_str());
+                        section->set_segname(name);
+                        section->set_addr(region->unslidLoadAddress + customSection.offsetInRegion);
+                        section->set_size(customSection.data.size());
+                        section->set_offset((uint32_t)(region->cacheFileOffset + customSection.offsetInRegion));
+                        section->set_align(0);
+                        section->set_reloff(0);
+                        section->set_nreloc(0);
+                        section->set_flags(S_REGULAR);
+                        section->set_reserved1(0);
+                        section->set_reserved2(0);
+
+                        bufferPos += sizeof(section_64);
+                    }
+                }
+            }
+        }
+
+        // Write the dylibs.  These are all we need for now to be able to walk the
+        // app cache
+        for (CacheHeader64::DylibCommandAndInfo& cmdAndInfo : header.dylibs) {
+            macho_fileset_entry_command<P>* cmd = (macho_fileset_entry_command<P>*)cmdAndInfo.first;
+            const DylibInfo* dylib = cmdAndInfo.second;
+
+            const char* dylibID = dylib->dylibID.c_str();
+            uint64_t size = align(sizeof(fileset_entry_command) + strlen(dylibID) + 1, 3);
+
+            cmd->set_cmd(LC_FILESET_ENTRY);
+            cmd->set_cmdsize((uint32_t)size);
+            cmd->set_vmaddr(dylib->cacheLocation[0].dstCacheUnslidAddress);
+            cmd->set_fileoff(dylib->cacheLocation[0].dstCacheFileOffset);
+            cmd->set_entry_id(dylibID);
+        }
+    }
+}
+
+void AppCacheBuilder::generatePrelinkInfo() {
+    if ( prelinkInfoDict == nullptr ) {
+        // The kernel doesn't need a prelink dictionary just for itself
+        bool needsPrelink = true;
+        if ( appCacheOptions.cacheKind == Options::AppCacheKind::kernel ) {
+            if ( sortedDylibs.size() == 1 )
+                needsPrelink = false;
+        }
+        if ( needsPrelink ) {
+            _diagnostics.error("Expected prelink info dictionary");
+        }
+        return;
+    }
+
+    CFMutableArrayRef arrayRef = (CFMutableArrayRef)CFDictionaryGetValue(prelinkInfoDict,
+                                                                         CFSTR("_PrelinkInfoDictionary"));
+    if ( arrayRef == nullptr ) {
+        _diagnostics.error("Expected prelink info dictionary array");
+        return;
+    }
+
+    typedef std::pair<const dyld3::MachOAnalyzer*, Diagnostics*> DylibAndDiag;
+    __block std::unordered_map<std::string_view, DylibAndDiag> dylibs;
+    forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID,
+                        DylibStripMode stripMode, const std::vector<std::string>& dependencies,
+                        Diagnostics& dylibDiag, bool& stop) {
+        dylibs[dylibID] = { ma, &dylibDiag };
+    });
+    for (const InputDylib& dylib : codelessKexts) {
+        dylibs[dylib.dylibID] = { nullptr, nullptr };
+    }
+
+    __block std::list<std::string> nonASCIIStrings;
+    auto getString = ^(Diagnostics& diags, CFStringRef symbolNameRef) {
+        const char* symbolName = CFStringGetCStringPtr(symbolNameRef, kCFStringEncodingUTF8);
+        if ( symbolName != nullptr )
+            return symbolName;
+
+        CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(symbolNameRef), kCFStringEncodingUTF8);
+        char buffer[len + 1];
+        if ( !CFStringGetCString(symbolNameRef, buffer, len, kCFStringEncodingUTF8) ) {
+            diags.error("Could not convert string to ASCII");
+            return (const char*)nullptr;
+        }
+        buffer[len] = '\0';
+        nonASCIIStrings.push_back(buffer);
+        return nonASCIIStrings.back().c_str();
+    };
+
+    bool badKext = false;
+    CFIndex arrayCount = CFArrayGetCount(arrayRef);
+    for (CFIndex i = 0; i != arrayCount; ++i) {
+        CFMutableDictionaryRef dictRef = (CFMutableDictionaryRef)CFArrayGetValueAtIndex(arrayRef, i);
+
+        CFStringRef bundleIDRef = (CFStringRef)CFDictionaryGetValue(dictRef, CFSTR("CFBundleIdentifier"));
+        if ( bundleIDRef == nullptr ) {
+            _diagnostics.error("Cannot get bundle ID for dylib");
+            return;
+        }
+
+        const char* bundleIDStr = getString(_diagnostics, bundleIDRef);
+        if ( _diagnostics.hasError() )
+            return;
+
+        auto dylibIt = dylibs.find(bundleIDStr);
+        if ( dylibIt == dylibs.end() ) {
+            _diagnostics.error("Cannot get dylib for bundle ID %s", bundleIDStr);
+            return;
+        }
+        const dyld3::MachOAnalyzer *ma = dylibIt->second.first;
+        Diagnostics* dylibDiag = dylibIt->second.second;
+        // Skip codeless kext's
+        if ( ma == nullptr )
+            continue;
+        uint64_t loadAddress = ma->preferredLoadAddress();
+
+        // _PrelinkExecutableLoadAddr
+        CFNumberRef loadAddrRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &loadAddress);
+        CFDictionarySetValue(dictRef, CFSTR("_PrelinkExecutableLoadAddr"), loadAddrRef);
+        CFRelease(loadAddrRef);
+
+        // _PrelinkExecutableSourceAddr
+        CFNumberRef sourceAddrRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &loadAddress);
+        CFDictionarySetValue(dictRef, CFSTR("_PrelinkExecutableSourceAddr"), sourceAddrRef);
+        CFRelease(sourceAddrRef);
+
+        // _PrelinkKmodInfo
+        __block uint64_t kmodInfoAddress = 0;
+
+        // Check for a global first
+        __block bool found = false;
+        {
+            dyld3::MachOAnalyzer::FoundSymbol foundInfo;
+            found = ma->findExportedSymbol(_diagnostics, "_kmod_info", true, foundInfo, nullptr);
+            if ( found ) {
+                kmodInfoAddress = loadAddress + foundInfo.value;
+            }
+        }
+        // And fall back to a local if we need to
+        if ( !found ) {
+            ma->forEachLocalSymbol(_diagnostics, ^(const char* aSymbolName, uint64_t n_value, uint8_t n_type,
+                                                   uint8_t n_sect, uint16_t n_desc, bool& stop) {
+                if ( strcmp(aSymbolName, "_kmod_info") == 0 ) {
+                    kmodInfoAddress = n_value;
+                    found = true;
+                    stop = true;
+                }
+            });
+        }
+
+        if ( found ) {
+            CFNumberRef kmodInfoAddrRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &kmodInfoAddress);
+            CFDictionarySetValue(dictRef, CFSTR("_PrelinkKmodInfo"), kmodInfoAddrRef);
+            CFRelease(kmodInfoAddrRef);
+
+            // Since we have a reference to the kmod info anyway, set its address field to the correct value
+            assert(_is64);
+            uint64_t kmodInfoVMOffset = kmodInfoAddress - loadAddress;
+            dyld3::MachOAppCache::KModInfo64_v1* kmodInfo = (dyld3::MachOAppCache::KModInfo64_v1*)((uint8_t*)ma + kmodInfoVMOffset);
+            if ( kmodInfo->info_version != 1 ) {
+                dylibDiag->error("unsupported kmod_info version of %d", kmodInfo->info_version);
+                badKext = true;
+                continue;
+            }
+            __block uint64_t textSegmnentVMAddr = 0;
+            __block uint64_t textSegmnentVMSize = 0;
+            ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
+                if ( !strcmp(info.segName, "__TEXT") ) {
+                    textSegmnentVMAddr = info.vmAddr;
+                    textSegmnentVMSize = info.vmSize;
+                    stop = true;
+                }
+            });
+            kmodInfo->address   = textSegmnentVMAddr;
+            kmodInfo->size      = textSegmnentVMSize;
+        }
+    }
+
+    CFErrorRef errorRef = nullptr;
+    CFDataRef xmlData = CFPropertyListCreateData(kCFAllocatorDefault, prelinkInfoDict,
+                                                 kCFPropertyListXMLFormat_v1_0, 0, &errorRef);
+    if (errorRef != nullptr) {
+        CFStringRef errorString = CFErrorCopyDescription(errorRef);
+        _diagnostics.error("Could not serialise plist because :%s",
+                           CFStringGetCStringPtr(errorString, kCFStringEncodingASCII));
+        CFRelease(xmlData);
+        CFRelease(errorRef);
+        return;
+    } else {
+        CFIndex xmlDataLength = CFDataGetLength(xmlData);
+        if ( xmlDataLength > prelinkInfoRegion.bufferSize ) {
+            _diagnostics.error("Overflow in prelink info segment.  0x%llx vs 0x%llx",
+                               (uint64_t)xmlDataLength, prelinkInfoRegion.bufferSize);
+            CFRelease(xmlData);
+            return;
+        }
+
+        // Write the prelink info in to the buffer
+        memcpy(prelinkInfoRegion.buffer, CFDataGetBytePtr(xmlData), xmlDataLength);
+        CFRelease(xmlData);
+    }
+
+    if ( badKext && _diagnostics.noError() ) {
+        _diagnostics.error("One or more binaries has an error which prevented linking.  See other errors.");
+    }
+}
+
+bool AppCacheBuilder::addCustomSection(const std::string& segmentName,
+                                       CustomSegment::CustomSection section) {
+    for (CustomSegment& segment: customSegments) {
+        if ( segment.segmentName != segmentName )
+            continue;
+
+        // Found a matching segment
+        // Make sure we don't have a section with this name already
+        if ( section.sectionName.empty() ) {
+            // We can't add a segment only section if other sections exist
+            _diagnostics.error("Cannot add empty section name with segment '%s' as other sections exist on that segment",
+                               segmentName.c_str());
+            return false;
+        }
+
+        for (const CustomSegment::CustomSection& existingSection : segment.sections) {
+            if ( existingSection.sectionName.empty() ) {
+                // We can't add a section with a name if an existing section exists with no name
+                _diagnostics.error("Cannot add section named '%s' with segment '%s' as segment has existing nameless section",
+                                   segmentName.c_str(), section.sectionName.c_str());
+                return false;
+            }
+            if ( existingSection.sectionName == section.sectionName ) {
+                // We can't add a section with the same name as an existing one
+                _diagnostics.error("Cannot add section named '%s' with segment '%s' as section already exists",
+                                   segmentName.c_str(), section.sectionName.c_str());
+                return false;
+            }
+        }
+        segment.sections.push_back(section);
+        return true;
+    }
+
+    // Didn't find a segment, so add a new one
+    CustomSegment segment;
+    segment.segmentName = segmentName;
+    segment.sections.push_back(section);
+    customSegments.push_back(segment);
+    return true;
+}
+
+void AppCacheBuilder::setExistingKernelCollection(const dyld3::MachOAppCache* appCacheMA) {
+    existingKernelCollection = appCacheMA;
+}
+
+void AppCacheBuilder::setExistingPageableKernelCollection(const dyld3::MachOAppCache* appCacheMA) {
+    pageableKernelCollection = appCacheMA;
+}
+
+void AppCacheBuilder::setExtraPrelinkInfo(CFDictionaryRef dictionary) {
+    extraPrelinkInfo = dictionary;
+}
+
+
+inline uint32_t absolutetime_to_milliseconds(uint64_t abstime)
+{
+    return (uint32_t)(abstime/1000/1000);
+}
+
+void AppCacheBuilder::buildAppCache(const std::vector<InputDylib>& dylibs)
+{
+    uint64_t t1 = mach_absolute_time();
+
+    // make copy of dylib list and sort
+    makeSortedDylibs(dylibs);
+
+    // Set the chained pointer format
+    // x86_64 uses unaligned fixups
+    if ( (_options.archs == &dyld3::GradedArchs::x86_64) || (_options.archs == &dyld3::GradedArchs::x86_64h) ) {
+        chainedPointerFormat = DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE;
+    } else {
+        chainedPointerFormat = DYLD_CHAINED_PTR_64_KERNEL_CACHE;
+    }
+
+    // If we have only codeless kexts, then error out
+    if ( sortedDylibs.empty() ) {
+        if ( codelessKexts.empty() ) {
+            _diagnostics.error("No binaries or codeless kexts were provided");
+        } else {
+            _diagnostics.error("Cannot build collection without binaries as only %lx codeless kexts provided",
+                               codelessKexts.size());
+        }
+        return;
+    }
+
+    // assign addresses for each segment of each dylib in new cache
+    assignSegmentRegionsAndOffsets();
+    if ( _diagnostics.hasError() )
+        return;
+
+    // allocate space used by largest possible cache plus room for LINKEDITS before optimization
+    allocateBuffer();
+    if ( _diagnostics.hasError() )
+        return;
+
+    assignSegmentAddresses();
+
+    generateCacheHeader();
+
+     // copy all segments into cache
+    uint64_t t2 = mach_absolute_time();
+    copyRawSegments();
+
+    // rebase all dylibs for new location in cache
+    uint64_t t3 = mach_absolute_time();
+    if ( appCacheOptions.cacheKind == Options::AppCacheKind::auxKC ) {
+        // We can have text fixups in the auxKC so ASLR should just track the whole buffer
+        __block const Region* firstDataRegion = nullptr;
+        __block const Region* lastDataRegion = nullptr;
+        forEachRegion(^(const Region &region) {
+            if ( (firstDataRegion == nullptr) || (region.buffer < firstDataRegion->buffer) )
+                firstDataRegion = &region;
+            if ( (lastDataRegion == nullptr) || (region.buffer > lastDataRegion->buffer) )
+                lastDataRegion = &region;
+        });
+
+        if ( firstDataRegion != nullptr ) {
+            uint64_t size = (lastDataRegion->buffer - firstDataRegion->buffer) + lastDataRegion->bufferSize;
+            _aslrTracker.setDataRegion(firstDataRegion->buffer, size);
+        }
+    } else {
+        const Region* firstDataRegion = nullptr;
+        const Region* lastDataRegion = nullptr;
+        if ( hibernateRegion.sizeInUse != 0 ) {
+            firstDataRegion = &hibernateRegion;
+            lastDataRegion  = &hibernateRegion;
+        }
+
+        if ( dataConstRegion.sizeInUse != 0 ) {
+            if ( firstDataRegion == nullptr )
+                firstDataRegion = &dataConstRegion;
+            if ( (lastDataRegion == nullptr) || (dataConstRegion.buffer > lastDataRegion->buffer) )
+                lastDataRegion = &dataConstRegion;
+        }
+
+        if ( branchGOTsRegion.bufferSize != 0 ) {
+            if ( firstDataRegion == nullptr )
+                firstDataRegion = &branchGOTsRegion;
+            if ( (lastDataRegion == nullptr) || (branchGOTsRegion.buffer > lastDataRegion->buffer) )
+                lastDataRegion = &branchGOTsRegion;
+        }
+
+        if ( readWriteRegion.sizeInUse != 0 ) {
+            // __DATA might be before __DATA_CONST in an auxKC
+            if ( (firstDataRegion == nullptr) || (readWriteRegion.buffer < firstDataRegion->buffer) )
+                firstDataRegion = &readWriteRegion;
+            if ( (lastDataRegion == nullptr) || (readWriteRegion.buffer > lastDataRegion->buffer) )
+                lastDataRegion = &readWriteRegion;
+        }
+
+        for (const Region& region : nonSplitSegRegions) {
+            // Assume writable regions have fixups to emit
+            // Note, third party kext's have __TEXT fixups, so assume all of these have fixups
+            // LINKEDIT is already elsewhere
+            if ( readWriteRegion.sizeInUse != 0 ) {
+                assert(region.buffer >= readWriteRegion.buffer);
+            }
+            if ( firstDataRegion == nullptr )
+                firstDataRegion = &region;
+            if ( (lastDataRegion == nullptr) || (region.buffer > lastDataRegion->buffer) )
+                lastDataRegion = &region;
+        }
+
+        if ( firstDataRegion != nullptr ) {
+            uint64_t size = (lastDataRegion->buffer - firstDataRegion->buffer) + lastDataRegion->bufferSize;
+            _aslrTracker.setDataRegion(firstDataRegion->buffer, size);
+        }
+    }
+    adjustAllImagesForNewSegmentLocations(cacheBaseAddress, _aslrTracker, nullptr, nullptr);
+    if ( _diagnostics.hasError() )
+        return;
+
+    // Once we have the final addresses, we can emit the prelink info segment
+    generatePrelinkInfo();
+    if ( _diagnostics.hasError() )
+        return;
+
+    // build ImageArray for dyld3, which has side effect of binding all cached dylibs
+    uint64_t t4 = mach_absolute_time();
+    processFixups();
+    if ( _diagnostics.hasError() )
+        return;
+
+    uint64_t t5 = mach_absolute_time();
+
+    // optimize away stubs
+    uint64_t t6 = mach_absolute_time();
+    {
+        __block std::vector<std::pair<const mach_header*, const char*>> images;
+        forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID,
+                            DylibStripMode stripMode, const std::vector<std::string>& dependencies,
+                            Diagnostics& dylibDiag, bool& stop) {
+            images.push_back({ ma, dylibID.c_str() });
+        });
+        // FIXME: Should we keep the same never stub eliminate symbols?  Eg, for gmalloc.
+        const char* const neverStubEliminateSymbols[] = {
+            nullptr
+        };
+
+        uint64_t cacheUnslidAddr = cacheBaseAddress;
+        int64_t cacheSlide = (long)_fullAllocatedBuffer - cacheUnslidAddr;
+        optimizeAwayStubs(images, cacheSlide, cacheUnslidAddr,
+                          nullptr, neverStubEliminateSymbols);
+    }
+
+    // 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
+    uint64_t t7 = mach_absolute_time();
+    {
+        __block std::vector<std::tuple<const mach_header*, const char*, DylibStripMode>> images;
+        __block std::set<const mach_header*> imagesToStrip;
+        __block const dyld3::MachOAnalyzer* kernelMA = nullptr;
+        forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID,
+                            DylibStripMode stripMode, const std::vector<std::string>& dependencies,
+                            Diagnostics& dylibDiag, bool& stop) {
+            if ( stripMode == DylibStripMode::stripNone ) {
+                // If the binary didn't have a strip mode, then use the global mode
+                switch (appCacheOptions.cacheKind) {
+                    case Options::AppCacheKind::none:
+                        assert("Unhandled kind");
+                        break;
+                    case Options::AppCacheKind::kernel:
+                        switch (appCacheOptions.stripMode) {
+                            case Options::StripMode::none:
+                                break;
+                            case Options::StripMode::all:
+                                stripMode = CacheBuilder::DylibStripMode::stripAll;
+                                break;
+                            case Options::StripMode::allExceptKernel:
+                                // Strip all binaries which are not the kernel
+                                if ( kernelMA == nullptr ) {
+                                    kernelMA = getKernelStaticExecutableFromCache();
+                                }
+                                if ( ma != kernelMA )
+                                    stripMode = CacheBuilder::DylibStripMode::stripAll;
+                                break;
+                        }
+                        break;
+                    case Options::AppCacheKind::pageableKC:
+                        assert("Unhandled kind");
+                        break;
+                    case Options::AppCacheKind::kernelCollectionLevel2:
+                        assert("Unhandled kind");
+                        break;
+                    case Options::AppCacheKind::auxKC:
+                        assert("Unhandled kind");
+                        break;
+                }
+            }
+            images.push_back({ ma, dylibID.c_str(), stripMode });
+        });
+        optimizeLinkedit(nullptr, images);
+
+        // update final readOnly region size
+        if ( !_is64 )
+            assert(0 && "Unimplemented");
+
+        {
+            // 64-bit
+            typedef Pointer64<LittleEndian> P;
+            CacheHeader64& header = cacheHeader;
+
+            for (CacheHeader64::SegmentCommandAndRegion& cmdAndRegion : header.segments) {
+                if (cmdAndRegion.second != &_readOnlyRegion)
+                    continue;
+                cmdAndRegion.first->vmsize      = _readOnlyRegion.sizeInUse;
+                cmdAndRegion.first->filesize    = _readOnlyRegion.sizeInUse;
+                break;
+            }
+        }
+    }
+
+    uint64_t t8 = mach_absolute_time();
+
+    uint64_t t9 = mach_absolute_time();
+
+    // Add fixups to rebase/bind the app cache
+    writeFixups();
+    {
+        if ( !_is64 )
+            assert(0 && "Unimplemented");
+
+        // update final readOnly region size
+        {
+            // 64-bit
+            typedef Pointer64<LittleEndian> P;
+            CacheHeader64& header = cacheHeader;
+
+            for (CacheHeader64::SegmentCommandAndRegion& cmdAndRegion : header.segments) {
+                if (cmdAndRegion.second != &_readOnlyRegion)
+                    continue;
+                cmdAndRegion.first->vmsize      = _readOnlyRegion.sizeInUse;
+                cmdAndRegion.first->filesize    = _readOnlyRegion.sizeInUse;
+                break;
+            }
+        }
+    }
+
+    // FIXME: We could move _aslrTracker to a worker thread to be destroyed as we don't need it
+    // after this point
+
+    uint64_t t10 = mach_absolute_time();
+
+    generateUUID();
+    if ( _diagnostics.hasError() )
+        return;
+
+    uint64_t t11 = mach_absolute_time();
+
+    if ( _options.verbose ) {
+        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 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));
+    }
+}
+
+void AppCacheBuilder::fipsSign()
+{
+    if ( appCacheOptions.cacheKind != Options::AppCacheKind::kernel )
+        return;
+
+    // find com.apple.kec.corecrypto in collection being built
+    __block const dyld3::MachOAnalyzer* kextMA = nullptr;
+    forEachCacheDylib(^(const dyld3::MachOAnalyzer *ma, const std::string &dylibID,
+                        DylibStripMode stripMode, const std::vector<std::string>& dependencies,
+                        Diagnostics& dylibDiag, bool& stop) {
+        if ( dylibID == "com.apple.kec.corecrypto" ) {
+            kextMA = ma;
+            stop = true;
+        }
+    });
+
+    if ( kextMA == nullptr ) {
+        _diagnostics.warning("Could not find com.apple.kec.corecrypto, skipping FIPS sealing");
+        return;
+    }
+
+    // find location in com.apple.kec.corecrypto to store hash of __text section
+    uint64_t hashStoreSize;
+    const void* hashStoreLocation = kextMA->findSectionContent("__TEXT", "__fips_hmacs", hashStoreSize);
+    if ( hashStoreLocation == nullptr ) {
+        _diagnostics.warning("Could not find __TEXT/__fips_hmacs section in com.apple.kec.corecrypto, skipping FIPS sealing");
+        return;
+    }
+    if ( hashStoreSize != 32 ) {
+        _diagnostics.warning("__TEXT/__fips_hmacs section in com.apple.kec.corecrypto is not 32 bytes in size, skipping FIPS sealing");
+        return;
+    }
+
+    // compute hmac hash of __text section.  It may be in __TEXT_EXEC or __TEXT
+    uint64_t textSize;
+    const void* textLocation = kextMA->findSectionContent("__TEXT", "__text", textSize);
+    if ( textLocation == nullptr ) {
+        textLocation = kextMA->findSectionContent("__TEXT_EXEC", "__text", textSize);
+    }
+    if ( textLocation == nullptr ) {
+        _diagnostics.warning("Could not find __TEXT/__text section in com.apple.kec.corecrypto, skipping FIPS sealing");
+        return;
+    }
+    unsigned char hmac_key = 0;
+    CCHmac(kCCHmacAlgSHA256, &hmac_key, 1, textLocation, textSize, (void*)hashStoreLocation); // store hash directly into hashStoreLocation
+}
+
+void AppCacheBuilder::generateUUID() {
+    uint8_t* uuidLoc = cacheHeader.uuid->uuid;
+    assert(uuid_is_null(uuidLoc));
+
+    CCDigestRef digestRef = CCDigestCreate(kCCDigestSHA256);
+    forEachRegion(^(const Region &region) {
+        if ( _diagnostics.hasError() )
+            return;
+        if ( region.sizeInUse == 0 )
+            return;
+        int result = CCDigestUpdate(digestRef, region.buffer, region.sizeInUse);
+        if ( result != 0 ) {
+            _diagnostics.error("Could not generate UUID: %d", result);
+            return;
+        }
+    });
+    if ( !_diagnostics.hasError() ) {
+        uint8_t buffer[CCDigestGetOutputSize(kCCDigestSHA256)];
+        int result = CCDigestFinal(digestRef, buffer);
+        memcpy(cacheHeader.uuid->uuid, buffer, sizeof(cacheHeader.uuid->uuid));
+        if ( result != 0 ) {
+            _diagnostics.error("Could not finalize UUID: %d", result);
+        }
+    }
+    CCDigestDestroy(digestRef);
+    if ( _diagnostics.hasError() )
+        return;
+
+    // Update the prelink info dictionary too
+    if ( prelinkInfoDict != nullptr ) {
+        CFDataRef dataRef = CFDataCreate(kCFAllocatorDefault, &cacheHeader.uuid->uuid[0], sizeof(cacheHeader.uuid->uuid));
+        CFDictionarySetValue(prelinkInfoDict, CFSTR("_PrelinkKCID"), dataRef);
+        CFRelease(dataRef);
+
+        CFErrorRef errorRef = nullptr;
+        CFDataRef xmlData = CFPropertyListCreateData(kCFAllocatorDefault, prelinkInfoDict,
+                                                     kCFPropertyListXMLFormat_v1_0, 0, &errorRef);
+        if (errorRef != nullptr) {
+            CFStringRef errorString = CFErrorCopyDescription(errorRef);
+            _diagnostics.error("Could not serialise plist because :%s",
+                               CFStringGetCStringPtr(errorString, kCFStringEncodingASCII));
+            CFRelease(xmlData);
+            CFRelease(errorRef);
+            return;
+        } else {
+            CFIndex xmlDataLength = CFDataGetLength(xmlData);
+            if ( xmlDataLength > prelinkInfoRegion.bufferSize ) {
+                _diagnostics.error("Overflow in prelink info segment.  0x%llx vs 0x%llx",
+                                   (uint64_t)xmlDataLength, prelinkInfoRegion.bufferSize);
+                CFRelease(xmlData);
+                return;
+            }
+
+            // Write the prelink info in to the buffer
+            memcpy(prelinkInfoRegion.buffer, CFDataGetBytePtr(xmlData), xmlDataLength);
+            CFRelease(xmlData);
+        }
+    }
+}
+
+
+void AppCacheBuilder::writeFile(const std::string& path)
+{
+    std::string pathTemplate = path + "-XXXXXX";
+    size_t templateLen = strlen(pathTemplate.c_str())+2;
+    BLOCK_ACCCESSIBLE_ARRAY(char, pathTemplateSpace, templateLen);
+    strlcpy(pathTemplateSpace, pathTemplate.c_str(), templateLen);
+    int fd = mkstemp(pathTemplateSpace);
+    if ( fd == -1 ) {
+        _diagnostics.error("could not open file %s", pathTemplateSpace);
+        return;
+    }
+    uint64_t cacheFileSize = 0;
+    // FIXME: Do we ever need to avoid allocating space for zero fill?
+    cacheFileSize = _readOnlyRegion.cacheFileOffset + _readOnlyRegion.sizeInUse;
+
+    // set final cache file size (may help defragment file)
+    ::ftruncate(fd, cacheFileSize);
+
+    // Write the whole buffer
+    uint64_t writtenSize = pwrite(fd, (const uint8_t*)_fullAllocatedBuffer, cacheFileSize, 0);
+    if (writtenSize == cacheFileSize) {
+        ::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 whole file.  %lld bytes out of %lld were written",
+                           writtenSize, cacheFileSize);
+        return;
+    }
+    ::close(fd);
+    ::unlink(pathTemplateSpace);
+}
+
+void AppCacheBuilder::writeBuffer(uint8_t*& buffer, uint64_t& bufferSize) const {
+    bufferSize = _readOnlyRegion.cacheFileOffset + _readOnlyRegion.sizeInUse;
+    buffer = (uint8_t*)malloc(bufferSize);
+
+    forEachRegion(^(const Region &region) {
+        if ( region.sizeInUse == 0 )
+            return;
+        memcpy(buffer + region.cacheFileOffset, (const uint8_t*)region.buffer, region.sizeInUse);
+    });
+}
diff --git a/dyld3/shared-cache/AppCacheBuilder.h b/dyld3/shared-cache/AppCacheBuilder.h
new file mode 100644 (file)
index 0000000..07dba23
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * 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 AppCacheBuilder_h
+#define AppCacheBuilder_h
+
+#include "CacheBuilder.h"
+#include "MachOFileAbstraction.hpp"
+#include "MachOAppCache.h"
+
+#include <list>
+
+#include <CoreFoundation/CFDictionary.h>
+
+class AppCacheBuilder final : public CacheBuilder {
+public:
+    struct Options {
+        enum class AppCacheKind {
+            none,
+            kernel,                 // The base first party kernel collection with xnu and boot time kexts
+            pageableKC,              // Other first party kexts which are usually only for some HW, eg, different GPUs
+            kernelCollectionLevel2, // Placeholder until we find a use for this
+            auxKC,                  // Third party kexts, or Apple kexts updated out of band with the OS, if any
+        };
+
+        enum class StripMode {
+            none,           // Don't strip anything
+            all,            // Strip everything
+            allExceptKernel // Strip everything other than the static kernel
+        };
+
+        AppCacheKind    cacheKind = AppCacheKind::none;
+        StripMode       stripMode = StripMode::none;
+    };
+    AppCacheBuilder(const DyldSharedCache::CreateOptions& dyldCacheOptions, const Options& appCacheOptions,
+                    const dyld3::closure::FileSystem& fileSystem);
+    ~AppCacheBuilder() override final;
+
+    // Wraps up a loaded macho with the other information required by kext's, eg, the list of dependencies
+    struct InputDylib {
+        LoadedMachO                     dylib;
+        std::string                     dylibID;
+        std::vector<std::string>        dylibDeps;
+        CFDictionaryRef                 infoPlist   = nullptr;
+        std::string                     bundlePath;
+        Diagnostics*                    errors          = nullptr;
+        CacheBuilder::DylibStripMode    stripMode = CacheBuilder::DylibStripMode::stripNone;
+    };
+
+    // The payload for -sectcreate
+    struct CustomSegment {
+        struct CustomSection {
+            std::string                     sectionName;
+            std::vector<uint8_t>            data;
+            uint64_t                        offsetInRegion = 0;
+        };
+
+        std::string                 segmentName;
+        std::vector<CustomSection>  sections;
+        Region*                     parentRegion = nullptr;
+    };
+
+    bool                    addCustomSection(const std::string& segmentName,
+                                             CustomSegment::CustomSection section);
+    void                    setExistingKernelCollection(const dyld3::MachOAppCache* appCacheMA);
+    void                    setExistingPageableKernelCollection(const dyld3::MachOAppCache* appCacheMA);
+    void                    setExtraPrelinkInfo(CFDictionaryRef dictionary);
+    void                    buildAppCache(const std::vector<InputDylib>& dylibs);
+    void                    writeFile(const std::string& path);
+    void                    writeBuffer(uint8_t*& buffer, uint64_t& bufferSize) const;
+
+private:
+
+    enum {
+        // The fixup format can't support more than 3 levels right now
+        numFixupLevels = 4
+    };
+
+    struct AppCacheDylibInfo : CacheBuilder::DylibInfo
+    {
+        // From CacheBuilder::DylibInfo
+        // const LoadedMachO*              input;
+        // std::string                     dylibID;
+        // std::vector<SegmentMappingInfo> cacheLocation;
+
+        DylibStripMode                  stripMode       = DylibStripMode::stripNone;
+        std::vector<std::string>        dependencies;
+        CFDictionaryRef                 infoPlist       = nullptr;
+        Diagnostics*                    errors          = nullptr;
+        std::string                     bundlePath;
+    };
+
+    static_assert(std::is_move_constructible<AppCacheDylibInfo>::value);
+
+    void                    forEachDylibInfo(void (^callback)(const DylibInfo& dylib, Diagnostics& dylibDiag)) override final;
+
+    void                    forEachCacheDylib(void (^callback)(const dyld3::MachOAnalyzer* ma,
+                                                               const std::string& dylibID,
+                                                               DylibStripMode stripMode,
+                                                               const std::vector<std::string>& dependencies,
+                                                               Diagnostics& dylibDiag,
+                                                               bool& stop)) const;
+
+    const DylibInfo*                    getKernelStaticExecutableInputFile() const;
+    const dyld3::MachOAnalyzer*         getKernelStaticExecutableFromCache() const;
+
+    void                                makeSortedDylibs(const std::vector<InputDylib>& dylibs);
+    void                                allocateBuffer();
+    void                                assignSegmentRegionsAndOffsets();
+    void                                copyRawSegments();
+    void                                assignSegmentAddresses();
+    void                                generateCacheHeader();
+    void                                generatePrelinkInfo();
+    uint32_t                            getCurrentFixupLevel() const;
+    void                                processFixups();
+    void                                writeFixups();
+    void                                fipsSign();
+    void                                generateUUID();
+    uint64_t                            numRegions() const;
+    uint64_t                            numBranchRelocationTargets();
+    uint64_t                            fixupsPageSize() const;
+    uint64_t                            numWritablePagesToFixup(uint64_t numBytesToFixup) const;
+    bool                                fixupsArePerKext() const;
+    void                                forEachRegion(void (^callback)(const Region& region)) const;
+
+    Options                             appCacheOptions;
+    const dyld3::MachOAppCache*         existingKernelCollection = nullptr;
+    const dyld3::MachOAppCache*         pageableKernelCollection = nullptr;
+    CFDictionaryRef                     extraPrelinkInfo         = nullptr;
+
+    std::vector<AppCacheDylibInfo>      sortedDylibs;
+    std::vector<InputDylib>             codelessKexts;
+    std::vector<CustomSegment>          customSegments;
+    Region                              cacheHeaderRegion;
+    Region                              readExecuteRegion;
+    Region                              branchStubsRegion;
+    Region                              dataConstRegion;
+    Region                              branchGOTsRegion;
+    Region                              readWriteRegion;
+    Region                              hibernateRegion;
+    Region                              readOnlyTextRegion;
+    std::list<Region>                   customDataRegions;       // -sectcreate
+    Region                              prelinkInfoRegion;
+    std::list<Region>                   nonSplitSegRegions;
+    // Region                           _readOnlyRegion;        // This comes from the base class
+    Region                              fixupsSubRegion;        // This will be in the __LINKEDIT when we write the file
+
+    // This is the base address from the statically linked kernel, or 0 for other caches
+    uint64_t                            cacheBaseAddress = 0;
+    // For x86_64 only, we want to keep the address of the hibernate segment from xnu
+    // We'll ensure this is mapped lower than the cache base address
+    uint64_t                            hibernateAddress = 0;
+
+    uint16_t                            chainedPointerFormat = 0;
+
+    // The dictionary we ultimately store in the __PRELINK_INFO region
+    CFMutableDictionaryRef              prelinkInfoDict = nullptr;
+
+    // Cache header fields
+    // FIXME: 32-bit
+
+    struct CacheHeader64 {
+        typedef std::pair<segment_command_64*, Region*> SegmentCommandAndRegion;
+        typedef std::pair<fileset_entry_command*, const DylibInfo*> DylibCommandAndInfo;
+
+        mach_header_64*                         header              = nullptr;
+
+        uint64_t                                numLoadCommands     = 0;
+        uint64_t                                loadCommandsSize    = 0;
+
+        uuid_command*                           uuid                = nullptr;
+        build_version_command*                  buildVersion        = nullptr;
+        thread_command*                         unixThread          = nullptr;
+        symtab_command*                         symbolTable         = nullptr;
+        dysymtab_command*                       dynSymbolTable      = nullptr;
+        linkedit_data_command*                  chainedFixups       = nullptr;
+        std::vector<SegmentCommandAndRegion>    segments;
+        std::vector<DylibCommandAndInfo>        dylibs;
+    };
+
+    CacheHeader64 cacheHeader;
+};
+
+#endif /* AppCacheBuilder_h */
diff --git a/dyld3/shared-cache/Architectures.hpp b/dyld3/shared-cache/Architectures.hpp
new file mode 100644 (file)
index 0000000..2da76e5
--- /dev/null
@@ -0,0 +1,68 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 
+ *
+ * Copyright (c) 2005-2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#ifndef __ARCHITECTURES__
+#define __ARCHITECTURES__
+
+#include "FileAbstraction.hpp"
+
+
+//
+// Architectures
+//
+struct x86
+{
+       typedef Pointer32<LittleEndian>         P;
+       
+};
+
+struct x86_64
+{
+       typedef Pointer64<LittleEndian>         P;
+};
+
+struct arm
+{
+       typedef Pointer32<LittleEndian>         P;
+       
+};
+
+struct arm64
+{
+       typedef Pointer64<LittleEndian>         P;
+
+};
+
+struct arm64_32
+{
+       typedef Pointer32<LittleEndian>         P;
+
+};
+
+
+
+
+#endif // __ARCHITECTURES__
+
+
index 2e0d5e155c255e75de73d0297a8396d58ae57009..b625e9f818b1aff2e4e12fa8c8a1044ee80d707c 100644 (file)
@@ -28,7 +28,7 @@
 #include "DyldSharedCache.h"
 #include "CacheBuilder.h"
 #include "Diagnostics.h"
-
+#include "IMPCaches.hpp"
 
 CacheBuilder::CacheBuilder(const DyldSharedCache::CreateOptions& options, const dyld3::closure::FileSystem& fileSystem)
     : _options(options)
@@ -39,6 +39,9 @@ CacheBuilder::CacheBuilder(const DyldSharedCache::CreateOptions& options, const
 {
 }
 
+CacheBuilder::~CacheBuilder() {
+}
+
 
 std::string CacheBuilder::errorMessage()
 {
@@ -48,8 +51,9 @@ std::string CacheBuilder::errorMessage()
 void CacheBuilder::copyRawSegments()
 {
     const bool log = false;
-    dispatch_apply(_sortedDylibs.size(), DISPATCH_APPLY_AUTO, ^(size_t index) {
-        const DylibInfo& dylib = _sortedDylibs[index];
+    const bool logCFConstants = false;
+
+    forEachDylibInfo(^(const DylibInfo& dylib, Diagnostics& dylibDiag) {
         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.archs->name(), info.segName, info.copySegmentSize, info.srcSegment, info.dstSegment, info.dstCacheUnslidAddress, dylib.input->mappedFile.runtimePath.c_str());
@@ -57,7 +61,7 @@ void CacheBuilder::copyRawSegments()
         }
     });
 
-    // Copy the coalesced sections
+    // Copy the coalesced __TEXT sections
     const uint64_t numCoalescedSections = sizeof(CacheCoalescedText::SupportedSections) / sizeof(*CacheCoalescedText::SupportedSections);
     dispatch_apply(numCoalescedSections, DISPATCH_APPLY_AUTO, ^(size_t index) {
         const CacheCoalescedText::StringSection& cacheStringSection = _coalescedText.getSectionData(CacheCoalescedText::SupportedSections[index]);
@@ -67,23 +71,51 @@ void CacheBuilder::copyRawSegments()
         for (const auto& stringAndOffset : cacheStringSection.stringsToOffsets)
             ::memcpy(cacheStringSection.bufferAddr + stringAndOffset.second, stringAndOffset.first.data(), stringAndOffset.first.size() + 1);
     });
+
+    // Copy the coalesced CF sections
+    if ( _coalescedText.cfStrings.bufferSize != 0 ) {
+        uint8_t* dstBuffer = _coalescedText.cfStrings.bufferAddr;
+        uint64_t dstBufferVMAddr = _coalescedText.cfStrings.bufferVMAddr;
+        forEachDylibInfo(^(const DylibInfo& dylib, Diagnostics& dylibDiag) {
+            const char* segmentName = "__OBJC_CONST";
+            const char* sectionName = "__cfstring";
+            const DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& sectionData = dylib.textCoalescer.getSectionCoalescer(segmentName, sectionName);
+            if ( sectionData.empty() )
+                return;
+
+            uint64_t sectionContentSize = 0;
+            const void* sectionContent = dylib.input->mappedFile.mh->findSectionContent(segmentName, sectionName, sectionContentSize);
+            assert(sectionContent != nullptr);
+            assert(sectionContentSize != 0);
+            for (const auto& dylibOffsetAndCacheOffset : sectionData) {
+                uint64_t dylibOffset = dylibOffsetAndCacheOffset.first;
+                uint64_t cacheOffset = dylibOffsetAndCacheOffset.second;
+                if (logCFConstants) fprintf(stderr, "copy %s %s section %s (0x%08X bytes) to %p (logical addr 0x%llX)\n",
+                                            _options.archs->name(), segmentName, sectionName,
+                                            (uint32_t)DyldSharedCache::ConstantClasses::cfStringAtomSize, dstBuffer + cacheOffset, dstBufferVMAddr + cacheOffset);
+                ::memcpy(dstBuffer + cacheOffset, (const uint8_t*)sectionContent + dylibOffset, (size_t)DyldSharedCache::ConstantClasses::cfStringAtomSize);
+            }
+        });
+    }
 }
 
-void CacheBuilder::adjustAllImagesForNewSegmentLocations()
+void CacheBuilder::adjustAllImagesForNewSegmentLocations(uint64_t cacheBaseAddress,
+                                                         ASLR_Tracker& aslrTracker, LOH_Tracker* lohTracker,
+                                                         const CacheBuilder::CacheCoalescedText* coalescedText)
 {
-    __block std::vector<Diagnostics> diags;
-    diags.resize(_sortedDylibs.size());
-
     // Note this cannot to be done in parallel because the LOH Tracker and aslr tracker are not thread safe
-    for (size_t index = 0; index != _sortedDylibs.size(); ++index) {
-        const DylibInfo& dylib = _sortedDylibs[index];
-        adjustDylibSegments(dylib, diags[index]);
-    }
-    for (const Diagnostics& diag : diags) {
-        if ( diag.hasError() ) {
-            _diagnostics.error("%s", diag.errorMessage().c_str());
-            break;
-        }
+    __block bool badDylib = false;
+    forEachDylibInfo(^(const DylibInfo& dylib, Diagnostics& dylibDiag) {
+        if ( dylibDiag.hasError() )
+            return;
+        adjustDylibSegments(dylib, dylibDiag, cacheBaseAddress, aslrTracker,
+                            lohTracker, coalescedText);
+        if ( dylibDiag.hasError() )
+            badDylib = true;
+    });
+
+    if ( badDylib && !_diagnostics.hasError() ) {
+        _diagnostics.error("One or more binaries has an error which prevented linking.  See other errors.");
     }
 }
 
@@ -92,6 +124,10 @@ CacheBuilder::ASLR_Tracker::~ASLR_Tracker()
 {
     if ( _bitmap != nullptr )
         ::free(_bitmap);
+#if BUILDING_APP_CACHE_UTIL
+    if ( _cacheLevels != nullptr )
+        ::free(_cacheLevels);
+#endif
 }
 
 void CacheBuilder::ASLR_Tracker::setDataRegion(const void* rwRegionStart, size_t rwRegionSize)
@@ -99,17 +135,28 @@ void CacheBuilder::ASLR_Tracker::setDataRegion(const void* rwRegionStart, size_t
     _pageCount   = (unsigned)(rwRegionSize+_pageSize-1)/_pageSize;
     _regionStart = (uint8_t*)rwRegionStart;
     _regionEnd   = (uint8_t*)rwRegionStart + rwRegionSize;
-    _bitmap      = (bool*)calloc(_pageCount*(_pageSize/4)*sizeof(bool), 1);
+    _bitmap      = (bool*)calloc(_pageCount*(_pageSize/kMinimumFixupAlignment)*sizeof(bool), 1);
+#if BUILDING_APP_CACHE_UTIL
+    size_t cacheLevelsSize = (_pageCount*(_pageSize/kMinimumFixupAlignment)*sizeof(uint8_t));
+    _cacheLevels = (uint8_t*)malloc(cacheLevelsSize);
+    memset(_cacheLevels, (int)~0U, cacheLevelsSize);
+#endif
 }
 
-void CacheBuilder::ASLR_Tracker::add(void* loc)
+void CacheBuilder::ASLR_Tracker::add(void* loc, uint8_t level)
 {
     if (!_enabled)
         return;
     uint8_t* p = (uint8_t*)loc;
     assert(p >= _regionStart);
     assert(p < _regionEnd);
-    _bitmap[(p-_regionStart)/4] = true;
+    _bitmap[(p-_regionStart)/kMinimumFixupAlignment] = true;
+
+#if BUILDING_APP_CACHE_UTIL
+    if ( level != (uint8_t)~0U ) {
+        _cacheLevels[(p-_regionStart)/kMinimumFixupAlignment] = level;
+    }
+#endif
 }
 
 void CacheBuilder::ASLR_Tracker::remove(void* loc)
@@ -119,17 +166,28 @@ void CacheBuilder::ASLR_Tracker::remove(void* loc)
     uint8_t* p = (uint8_t*)loc;
     assert(p >= _regionStart);
     assert(p < _regionEnd);
-    _bitmap[(p-_regionStart)/4] = false;
+    _bitmap[(p-_regionStart)/kMinimumFixupAlignment] = false;
 }
 
-bool CacheBuilder::ASLR_Tracker::has(void* loc)
+bool CacheBuilder::ASLR_Tracker::has(void* loc, uint8_t* level) const
 {
     if (!_enabled)
         return true;
     uint8_t* p = (uint8_t*)loc;
     assert(p >= _regionStart);
     assert(p < _regionEnd);
-    return _bitmap[(p-_regionStart)/4];
+
+    if ( _bitmap[(p-_regionStart)/kMinimumFixupAlignment] ) {
+#if BUILDING_APP_CACHE_UTIL
+        if ( level != nullptr ) {
+            uint8_t levelValue = _cacheLevels[(p-_regionStart)/kMinimumFixupAlignment];
+            if ( levelValue != (uint8_t)~0U )
+                *level = levelValue;
+        }
+#endif
+        return true;
+    }
+    return false;
 }
 
 void CacheBuilder::ASLR_Tracker::setHigh8(void* p, uint8_t high8)
@@ -152,7 +210,7 @@ void CacheBuilder::ASLR_Tracker::setRebaseTarget64(void*p, uint64_t targetVMAddr
     _rebaseTarget64[p] = targetVMAddr;
 }
 
-bool CacheBuilder::ASLR_Tracker::hasHigh8(void* p, uint8_t* highByte)
+bool CacheBuilder::ASLR_Tracker::hasHigh8(void* p, uint8_t* highByte) const
 {
     auto pos = _high8Map.find(p);
     if ( pos == _high8Map.end() )
@@ -161,7 +219,7 @@ bool CacheBuilder::ASLR_Tracker::hasHigh8(void* p, uint8_t* highByte)
     return true;
 }
 
-bool CacheBuilder::ASLR_Tracker::hasAuthData(void* p, uint16_t* diversity, bool* hasAddrDiv, uint8_t* key)
+bool CacheBuilder::ASLR_Tracker::hasAuthData(void* p, uint16_t* diversity, bool* hasAddrDiv, uint8_t* key) const
 {
     auto pos = _authDataMap.find(p);
     if ( pos == _authDataMap.end() )
@@ -172,7 +230,7 @@ bool CacheBuilder::ASLR_Tracker::hasAuthData(void* p, uint16_t* diversity, bool*
     return true;
 }
 
-bool CacheBuilder::ASLR_Tracker::hasRebaseTarget32(void* p, uint32_t* vmAddr)
+bool CacheBuilder::ASLR_Tracker::hasRebaseTarget32(void* p, uint32_t* vmAddr) const
 {
     auto pos = _rebaseTarget32.find(p);
     if ( pos == _rebaseTarget32.end() )
@@ -181,7 +239,7 @@ bool CacheBuilder::ASLR_Tracker::hasRebaseTarget32(void* p, uint32_t* vmAddr)
     return true;
 }
 
-bool CacheBuilder::ASLR_Tracker::hasRebaseTarget64(void* p, uint64_t* vmAddr)
+bool CacheBuilder::ASLR_Tracker::hasRebaseTarget64(void* p, uint64_t* vmAddr) const
 {
     auto pos = _rebaseTarget64.find(p);
     if ( pos == _rebaseTarget64.end() )
@@ -190,46 +248,108 @@ bool CacheBuilder::ASLR_Tracker::hasRebaseTarget64(void* p, uint64_t* vmAddr)
     return true;
 }
 
+std::vector<void*> CacheBuilder::ASLR_Tracker::getRebaseTargets() const {
+    std::vector<void*> targets;
+    for (const auto& target : _rebaseTarget32)
+        targets.push_back(target.first);
+    for (const auto& target : _rebaseTarget64)
+        targets.push_back(target.first);
+    return targets;
+}
+
 ////////////////////////////  DylibTextCoalescer ////////////////////////////////////
 
-bool CacheBuilder::DylibTextCoalescer::sectionWasCoalesced(std::string_view sectionName) const {
+bool CacheBuilder::DylibTextCoalescer::segmentWasCoalesced(std::string_view segmentName) const {
+    if (segmentName.size() > 16)
+        segmentName = segmentName.substr(0, 16);
+
+    if ( segmentName == "__OBJC_CONST" ) {
+        return !cfStrings.empty();
+    }
+
+    return false;
+}
+
+bool CacheBuilder::DylibTextCoalescer::sectionWasCoalesced(std::string_view segmentName,
+                                                           std::string_view sectionName) const {
+    if (segmentName.size() > 16)
+        segmentName = segmentName.substr(0, 16);
     if (sectionName.size() > 16)
         sectionName = sectionName.substr(0, 16);
-    std::map<std::string_view, const DylibSectionOffsetToCacheSectionOffset*> supportedSections = {
-        { "__objc_classname", &objcClassNames },
-        { "__objc_methname", &objcMethNames },
-        { "__objc_methtype", &objcMethTypes }
-    };
-    auto it = supportedSections.find(sectionName);
-    if (it == supportedSections.end())
-        return false;
-    return !it->second->empty();
+
+    if ( segmentName == "__TEXT" ) {
+        std::map<std::string_view, const DylibSectionOffsetToCacheSectionOffset*> supportedSections = {
+            { "__objc_classname", &objcClassNames },
+            { "__objc_methname", &objcMethNames },
+            { "__objc_methtype", &objcMethTypes }
+        };
+        auto it = supportedSections.find(sectionName);
+        if (it == supportedSections.end())
+            return false;
+        return !it->second->empty();
+    }
+
+    if ( segmentName == "__OBJC_CONST" ) {
+        if ( sectionName == "__cfstring" ) {
+            return !cfStrings.empty();
+        }
+    }
+
+    return false;
 }
 
-CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& CacheBuilder::DylibTextCoalescer::getSectionCoalescer(std::string_view sectionName) {
+CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset&
+CacheBuilder::DylibTextCoalescer::getSectionCoalescer(std::string_view segmentName, std::string_view sectionName) {
+    if (segmentName.size() > 16)
+        segmentName = segmentName.substr(0, 16);
     if (sectionName.size() > 16)
         sectionName = sectionName.substr(0, 16);
-    std::map<std::string_view, DylibSectionOffsetToCacheSectionOffset*> supportedSections = {
-        { "__objc_classname", &objcClassNames },
-        { "__objc_methname", &objcMethNames },
-        { "__objc_methtype", &objcMethTypes }
-    };
-    auto it = supportedSections.find(sectionName);
-    assert(it != supportedSections.end());
-    return *it->second;
+
+    if ( segmentName == "__TEXT" ) {
+        std::map<std::string_view, DylibSectionOffsetToCacheSectionOffset*> supportedSections = {
+            { "__objc_classname", &objcClassNames },
+            { "__objc_methname", &objcMethNames },
+            { "__objc_methtype", &objcMethTypes }
+        };
+        auto it = supportedSections.find(sectionName);
+        assert(it != supportedSections.end());
+        return *it->second;
+    }
+
+    if ( segmentName == "__OBJC_CONST" ) {
+        if ( sectionName == "__cfstring" ) {
+            return cfStrings;
+        }
+    }
+
+    assert(false);
 }
 
-const CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& CacheBuilder::DylibTextCoalescer::getSectionCoalescer(std::string_view sectionName) const {
+const CacheBuilder::DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset&
+CacheBuilder::DylibTextCoalescer::getSectionCoalescer(std::string_view segmentName, std::string_view sectionName) const {
+    if (segmentName.size() > 16)
+        segmentName = segmentName.substr(0, 16);
     if (sectionName.size() > 16)
         sectionName = sectionName.substr(0, 16);
-    std::map<std::string_view, const DylibSectionOffsetToCacheSectionOffset*> supportedSections = {
-        { "__objc_classname", &objcClassNames },
-        { "__objc_methname", &objcMethNames },
-        { "__objc_methtype", &objcMethTypes }
-    };
-    auto it = supportedSections.find(sectionName);
-    assert(it != supportedSections.end());
-    return *it->second;
+
+    if ( segmentName == "__TEXT" ) {
+        std::map<std::string_view, const DylibSectionOffsetToCacheSectionOffset*> supportedSections = {
+            { "__objc_classname", &objcClassNames },
+            { "__objc_methname", &objcMethNames },
+            { "__objc_methtype", &objcMethTypes }
+        };
+        auto it = supportedSections.find(sectionName);
+        assert(it != supportedSections.end());
+        return *it->second;
+    }
+
+    if ( segmentName == "__OBJC_CONST" ) {
+        if ( sectionName == "__cfstring" ) {
+            return cfStrings;
+        }
+    }
+
+    assert(false);
 }
 
 ////////////////////////////  CacheCoalescedText ////////////////////////////////////
@@ -239,8 +359,11 @@ const char* CacheBuilder::CacheCoalescedText::SupportedSections[] = {
     "__objc_methtype",
 };
 
-void CacheBuilder::CacheCoalescedText::parseCoalescableText(const dyld3::MachOAnalyzer *ma,
-                                                            DylibTextCoalescer& textCoalescer) {
+void CacheBuilder::CacheCoalescedText::
+     parseCoalescableText(const dyld3::MachOAnalyzer* ma,
+                          DylibTextCoalescer& textCoalescer,
+                          const IMPCaches::SelectorMap& selectors,
+                          IMPCaches::HoleMap& selectorsHoleMap) {
     static const bool log = false;
 
     // We can only remove sections if we know we have split seg v2 to point to it
@@ -266,17 +389,20 @@ void CacheBuilder::CacheCoalescedText::parseCoalescableText(const dyld3::MachOAn
     const std::set<std::string_view> supportedSections(std::begin(SupportedSections), std::end(SupportedSections));
     int64_t slide = ma->getSlide();
 
+    bool isSelectorsSection = false;
     for (auto sectionInfoIt = textSectionInfos.rbegin(); sectionInfoIt != textSectionInfos.rend(); ++sectionInfoIt) {
         const std::string& sectionName = sectionInfoIt->first;
         const dyld3::MachOAnalyzer::SectionInfo& sectInfo = sectionInfoIt->second;
 
+        isSelectorsSection = (sectionName == "__objc_methname");
+
         // If we find a section we can't handle then stop here.  Hopefully we coalesced some from the end.
         if (supportedSections.find(sectionName) == supportedSections.end())
             break;
 
         StringSection& cacheStringSection = getSectionData(sectionName);
 
-        DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& sectionStringData = textCoalescer.getSectionCoalescer(sectionName);
+        DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& sectionStringData = textCoalescer.getSectionCoalescer("__TEXT", sectionName);
 
         // Walk the strings in this section
         const uint8_t* content = (uint8_t*)(sectInfo.sectAddr + slide);
@@ -284,26 +410,313 @@ void CacheBuilder::CacheCoalescedText::parseCoalescableText(const dyld3::MachOAn
         const char* end = s + sectInfo.sectSize;
         while ( s < end ) {
             std::string_view str = s;
-            auto itAndInserted = cacheStringSection.stringsToOffsets.insert({ str, cacheStringSection.bufferSize });
-            if (itAndInserted.second) {
-                // If we inserted the string then we need to include it in the total
-                cacheStringSection.bufferSize += str.size() + 1;
-                if (log)
-                    printf("Selector: %s -> %s\n", ma->installName(), s);
-            } else {
+            int cacheSectionOffset = 0;
+
+            auto it = cacheStringSection.stringsToOffsets.find(str);
+            if (it != cacheStringSection.stringsToOffsets.end()) {
                 // Debugging only.  If we didn't include the string then we saved that many bytes
                 cacheStringSection.savedSpace += str.size() + 1;
+                cacheSectionOffset = it->second;
+            } else if (isSelectorsSection) {
+                // If we are in the selectors section, we need to move
+                // the selectors in the selector map to their correct addresses,
+                // and fill the holes with the rest
+
+#if BUILDING_APP_CACHE_UTIL
+                cacheSectionOffset = cacheStringSection.bufferSize;
+#else
+                const IMPCaches::SelectorMap::UnderlyingMap & map = selectors.map;
+                IMPCaches::SelectorMap::UnderlyingMap::const_iterator selectorsIterator = map.find(str);
+                if (selectorsIterator != map.end()) {
+                    cacheSectionOffset = selectorsIterator->second->offset;
+                } else {
+                    cacheSectionOffset = selectorsHoleMap.addStringOfSize((unsigned)str.size() + 1);
+                }
+#endif
+                cacheStringSection.stringsToOffsets[str] = cacheSectionOffset;
+                uint32_t sizeAtLeast = cacheSectionOffset + (uint32_t)str.size() + 1;
+                if (cacheStringSection.bufferSize < sizeAtLeast) {
+                    cacheStringSection.bufferSize = sizeAtLeast;
+                }
+            } else {
+                auto itAndInserted = cacheStringSection.stringsToOffsets.insert({ str, cacheStringSection.bufferSize });
+                cacheSectionOffset = itAndInserted.first->second;
+                assert(itAndInserted.second);
+
+                cacheStringSection.bufferSize += str.size() + 1;
+                if (log) {
+                    printf("Selector: %s -> %s\n", ma->installName(), s);
+                }
             }
 
             // Now keep track of this offset in our source dylib as pointing to this offset
             uint32_t sourceSectionOffset = (uint32_t)((uint64_t)s - (uint64_t)content);
-            uint32_t cacheSectionOffset = itAndInserted.first->second;
             sectionStringData[sourceSectionOffset] = cacheSectionOffset;
             s += str.size() + 1;
         }
     }
 }
 
+void CacheBuilder::CacheCoalescedText::parseCFConstants(const dyld3::MachOAnalyzer *ma,
+                                                        DylibTextCoalescer &textCoalescer) {
+    static const bool log = false;
+
+    // FIXME: Re-enable this once we can correctly patch the shared cache
+    if ( ma != nullptr )
+        return;
+
+    if ( !ma->is64() )
+        return;
+
+    // We only support chained fixups as we need to rewrite binds/rebases after applying split seg
+    // and that is much easier with chained fixups than opcodes
+    if ( !ma->hasChainedFixupsLoadCommand() )
+        return;
+
+    // FIXME: Support DYLD_CHAINED_PTR_ARM64E_USERLAND once ld64 moves to it.
+    const uint16_t pointerFormat = ma->chainedPointerFormat();
+    if ( pointerFormat != DYLD_CHAINED_PTR_ARM64E )
+        return;
+
+    // We can only remove sections if we know we have split seg v2 to point to it
+    // Otherwise, a PC relative load in the __TEXT segment wouldn't know how to point to the new constants
+    // which are no longer in the same segment
+    if ( !ma->isSplitSegV2() )
+        return;
+
+    // We can only remove sections from the end of a segment, so cache them all and walk backwards.
+    __block std::vector<std::pair<std::string, dyld3::MachOAnalyzer::SectionInfo>> dataSectionInfos;
+    __block uint64_t cstringStartVMAddr = 0;
+    __block uint64_t cstringEndVMAddr = 0;
+    ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo &sectInfo, bool malformedSectionRange, bool &stop) {
+        if ( malformedSectionRange )
+            return;
+        if ( strcmp(sectInfo.segInfo.segName, "__OBJC_CONST") == 0 ) {
+            dataSectionInfos.push_back({ sectInfo.sectName, sectInfo });
+            return;
+        }
+        if ( strcmp(sectInfo.segInfo.segName, "__TEXT") == 0 ) {
+            if ( strcmp(sectInfo.sectName, "__cstring") == 0 ) {
+                if ( ( (sectInfo.sectFlags & SECTION_TYPE) == S_CSTRING_LITERALS ) ) {
+                    cstringStartVMAddr = sectInfo.sectAddr;
+                    cstringEndVMAddr = cstringStartVMAddr + sectInfo.sectSize;
+                }
+            }
+        }
+    });
+
+    // We need to clear the chained pointer fixups for the whole segment, so can only
+    // process any type of CF object if we can process them all
+    if ( dataSectionInfos.size() != 1 )
+        return;
+
+    if ( dataSectionInfos.front().first != "__cfstring" )
+        return;
+
+    if ( cstringStartVMAddr == 0 )
+        return;
+
+    const dyld3::MachOAnalyzer::SectionInfo& cfStringsSection = dataSectionInfos.back().second;
+
+    // A CFString is layed out in memory as
+    // {
+    //     uintptr_t   isa;
+    //     uint32_t    encoding;
+    //     uint32_t    padding;
+    //     uintptr_t   cstringData;
+    //     uintptr_t   cstringLength;
+    // }
+    const uint64_t cstringDataOffset = 16;
+    const char* className = cfStrings.isaClassName;
+    if ( cfStringsSection.sectSize % (uint32_t)DyldSharedCache::ConstantClasses::cfStringAtomSize ) {
+        // We don't support padding or any kind on the section
+        return;
+    }
+
+    uint64_t baseAddress = ma->preferredLoadAddress();
+
+    uint64_t startVMOffset = cfStringsSection.sectAddr - baseAddress;
+    uint64_t endVMOffset = startVMOffset + cfStringsSection.sectSize;
+
+    __block Diagnostics diag;
+
+    // Make sure no symbols are pointing in to this section
+    __block bool hasSymbols = false;
+    ma->forEachGlobalSymbol(diag, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) {
+        uint64_t vmOffset = n_value - baseAddress;
+        if ( vmOffset < startVMOffset )
+            return;
+        if ( vmOffset >= endVMOffset )
+            return;
+        // In range of our section
+        hasSymbols = true;
+        stop = true;
+    });
+    if ( diag.hasError() )
+        return;
+    if ( hasSymbols )
+        return;
+
+    ma->forEachLocalSymbol(diag, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) {
+        uint64_t vmOffset = n_value - baseAddress;
+        if ( vmOffset < startVMOffset )
+            return;
+        if ( vmOffset >= endVMOffset )
+            return;
+        // In range of our section
+        hasSymbols = true;
+        stop = true;
+    });
+    if ( diag.hasError() )
+        return;
+    if ( hasSymbols )
+        return;
+
+    ma->forEachExportedSymbol(diag, ^(const char *symbolName, uint64_t imageOffset, uint64_t flags, uint64_t other, const char *importName, bool &stop) {
+        if ( imageOffset < startVMOffset )
+            return;
+        if ( imageOffset >= endVMOffset )
+            return;
+        // In range of our section
+        hasSymbols = true;
+        stop = true;
+    });
+    if ( diag.hasError() )
+        return;
+    if ( hasSymbols )
+        return;
+
+    __block std::vector<const char*> dependentPaths;
+    ma->forEachDependentDylib(^(const char *loadPath, bool isWeak, bool isReExport,
+                                bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+        dependentPaths.push_back(loadPath);
+    });
+
+    // Find all the binds to the ISA class.  These delineate the atoms
+    // In CoreFoundation itself, we are looking for rebases to the ISA
+    __block std::vector<uint64_t> atomOffsets;
+
+    bool dylibExportsISA = strcmp(ma->installName(), cfStrings.isaInstallName) == 0;
+    if ( !dylibExportsISA ) {
+        // This dylib doens't export the class, so look for binds to the ISA
+        __block std::vector<std::pair<const char*, int>> bindTargetSymbols;
+        ma->forEachChainedFixupTarget(diag, ^(int libraryOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
+            bindTargetSymbols.push_back({ symbolName, libraryOrdinal });
+        });
+
+        __block bool foundBadBind = false;
+        ma->withChainStarts(diag, ma->chainStartsOffset(), ^(const dyld_chained_starts_in_image* startsInfo) {
+            if ( foundBadBind )
+                return;
+            ma->forEachFixupInAllChains(diag, startsInfo, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc,
+                                                                   const dyld_chained_starts_in_segment* segInfo, bool& stopFixups) {
+                // Skip anything not in this section
+                uint64_t vmOffset = (uint8_t*)fixupLoc - (uint8_t*)ma;
+                if ( vmOffset < startVMOffset )
+                    return;
+                if ( vmOffset >= endVMOffset )
+                    return;
+
+                uint32_t bindOrdinal;
+                int64_t  ptrAddend;
+                if ( fixupLoc->isBind(pointerFormat, bindOrdinal, ptrAddend) ) {
+                    if ( ptrAddend != 0 ) {
+                        foundBadBind = true;
+                        stopFixups = true;
+                        return;
+                    }
+                    if ( bindOrdinal >= bindTargetSymbols.size() ) {
+                        foundBadBind = true;
+                        stopFixups = true;
+                        return;
+                    }
+                    if ( strcmp(bindTargetSymbols[bindOrdinal].first, className) != 0 ) {
+                        foundBadBind = true;
+                        stopFixups = true;
+                        return;
+                    }
+                    int libOrdinal = bindTargetSymbols[bindOrdinal].second;
+                    if ( libOrdinal <= 0 ) {
+                        foundBadBind = true;
+                        stopFixups = true;
+                        return;
+                    }
+                    int depIndex = libOrdinal - 1;
+                    if ( depIndex >= dependentPaths.size() ) {
+                        foundBadBind = true;
+                        stopFixups = true;
+                        return;
+                    }
+                    const char* depLoadPath = dependentPaths[depIndex];
+                    // All dylibs must find the ISA in the same place
+                    if ( strcmp(cfStrings.isaInstallName, depLoadPath) != 0 ) {
+                        foundBadBind = true;
+                        stopFixups = true;
+                        return;
+                    }
+                    atomOffsets.push_back(vmOffset);
+                }
+            });
+        });
+        if ( foundBadBind )
+            return;
+
+        if ( atomOffsets.empty() )
+            return;
+    }
+
+    if ( diag.hasError() )
+        return;
+
+    // Find all the rebases in the atoms, which correpond to pointers strings
+    __block std::map<uint64_t, uint64_t> sectionRebases;
+    ma->withChainStarts(diag, ma->chainStartsOffset(), ^(const dyld_chained_starts_in_image* startsInfo) {
+        ma->forEachFixupInAllChains(diag, startsInfo, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stopFixups) {
+            // Skip anything not in this section
+            uint64_t vmOffset = (uint8_t*)fixupLoc - (uint8_t*)ma;
+            if ( vmOffset < startVMOffset )
+                return;
+            if ( vmOffset >= endVMOffset )
+                return;
+
+            uint64_t rebaseTargetRuntimeOffset;
+            if ( fixupLoc->isRebase(pointerFormat, 0, rebaseTargetRuntimeOffset) ) {
+                if ( dylibExportsISA && (rebaseTargetRuntimeOffset == cfStrings.isaVMOffset) ) {
+                    atomOffsets.push_back(vmOffset);
+                } else {
+                    sectionRebases[vmOffset] = rebaseTargetRuntimeOffset;
+                }
+            }
+        });
+    });
+    if ( diag.hasError() )
+        return;
+
+    // Every atom should have a single rebase to a cstring
+    if ( sectionRebases.size() != atomOffsets.size() )
+        return;
+
+    std::sort(atomOffsets.begin(), atomOffsets.end());
+    for (uint64_t atomOffset : atomOffsets) {
+        auto it = sectionRebases.find(atomOffset + cstringDataOffset);
+        if ( it == sectionRebases.end() )
+            return;
+    }
+
+    CFSection& stringSection = this->cfStrings;
+    DylibTextCoalescer::DylibSectionOffsetToCacheSectionOffset& sectionData = textCoalescer.getSectionCoalescer("__OBJC_CONST", "__cfstring");
+    for (uint64_t atomOffset : atomOffsets) {
+        if ( log )
+            printf("%s: found __cfstring at: 0x%llx\n", ma->installName(), atomOffset);
+
+        // Now keep track of this offset in our source dylib as pointing to this offset
+        uint32_t sourceSectionOffset = (uint32_t)(atomOffset - startVMOffset);
+        uint32_t cacheSectionOffset = stringSection.bufferSize;
+        sectionData[sourceSectionOffset] = cacheSectionOffset;
+        stringSection.bufferSize += (uint32_t)DyldSharedCache::ConstantClasses::cfStringAtomSize;
+    }
+}
+
 void CacheBuilder::CacheCoalescedText::clear() {
     *this = CacheBuilder::CacheCoalescedText();
 }
@@ -335,3 +748,67 @@ const CacheBuilder::CacheCoalescedText::StringSection& CacheBuilder::CacheCoales
     assert(it != supportedSections.end());
     return *it->second;
 }
+
+uint64_t CacheBuilder::CacheCoalescedText::getSectionVMAddr(std::string_view segmentName,
+                                                            std::string_view sectionName) const {
+    if (segmentName.size() > 16)
+        segmentName = segmentName.substr(0, 16);
+    if (sectionName.size() > 16)
+        sectionName = sectionName.substr(0, 16);
+
+    if ( segmentName == "__TEXT" ) {
+        return getSectionData(sectionName).bufferVMAddr;
+    }
+
+    if ( segmentName == "__OBJC_CONST" ) {
+        if ( sectionName == "__cfstring" ) {
+            return cfStrings.bufferVMAddr;
+        }
+    }
+
+    assert(false);
+}
+
+uint8_t* CacheBuilder::CacheCoalescedText::getSectionBufferAddr(std::string_view segmentName,
+                                                                std::string_view sectionName) const {
+    if (segmentName.size() > 16)
+        segmentName = segmentName.substr(0, 16);
+    if (sectionName.size() > 16)
+        sectionName = sectionName.substr(0, 16);
+
+    if ( segmentName == "__TEXT" ) {
+        return getSectionData(sectionName).bufferAddr;
+    }
+
+    if ( segmentName == "__OBJC_CONST" ) {
+        if ( sectionName == "__cfstring" ) {
+            return cfStrings.bufferAddr;
+        }
+    }
+
+    assert(false);
+}
+
+uint64_t CacheBuilder::CacheCoalescedText::getSectionObjcTag(std::string_view segmentName,
+                                                             std::string_view sectionName) const {
+    if (segmentName.size() > 16)
+        segmentName = segmentName.substr(0, 16);
+    if (sectionName.size() > 16)
+        sectionName = sectionName.substr(0, 16);
+
+    if ( segmentName == "__TEXT" ) {
+        // Nothing has a tag in __TEXT
+        return 0;
+    }
+
+    if ( segmentName == "__OBJC_CONST" ) {
+        if ( sectionName == "__cfstring" ) {
+            // This is defined by objc as the tag we put in the high bits
+            // FIXME: Get a tag from objc
+            // return 1ULL << 63;
+            return 0;
+        }
+    }
+
+    assert(false);
+}
index 8b2bdc3ca5566eb5f76c073d7eccd95653db370d..b8805de4b46a3f0a1c4d4d16f7cf47b64d7d29df 100644 (file)
@@ -35,8 +35,7 @@
 #include "DyldSharedCache.h"
 #include "Diagnostics.h"
 #include "MachOAnalyzer.h"
-
-
+#include "IMPCaches.hpp"
 
 template <typename P> class LinkeditOptimizer;
 
@@ -44,6 +43,7 @@ template <typename P> class LinkeditOptimizer;
 class CacheBuilder {
 public:
     CacheBuilder(const DyldSharedCache::CreateOptions& options, const dyld3::closure::FileSystem& fileSystem);
+    virtual ~CacheBuilder();
 
     struct InputFile {
         enum State {
@@ -71,6 +71,24 @@ public:
 
     std::string                                 errorMessage();
 
+    struct Region
+    {
+        uint8_t*    buffer                          = nullptr;
+        uint64_t    bufferSize                      = 0;
+        uint64_t    sizeInUse                       = 0;
+        uint64_t    unslidLoadAddress               = 0;
+        uint64_t    cacheFileOffset                 = 0;
+        uint8_t     permissions                     = 0;
+        std::string name;
+        uint64_t    index                  = ~0ULL; // The index of this region in the final binary
+
+        // Each region can optionally have its own slide info
+        uint8_t*    slideInfoBuffer                 = nullptr;
+        uint64_t    slideInfoBufferSizeAllocated    = 0;
+        uint64_t    slideInfoFileOffset             = 0;
+        uint64_t    slideInfoFileSize               = 0;
+    };
+
     struct SegmentMappingInfo {
         const void*     srcSegment;
         const char*     segName;
@@ -81,6 +99,8 @@ public:
         uint32_t        dstCacheFileSize;
         uint32_t        copySegmentSize;
         uint32_t        srcSegmentIndex;
+        // Used by the AppCacheBuilder to work out which one of the regions this segment is in
+        const Region*   parentRegion            = nullptr;
     };
 
     struct DylibTextCoalescer {
@@ -91,9 +111,12 @@ public:
         DylibSectionOffsetToCacheSectionOffset objcMethNames;
         DylibSectionOffsetToCacheSectionOffset objcMethTypes;
 
-        bool sectionWasCoalesced(std::string_view sectionName) const;
-        DylibSectionOffsetToCacheSectionOffset& getSectionCoalescer(std::string_view sectionName);
-        const DylibSectionOffsetToCacheSectionOffset& getSectionCoalescer(std::string_view sectionName) const;
+        DylibSectionOffsetToCacheSectionOffset cfStrings;
+
+        bool segmentWasCoalesced(std::string_view segmentName) const;
+        bool sectionWasCoalesced(std::string_view segmentName, std::string_view sectionName) const;
+        DylibSectionOffsetToCacheSectionOffset& getSectionCoalescer(std::string_view segmentName, std::string_view sectionName);
+        const DylibSectionOffsetToCacheSectionOffset& getSectionCoalescer(std::string_view segmentName, std::string_view sectionName) const;
     };
 
     struct CacheCoalescedText {
@@ -109,41 +132,83 @@ public:
             uint64_t                             savedSpace       = 0;
         };
 
+        struct CFSection {
+            uint8_t*                             bufferAddr         = nullptr;
+            uint32_t                             bufferSize         = 0;
+            uint64_t                             bufferVMAddr       = 0;
+            uint64_t                             cacheFileOffset    = 0;
+
+            // The install name of the dylib for the ISA
+            const char*                          isaInstallName     = nullptr;
+            const char*                          isaClassName       = "___CFConstantStringClassReference";
+            uint64_t                             isaVMOffset        = 0;
+        };
+
         StringSection objcClassNames;
         StringSection objcMethNames;
         StringSection objcMethTypes;
 
+        CFSection     cfStrings;
+
         void parseCoalescableText(const dyld3::MachOAnalyzer* ma,
-                                   DylibTextCoalescer& textCoalescer);
+                                  DylibTextCoalescer& textCoalescer,
+                                  const IMPCaches::SelectorMap& selectors,
+                                  IMPCaches::HoleMap& selectorHoleMap);
+        void parseCFConstants(const dyld3::MachOAnalyzer* ma,
+                              DylibTextCoalescer& textCoalescer);
         void clear();
 
         StringSection& getSectionData(std::string_view sectionName);
         const StringSection& getSectionData(std::string_view sectionName) const;
+        uint64_t getSectionVMAddr(std::string_view segmentName, std::string_view sectionName) const;
+        uint8_t* getSectionBufferAddr(std::string_view segmentName, std::string_view sectionName) const;
+        uint64_t getSectionObjcTag(std::string_view segmentName, std::string_view sectionName) const;
     };
 
     class ASLR_Tracker
     {
     public:
-                ~ASLR_Tracker();
+        ASLR_Tracker() = default;
+        ~ASLR_Tracker();
+
+        ASLR_Tracker(ASLR_Tracker&&) = delete;
+        ASLR_Tracker(const ASLR_Tracker&) = delete;
+        ASLR_Tracker& operator=(ASLR_Tracker&& other) = delete;
+        ASLR_Tracker& operator=(const ASLR_Tracker& other) = delete;
 
         void        setDataRegion(const void* rwRegionStart, size_t rwRegionSize);
-        void        add(void* p);
+        void        add(void* loc, uint8_t level = (uint8_t)~0);
         void        setHigh8(void* p, uint8_t high8);
         void        setAuthData(void* p, uint16_t diversity, bool hasAddrDiv, uint8_t key);
         void        setRebaseTarget32(void*p, uint32_t targetVMAddr);
         void        setRebaseTarget64(void*p, uint64_t targetVMAddr);
         void        remove(void* p);
-        bool        has(void* p);
+        bool        has(void* loc, uint8_t* level = nullptr) const;
         const bool* bitmap()        { return _bitmap; }
         unsigned    dataPageCount() { return _pageCount; }
+        unsigned    pageSize() const { return _pageSize; }
         void        disable()       { _enabled = false; };
-        bool        hasHigh8(void* p, uint8_t* highByte);
-        bool        hasAuthData(void* p, uint16_t* diversity, bool* hasAddrDiv, uint8_t* key);
-        bool        hasRebaseTarget32(void* p, uint32_t* vmAddr);
-        bool        hasRebaseTarget64(void* p, uint64_t* vmAddr);
+        bool        hasHigh8(void* p, uint8_t* highByte) const;
+        bool        hasAuthData(void* p, uint16_t* diversity, bool* hasAddrDiv, uint8_t* key) const;
+        bool        hasRebaseTarget32(void* p, uint32_t* vmAddr) const;
+        bool        hasRebaseTarget64(void* p, uint64_t* vmAddr) const;
+
+        // Get all the out of band rebase targets.  Used for the kernel collection builder
+        // to emit the classic relocations
+        std::vector<void*> getRebaseTargets() const;
 
     private:
 
+        enum {
+#if BUILDING_APP_CACHE_UTIL
+            // The x86_64 kernel collection needs 1-byte aligned fixups
+            kMinimumFixupAlignment = 1
+#else
+            // Shared cache fixups must be at least 4-byte aligned
+            kMinimumFixupAlignment = 4
+#endif
+        };
+
         uint8_t*     _regionStart    = nullptr;
         uint8_t*     _regionEnd      = nullptr;
         bool*        _bitmap         = nullptr;
@@ -160,22 +225,33 @@ public:
         std::unordered_map<void*, AuthData> _authDataMap;
         std::unordered_map<void*, uint32_t> _rebaseTarget32;
         std::unordered_map<void*, uint64_t> _rebaseTarget64;
+
+        // For kernel collections to work out which other collection a given
+        // fixup is relative to
+#if BUILDING_APP_CACHE_UTIL
+        uint8_t*                            _cacheLevels = nullptr;
+#endif
     };
 
     typedef std::map<uint64_t, std::set<void*>> LOH_Tracker;
 
-    static const uint64_t kRebaseTargetInSideTableArm64e    = 0x7FFFFFFFFFFULL;
-    static const uint64_t kRebaseTargetInSideTableArm64     =   0xFFFFFFFFFULL;
-    static const uint64_t kRebaseTargetInSideTableGeneric32 =     0x3FFFFFFULL;
-
+    // For use by the LinkeditOptimizer to work out which symbols to strip on each binary
+    enum class DylibStripMode {
+        stripNone,
+        stripLocals,
+        stripExports,
+        stripAll
+    };
 
-    struct Region
+    struct DylibInfo
     {
-        uint8_t*    buffer                 = nullptr;
-        uint64_t    bufferSize             = 0;
-        uint64_t    sizeInUse              = 0;
-        uint64_t    unslidLoadAddress      = 0;
-        uint64_t    cacheFileOffset        = 0;
+        const LoadedMachO*              input;
+        std::string                     dylibID;
+        std::vector<SegmentMappingInfo> cacheLocation;
+        DylibTextCoalescer              textCoalescer;
+
+        // <class name, metaclass> -> pointer
+        std::unordered_map<IMPCaches::ClassKey, std::unique_ptr<IMPCaches::ClassData>, IMPCaches::ClassKeyHasher> impCachesClassData;
     };
 
 protected:
@@ -189,51 +265,58 @@ protected:
         uint64_t    sizeInUse              = 0;
     };
 
-    struct DylibInfo
-    {
-        const LoadedMachO*              input;
-        std::string                     runtimePath;
-        std::vector<SegmentMappingInfo> cacheLocation;
-        DylibTextCoalescer              textCoalescer;
-    };
+    // Virtual methods overridden by the shared cache builder and app cache builder
+    virtual void forEachDylibInfo(void (^callback)(const DylibInfo& dylib, Diagnostics& dylibDiag)) = 0;
 
     void        copyRawSegments();
-    void        adjustAllImagesForNewSegmentLocations();
+    void        adjustAllImagesForNewSegmentLocations(uint64_t cacheBaseAddress,
+                                                      ASLR_Tracker& aslrTracker, LOH_Tracker* lohTracker,
+                                                      const CacheBuilder::CacheCoalescedText* coalescedText);
 
     // implemented in AdjustDylibSegemnts.cpp
-    void        adjustDylibSegments(const DylibInfo& dylib, Diagnostics& diag) const;
+    void        adjustDylibSegments(const DylibInfo& dylib, Diagnostics& diag,
+                                    uint64_t cacheBaseAddress,
+                                    CacheBuilder::ASLR_Tracker& aslrTracker,
+                                    CacheBuilder::LOH_Tracker* lohTracker,
+                                    const CacheBuilder::CacheCoalescedText* coalescedText) const;
 
     // implemented in OptimizerLinkedit.cpp
-    void        optimizeLinkedit();
+    void        optimizeLinkedit(UnmappedRegion* localSymbolsRegion,
+                                 const std::vector<std::tuple<const mach_header*, const char*, DylibStripMode>>& images);
+
+    // implemented in OptimizerBranches.cpp
+    void        optimizeAwayStubs(const std::vector<std::pair<const mach_header*, const char*>>& images,
+                                  int64_t cacheSlide, uint64_t cacheUnslidAddr,
+                                  const DyldSharedCache* dyldCache,
+                                  const char* const neverStubEliminateSymbols[]);
 
     const DyldSharedCache::CreateOptions&       _options;
     const dyld3::closure::FileSystem&           _fileSystem;
     Region                                      _readExecuteRegion;
-    Region                                      _readWriteRegion;
     Region                                      _readOnlyRegion;
     UnmappedRegion                              _localSymbolsRegion;
     vm_address_t                                _fullAllocatedBuffer;
     uint64_t                                    _nonLinkEditReadOnlySize;
     Diagnostics                                 _diagnostics;
+    TimeRecorder                                _timeRecorder;
     uint64_t                                    _allocatedBufferSize;
-    std::vector<DylibInfo>                      _sortedDylibs;
     CacheCoalescedText                          _coalescedText;
-    uint32_t                                    _sharedStringsPoolVmOffset  = 0;
     bool                                        _is64                       = false;
     // Note this is mutable as the only parallel writes to it are done atomically to the bitmap
     mutable ASLR_Tracker                        _aslrTracker;
     mutable LOH_Tracker                         _lohTracker;
 };
 
-
-
-
 inline uint64_t align(uint64_t addr, uint8_t p2)
 {
     uint64_t mask = (1 << p2);
     return (addr + mask - 1) & (-mask);
 }
 
+inline uint8_t* align_buffer(uint8_t* addr, uint8_t p2)
+{
+    return (uint8_t *)align((uintptr_t)addr, p2);
+}
 
 
 #endif /* CacheBuilder_h */
index f0b179d74b82a8f99a22f84a7856584a40c88722..6c30017682300b1802b675f70c41ae6bc47500dc 100644 (file)
@@ -115,7 +115,7 @@ DyldSharedCache::CreateResults DyldSharedCache::create(const CreateOptions&
 
 bool DyldSharedCache::verifySelfContained(std::vector<MappedMachO>& dylibsToCache,
                                           std::unordered_set<std::string>& badZippered,
-                                          MappedMachO (^loader)(const std::string& runtimePath),
+                                          MappedMachO (^loader)(const std::string& runtimePath, Diagnostics& diag),
                                           std::vector<std::pair<DyldSharedCache::MappedMachO, std::set<std::string>>>& rejected)
 {
     // build map of dylibs
@@ -132,6 +132,7 @@ bool DyldSharedCache::verifySelfContained(std::vector<MappedMachO>& dylibsToCach
     }
 
     // check all dependencies to assure every dylib in cache only depends on other dylibs in cache
+    __block std::set<std::string> missingWeakDylibs;
     __block bool doAgain = true;
     while ( doAgain ) {
         __block std::vector<DyldSharedCache::MappedMachO> foundMappings;
@@ -141,6 +142,8 @@ bool DyldSharedCache::verifySelfContained(std::vector<MappedMachO>& dylibsToCach
             if ( badDylibs.count(dylib.runtimePath) != 0 )
                 continue;
             dylib.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+                if ( isWeak && (missingWeakDylibs.count(loadPath) != 0) )
+                    return;
                 if ( knownDylibs.count(loadPath) == 0 ) {
                     doAgain = true;
                     if ( badZippered.count(loadPath) != 0 ) {
@@ -151,11 +154,24 @@ bool DyldSharedCache::verifySelfContained(std::vector<MappedMachO>& dylibsToCach
                         badZippered.insert(dylib.mh->installName());
                         return;
                     }
+                    Diagnostics diag;
                     MappedMachO foundMapping;
                     if ( badDylibs.count(loadPath) == 0 )
-                        foundMapping = loader(loadPath);
+                        foundMapping = loader(loadPath, diag);
                     if ( foundMapping.length == 0 ) {
-                        badDylibs[dylib.runtimePath].insert(std::string("Could not find dependency '") + loadPath +"'");
+                        // We allow weakly linked dylibs to be missing only if they are not present on disk
+                        // The shared cache doesn't contain enough information to patch them in later if they are
+                        // found on disk, so we don't want to pull something in to cache and cut it off from a dylib it
+                        // could have used.
+                        if ( isWeak ) {
+                            missingWeakDylibs.insert(loadPath);
+                            return;
+                        }
+
+                        if (diag.hasError())
+                            badDylibs[dylib.runtimePath].insert(diag.errorMessage());
+                        else
+                            badDylibs[dylib.runtimePath].insert(std::string("Could not find dependency '") + loadPath +"'");
                         knownDylibs.erase(dylib.runtimePath);
                         knownDylibs.erase(dylib.mh->installName());
                     }
@@ -209,7 +225,14 @@ const T DyldSharedCache::getAddrField(uint64_t addr) const {
     return (const T)(addr + slide);
 }
 
-void DyldSharedCache::forEachRegion(void (^handler)(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions)) const
+uint64_t DyldSharedCache::getCodeSignAddress() const
+{
+    auto mappings = (const dyld_cache_mapping_info*)((uint8_t*)this + header.mappingOffset);
+    return mappings[header.mappingCount-1].address + mappings[header.mappingCount-1].size;
+}
+
+void DyldSharedCache::forEachRegion(void (^handler)(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions,
+                                                    uint64_t flags)) const
 {
     // <rdar://problem/49875993> sanity check cache header
     if ( strncmp(header.magic, "dyld_v1", 7) != 0 )
@@ -218,10 +241,18 @@ void DyldSharedCache::forEachRegion(void (^handler)(const void* content, uint64_
         return;
     if ( header.mappingCount > 20 )
         return;
-    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
-    const dyld_cache_mapping_info* mappingsEnd = &mappings[header.mappingCount];
-    for (const dyld_cache_mapping_info* m=mappings; m < mappingsEnd; ++m) {
-        handler((char*)this + m->fileOffset, m->address, m->size, m->initProt);
+    if ( header.mappingOffset <= __offsetof(dyld_cache_header, mappingWithSlideOffset) ) {
+        const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+        const dyld_cache_mapping_info* mappingsEnd = &mappings[header.mappingCount];
+        for (const dyld_cache_mapping_info* m=mappings; m < mappingsEnd; ++m) {
+            handler((char*)this + m->fileOffset, m->address, m->size, m->initProt, 0);
+        }
+    } else {
+        const dyld_cache_mapping_and_slide_info* mappings = (const dyld_cache_mapping_and_slide_info*)((char*)this + header.mappingWithSlideOffset);
+        const dyld_cache_mapping_and_slide_info* mappingsEnd = &mappings[header.mappingCount];
+        for (const dyld_cache_mapping_and_slide_info* m=mappings; m < mappingsEnd; ++m) {
+            handler((char*)this + m->fileOffset, m->address, m->size, m->initProt, m->flags);
+        }
     }
 }
 
@@ -236,7 +267,8 @@ bool DyldSharedCache::inCache(const void* addr, size_t length, bool& readOnly) c
     uintptr_t unslidStart = (uintptr_t)addr - slide;
 
     // quick out if after end of cache
-    if ( unslidStart > (mappings[2].address + mappings[2].size) )
+    const dyld_cache_mapping_info* lastMapping = &mappings[header.mappingCount - 1];
+    if ( unslidStart > (lastMapping->address + lastMapping->size) )
         return false;
 
     // walk cache regions
@@ -252,6 +284,13 @@ bool DyldSharedCache::inCache(const void* addr, size_t length, bool& readOnly) c
     return false;
 }
 
+bool DyldSharedCache::isAlias(const char* path) 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);
+    // paths for aliases are store between cache header and first segment
+    return path < ((char*)mappings[0].address + slide);
+}
+
 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);
@@ -293,6 +332,62 @@ void DyldSharedCache::forEachImageEntry(void (^handler)(const char* path, uint64
     }
 }
 
+const bool DyldSharedCache::hasLocalSymbolsInfo() const
+{
+    return (header.localSymbolsOffset != 0 && header.mappingOffset > offsetof(dyld_cache_header,localSymbolsSize));
+}
+
+const void* DyldSharedCache::getLocalNlistEntries() const
+{
+    // check for cache without local symbols info
+    if (!this->hasLocalSymbolsInfo())
+        return nullptr;
+    const auto localInfo = (dyld_cache_local_symbols_info*)((uint8_t*)this + header.localSymbolsOffset);
+    return (uint8_t*)localInfo + localInfo->nlistOffset;
+}
+
+const uint32_t DyldSharedCache::getLocalNlistCount() const
+{
+    // check for cache without local symbols info
+     if (!this->hasLocalSymbolsInfo())
+        return 0;
+    const auto localInfo = (dyld_cache_local_symbols_info*)((uint8_t*)this + header.localSymbolsOffset);
+    return localInfo->nlistCount;
+}
+
+const char* DyldSharedCache::getLocalStrings() const
+{
+    // check for cache without local symbols info
+     if (!this->hasLocalSymbolsInfo())
+        return nullptr;
+    const auto localInfo = (dyld_cache_local_symbols_info*)((uint8_t*)this + header.localSymbolsOffset);
+    return (char*)localInfo + localInfo->stringsOffset;
+}
+
+const uint32_t DyldSharedCache::getLocalStringsSize() const
+{
+    // check for cache without local symbols info
+     if (!this->hasLocalSymbolsInfo())
+        return 0;
+    const auto localInfo = (dyld_cache_local_symbols_info*)((uint8_t*)this + header.localSymbolsOffset);
+    return localInfo->stringsSize;
+}
+
+ void DyldSharedCache::forEachLocalSymbolEntry(void (^handler)(uint32_t dylibOffset, uint32_t nlistStartIndex, uint32_t nlistCount, bool& stop)) const
+{
+    // check for cache without local symbols info
+     if (!this->hasLocalSymbolsInfo())
+        return;
+    const auto localInfo = (dyld_cache_local_symbols_info*)((uint8_t*)this + header.localSymbolsOffset);
+    const auto localEntries = (dyld_cache_local_symbols_entry*)((uint8_t*)localInfo + localInfo->entriesOffset);
+    bool stop = false;
+    for (uint32_t i = 0; i < localInfo->entriesCount; i++) {
+        dyld_cache_local_symbols_entry localEntry = localEntries[i];
+        handler(localEntry.dylibOffset, localEntry.nlistStartIndex, localEntry.nlistCount, stop);
+    }
+}
+
+
 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);
@@ -302,10 +397,17 @@ const mach_header* DyldSharedCache::getIndexedImageEntry(uint32_t index, uint64_
     return (mach_header*)((uint8_t*)this + dylibs[index].address - mappings[0].address);
 }
 
+
+ const char* DyldSharedCache::getIndexedImagePath(uint32_t index) const
+{
+    auto dylibs = (const dyld_cache_image_info*)((char*)this + header.imagesOffset);
+    return (char*)this + dylibs[index].pathFileOffset;
+}
+
 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 )
+    if ( (header.mappingOffset <= __offsetof(dyld_cache_header, imagesTextOffset)) || (header.imagesTextCount == 0) )
         return;
 
     // walk imageText table and call callback for each entry
@@ -358,7 +460,8 @@ std::string DyldSharedCache::mapFile() const
     __block std::vector<uint64_t>   regionFileOffsets;
 
     result.reserve(256*1024);
-    forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+    forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions,
+                    uint64_t flags) {
         regionStartAddresses.push_back(vmAddr);
         regionSizes.push_back(size);
         regionFileOffsets.push_back((uint8_t*)content - (uint8_t*)this);
@@ -409,7 +512,8 @@ uint64_t DyldSharedCache::mappedSize() const
 {
     __block uint64_t startAddr = 0;
     __block uint64_t endAddr = 0;
-    forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) {
+    forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions,
+                    uint64_t flags) {
         if ( startAddr == 0 )
             startAddr = vmAddr;
         uint64_t end = vmAddr+size;
@@ -473,6 +577,16 @@ bool DyldSharedCache::hasImagePath(const char* dylibPath, uint32_t& imageIndex)
     return false;
 }
 
+bool DyldSharedCache::isOverridablePath(const char* dylibPath) const
+{
+    // all dylibs in customer dyld cache cannot be overridden except libdispatch.dylib
+    if ( header.cacheType == kDyldSharedCacheTypeProduction ) {
+        return (strcmp(dylibPath, "/usr/lib/system/libdispatch.dylib") == 0);
+    }
+    // in dev caches we can override all paths
+    return true;
+}
+
 bool DyldSharedCache::hasNonOverridablePath(const char* dylibPath) const
 {
     // all dylibs in customer dyld cache cannot be overridden except libdispatch.dylib
@@ -480,18 +594,19 @@ bool DyldSharedCache::hasNonOverridablePath(const char* dylibPath) const
     if ( header.cacheType == kDyldSharedCacheTypeProduction ) {
         uint32_t imageIndex;
         pathIsInDyldCacheWhichCannotBeOverridden = this->hasImagePath(dylibPath, imageIndex);
-        if ( pathIsInDyldCacheWhichCannotBeOverridden && (strcmp(dylibPath, "/usr/lib/system/libdispatch.dylib") == 0) )
+        if ( pathIsInDyldCacheWhichCannotBeOverridden && isOverridablePath(dylibPath) )
             pathIsInDyldCacheWhichCannotBeOverridden = false;
     }
     return pathIsInDyldCacheWhichCannotBeOverridden;
 }
 
+#if !BUILDING_LIBDSC
 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) )
+    if ( header.mappingOffset < __offsetof(dyld_cache_header, otherImageArrayAddr) )
         return nullptr;
     if ( header.otherImageArrayAddr == 0 )
         return nullptr;
@@ -511,9 +626,6 @@ const dyld3::closure::Image* DyldSharedCache::findDlopenOtherImage(const char* p
     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);
@@ -575,7 +687,23 @@ void DyldSharedCache::forEachDlopenImage(void (^handler)(const char* runtimePath
         }
     }
 }
-#endif
+
+void DyldSharedCache::forEachDylibPath(void (^handler)(const char* dylibPath, uint32_t index)) 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* dylibTrieStart       = (uint8_t*)(this->header.dylibsTrieAddr + slide);
+    const uint8_t* dylibTrieEnd         = dylibTrieStart + this->header.dylibsTrieSize;
+
+   std::vector<DylibIndexTrie::Entry> dylibEntries;
+    if ( Trie<DylibIndex>::parseTrie(dylibTrieStart, dylibTrieEnd, dylibEntries) ) {
+        for (DylibIndexTrie::Entry& entry : dylibEntries ) {
+            handler(entry.name.c_str(), entry.info.index);
+        }
+    }
+}
+#endif // !BUILDING_LIBDYLD && !BUILDING_DYLD
+#endif // !BUILDING_LIBDSC
 
 const dyld3::closure::ImageArray* DyldSharedCache::cachedDylibsImageArray() const
 {
@@ -594,7 +722,7 @@ const dyld3::closure::ImageArray* DyldSharedCache::cachedDylibsImageArray() cons
 const dyld3::closure::ImageArray* DyldSharedCache::otherOSImageArray() const
 {
     // check for old cache without imagesArray
-    if ( header.mappingOffset < sizeof(dyld_cache_header) )
+    if ( header.mappingOffset < __offsetof(dyld_cache_header, otherImageArrayAddr) )
         return nullptr;
 
     if ( header.otherImageArrayAddr == 0 )
@@ -738,25 +866,68 @@ std::string DyldSharedCache::generateJSONDependents() const {
 
 #endif
 
+#if !(BUILDING_LIBDYLD || BUILDING_DYLD)
+dyld3::MachOAnalyzer::VMAddrConverter DyldSharedCache::makeVMAddrConverter(bool contentRebased) const {
+    typedef dyld3::MachOAnalyzer::VMAddrConverter VMAddrConverter;
+
+    __block VMAddrConverter::SharedCacheFormat pointerFormat = VMAddrConverter::SharedCacheFormat::none;
+    __block uint64_t pointerValueAdd = 0;;
+    forEachSlideInfo(^(uint64_t mappingStartAddress, uint64_t mappingSize, const uint8_t *mappingPagesStart, uint64_t slideInfoOffset, uint64_t slideInfoSize, const dyld_cache_slide_info *slideInfoHeader) {
+        assert(slideInfoHeader->version >= 2);
+        if ( slideInfoHeader->version == 2 ) {
+            const dyld_cache_slide_info2* slideInfo = (dyld_cache_slide_info2*)(slideInfoHeader);
+            assert(slideInfo->delta_mask == 0x00FFFF0000000000);
+            pointerFormat   = VMAddrConverter::SharedCacheFormat::v2_x86_64_tbi;
+            pointerValueAdd = slideInfo->value_add;
+        } else if ( slideInfoHeader->version == 3 ) {
+            pointerFormat   = VMAddrConverter::SharedCacheFormat::v3;
+            pointerValueAdd = unslidLoadAddress();
+        } else {
+            assert(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);
+
+    VMAddrConverter vmAddrConverter;
+    vmAddrConverter.preferredLoadAddress            = pointerValueAdd;
+    vmAddrConverter.slide                           = slide;
+    vmAddrConverter.chainedPointerFormat            = 0;
+    vmAddrConverter.sharedCacheChainedPointerFormat = pointerFormat;
+    vmAddrConverter.contentRebased                  = contentRebased;
 
-const dyld_cache_slide_info* DyldSharedCache::slideInfo() const
+    return vmAddrConverter;
+}
+#endif
+
+const dyld_cache_slide_info* DyldSharedCache::legacyCacheSlideInfo() const
 {
+    assert(header.mappingOffset <= __offsetof(dyld_cache_header, mappingWithSlideOffset));
     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 offsetInLinkEditRegion = (header.slideInfoOffset - mappings[2].fileOffset);
+    uint64_t offsetInLinkEditRegion = (header.slideInfoOffsetUnused - mappings[2].fileOffset);
     return (dyld_cache_slide_info*)((uint8_t*)(mappings[2].address) + slide + offsetInLinkEditRegion);
 }
 
-const uint8_t* DyldSharedCache::dataRegionStart() const
+const dyld_cache_mapping_info* DyldSharedCache::legacyCacheDataRegionMapping() const
 {
+    assert(header.mappingOffset <= __offsetof(dyld_cache_header, mappingWithSlideOffset));
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
+    return &mappings[1];
+}
+
+const uint8_t* DyldSharedCache::legacyCacheDataRegionBuffer() const
+{
+    assert(header.mappingOffset <= __offsetof(dyld_cache_header, mappingWithSlideOffset));
     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);
     
-    return (uint8_t*)(mappings[1].address) + slide;
+    return (uint8_t*)(legacyCacheDataRegionMapping()->address) + slide;
 }
 
+#if !BUILDING_LIBDSC
 const objc_opt::objc_opt_t* DyldSharedCache::objcOpt() const {
     // Find the objc image
     const dyld3::MachOAnalyzer* objcMA = nullptr;
@@ -810,7 +981,7 @@ const void* DyldSharedCache::objcOptPtrs() const {
     int64_t slide = objcMA->getSlide();
     uint32_t pointerSize = objcMA->pointerSize();
     objcMA->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool& stop) {
-        if ( strncmp(info.segInfo.segName, "__DATA", 6) != 0 )
+        if ( (strncmp(info.segInfo.segName, "__DATA", 6) != 0) && (strncmp(info.segInfo.segName, "__AUTH", 6) != 0) )
             return;
         if (strcmp(info.sectName, "__objc_opt_ptrs") != 0)
             return;
@@ -827,6 +998,97 @@ const void* DyldSharedCache::objcOptPtrs() const {
 
     return objcPointersContent;
 }
+#endif
+
+std::pair<const void*, uint64_t> DyldSharedCache::getObjCConstantRange() const {
+    const dyld3::MachOAnalyzer* libDyldMA = nullptr;
+    uint32_t imageIndex;
+    if ( hasImagePath("/usr/lib/system/libdyld.dylib", imageIndex) ) {
+        const dyld3::closure::ImageArray* images = cachedDylibsImageArray();
+        const dyld3::closure::Image* image = images->imageForNum(imageIndex+1);
+        libDyldMA = (const dyld3::MachOAnalyzer*)((uintptr_t)this + image->cacheOffset());
+
+        std::pair<const void*, uint64_t> ranges = { nullptr, 0 };
+#if TARGET_OS_OSX
+        ranges.first = libDyldMA->findSectionContent("__DATA", "__objc_ranges", ranges.second);
+#else
+        ranges.first = libDyldMA->findSectionContent("__DATA_CONST", "__objc_ranges", ranges.second);
+#endif
+        return ranges;
+    }
+
+    return { nullptr, 0 };
+}
+
+bool DyldSharedCache::hasSlideInfo() const {
+    if ( header.mappingOffset <= __offsetof(dyld_cache_header, mappingWithSlideOffset) ) {
+        return header.slideInfoSizeUnused != 0;
+    } else {
+        const dyld_cache_mapping_and_slide_info* slidableMappings = (const dyld_cache_mapping_and_slide_info*)((char*)this + header.mappingWithSlideOffset);
+        for (uint32_t i = 0; i != header.mappingWithSlideCount; ++i) {
+            if ( slidableMappings[i].slideInfoFileSize != 0 ) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+void DyldSharedCache::forEachSlideInfo(void (^handler)(uint64_t mappingStartAddress, uint64_t mappingSize,
+                                                       const uint8_t* mappingPagesStart,
+                                                       uint64_t slideInfoOffset, uint64_t slideInfoSize,
+                                                       const dyld_cache_slide_info* slideInfoHeader)) const {
+    if ( header.mappingOffset <= __offsetof(dyld_cache_header, mappingWithSlideOffset) ) {
+        // Old caches should get the slide info from the cache header and assume a single data region.
+        const dyld_cache_mapping_info* dataMapping = legacyCacheDataRegionMapping();
+        uint64_t dataStartAddress = dataMapping->address;
+        uint64_t dataSize = dataMapping->size;
+        const uint8_t* dataPagesStart = legacyCacheDataRegionBuffer();
+        const dyld_cache_slide_info* slideInfoHeader = legacyCacheSlideInfo();
+
+        handler(dataStartAddress, dataSize, dataPagesStart,
+                header.slideInfoOffsetUnused, header.slideInfoSizeUnused, slideInfoHeader);
+    } else {
+        const dyld_cache_mapping_and_slide_info* slidableMappings = (const dyld_cache_mapping_and_slide_info*)((char*)this + header.mappingWithSlideOffset);
+        const dyld_cache_mapping_and_slide_info* linkeditMapping = &slidableMappings[header.mappingWithSlideCount - 1];
+        uint64_t sharedCacheSlide = (uint64_t)this - unslidLoadAddress();
+
+        for (uint32_t i = 0; i != header.mappingWithSlideCount; ++i) {
+            if ( slidableMappings[i].slideInfoFileOffset != 0 ) {
+                // Get the data pages
+                uint64_t dataStartAddress = slidableMappings[i].address;
+                uint64_t dataSize = slidableMappings[i].size;
+                const uint8_t* dataPagesStart = (uint8_t*)dataStartAddress + sharedCacheSlide;
+
+                // Get the slide info
+                uint64_t offsetInLinkEditRegion = (slidableMappings[i].slideInfoFileOffset - linkeditMapping->fileOffset);
+                const dyld_cache_slide_info* slideInfoHeader = (dyld_cache_slide_info*)((uint8_t*)(linkeditMapping->address) + sharedCacheSlide + offsetInLinkEditRegion);
+                handler(dataStartAddress, dataSize, dataPagesStart,
+                        slidableMappings[i].slideInfoFileOffset, slidableMappings[i].slideInfoFileSize, slideInfoHeader);
+            }
+        }
+    }
+}
+
+#if BUILDING_LIBDYLD
+const char* DyldSharedCache::getCanonicalPath(const char *path) const {
+    uint32_t dyldCacheImageIndex;
+    if ( hasImagePath(path, dyldCacheImageIndex) )
+        return getIndexedImagePath(dyldCacheImageIndex);
+#if TARGET_OS_OSX
+    // on macOS support "Foo.framework/Foo" symlink
+    char resolvedPath[PATH_MAX];
+    realpath(path, resolvedPath);
+    int realpathErrno = errno;
+    // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT
+    if ( (realpathErrno == ENOENT) || (realpathErrno == 0) ) {
+        if ( hasImagePath(resolvedPath, dyldCacheImageIndex) )
+            return getIndexedImagePath(dyldCacheImageIndex);
+    }
+#endif
+    return nullptr;
+}
+#endif
 
 #if !(BUILDING_LIBDYLD || BUILDING_DYLD)
 void DyldSharedCache::fillMachOAnalyzersMap(std::unordered_map<std::string,dyld3::MachOAnalyzer*> & dylibAnalyzers) const {
index 26968436453d819199a6615e445d2919d254e819..6e084bd327952ba541df83784c633fb89b276653 100644 (file)
@@ -40,6 +40,7 @@
 #include "Diagnostics.h"
 #include "MachOAnalyzer.h"
 #include "Closure.h"
+#include "JSON.h"
 
 namespace objc_opt {
 struct objc_opt_t;
@@ -57,15 +58,22 @@ public:
         Agile      = 2
     };
 
+    enum class LocalSymbolsMode {
+        keep,
+        unmap,
+        strip
+    };
+
     struct CreateOptions
     {
         std::string                                 outputFilePath;
         std::string                                 outputMapFilePath;
         const dyld3::GradedArchs*                   archs;
         dyld3::Platform                             platform;
-        bool                                        excludeLocalSymbols;
+        LocalSymbolsMode                            localSymbolMode;
         bool                                        optimizeStubs;
-        bool                                        optimizeObjC;
+        bool                                        optimizeDyldDlopens;
+        bool                                        optimizeDyldLaunches;
         CodeSigningDigestMode                       codeSigningDigestMode;
         bool                                        dylibsRemovedDuringMastering;
         bool                                        inodesAreSameAsRuntime;
@@ -76,6 +84,7 @@ public:
         bool                                        evictLeafDylibsOnOverflow;
         std::unordered_map<std::string, unsigned>   dylibOrdering;
         std::unordered_map<std::string, unsigned>   dirtyDataSegmentOrdering;
+        dyld3::json::Node                           objcOptimizations;
         std::string                                 loggingPrefix;
     };
 
@@ -118,7 +127,7 @@ public:
     // outset the set.  It will call back the loader function to try to find any mising dylibs.
     static bool verifySelfContained(std::vector<MappedMachO>& dylibsToCache,
                                     std::unordered_set<std::string>& badZippered,
-                                    MappedMachO (^loader)(const std::string& runtimePath), std::vector<std::pair<DyldSharedCache::MappedMachO, std::set<std::string>>>& excluded);
+                                    MappedMachO (^loader)(const std::string& runtimePath, Diagnostics& diag), std::vector<std::pair<DyldSharedCache::MappedMachO, std::set<std::string>>>& excluded);
 
 
     //
@@ -154,7 +163,7 @@ public:
     //
     std::string         mapFile() const;
 
-#endif // TARGET_OS_OSX
+#endif // BUILDING_CACHE_BUILDER
 
 
     //
@@ -181,13 +190,31 @@ public:
     bool                hasImagePath(const char* dylibPath, uint32_t& imageIndex) const;
 
 
+    //
+    // Is this path (which we know is in the shared cache), overridable
+    //
+    bool                isOverridablePath(const char* dylibPath) const;
+
+
     //
     // Path is to a dylib in the cache and this is an optimized cache so that path cannot be overridden
     //
     bool                hasNonOverridablePath(const char* dylibPath) const;
 
 
-   //
+    //
+    // Check if shared cache contains local symbols info
+    //
+    const bool          hasLocalSymbolsInfo() const;
+
+
+    //
+    // Get code signature mapped address
+    //
+    uint64_t             getCodeSignAddress() const;
+
+
+    //
     // Searches cache for dylib with specified mach_header
     //
     bool                findMachHeaderImageIndex(const mach_header* mh, uint32_t& imageIndex) const;
@@ -199,13 +226,29 @@ public:
 
 
     //
-    // Iterates over each dylib in the cache
+    // Get image entry from index
     //
     const mach_header*  getIndexedImageEntry(uint32_t index, uint64_t& mTime, uint64_t& node) const;
 
 
+    // iterates over all dylibs and aliases
+    void forEachDylibPath(void (^handler)(const char* dylibPath, uint32_t index)) const;
+
+
     //
-    // Iterates over each dylib in the cache
+    // Get image path from index
+    //
+    const char*         getIndexedImagePath(uint32_t index) const;
+
+#if BUILDING_LIBDYLD
+    //
+    // Get the canonical (dylib) path for a given path, which may be a symlink to something in the cache
+    //
+    const char*         getCanonicalPath(const char* path) const;
+#endif
+
+    //
+    // Iterates over each text segment in the cache
     //
     void                forEachImageTextSegment(void (^handler)(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName, bool& stop)) const;
 
@@ -213,14 +256,48 @@ public:
     //
     // Iterates over each of the three regions in the cache
     //
-    void                forEachRegion(void (^handler)(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions)) const;
+    void                forEachRegion(void (^handler)(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions,
+                                                      uint64_t flags)) const;
+
+
+    //
+    // Get local symbols nlist entries
+    //
+    const void*         getLocalNlistEntries() const;
+
+
+    //
+    // Get local symbols nlist count
+    //
+    const uint32_t      getLocalNlistCount() const;
+
+
+    //
+    // Get local symbols strings
+    //
+    const char*  getLocalStrings() const;
+
+
+    //
+    // Get local symbols strings size
+    //
+    const uint32_t       getLocalStringsSize() const;
+
 
+     //
+     // Iterates over each local symbol entry in the cache
+     //
+     void                forEachLocalSymbolEntry(void (^handler)(uint32_t dylibOffset, uint32_t nlistStartIndex, uint32_t nlistCount, bool& stop)) 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 true if a path is an alternate path (symlink)
+    //
+    bool isAlias(const char* path) const;
 
     //
     // returns address the cache would load at if unslid
@@ -278,12 +355,17 @@ public:
     //
     // Returns the pointer to the slide info for this cache
     //
-    const dyld_cache_slide_info* slideInfo() const;
+    const dyld_cache_slide_info* legacyCacheSlideInfo() const;
+
+    //
+    // Returns a pointer to the __DATA region mapping in the cache
+    //
+    const dyld_cache_mapping_info* legacyCacheDataRegionMapping() const;
 
     //
     // Returns a pointer to the start of the __DATA region in the cache
     //
-    const uint8_t* dataRegionStart() const;
+    const uint8_t* legacyCacheDataRegionBuffer() const;
 
     //
     // Returns a pointer to the shared cache optimized Objective-C data structures
@@ -295,6 +377,15 @@ public:
     //
     const void* objcOptPtrs() const;
 
+    // Returns true if the cache has any slide info, either old style on a single data region
+    // or on each individual data mapping
+    bool                hasSlideInfo() const;
+
+    void                forEachSlideInfo(void (^handler)(uint64_t mappingStartAddress, uint64_t mappingSize,
+                                                         const uint8_t* mappingPagesStart,
+                                                         uint64_t slideInfoOffset, uint64_t slideInfoSize,
+                                                         const dyld_cache_slide_info* slideInfoHeader)) const;
+
 
     //
     // returns true if the offset is in the TEXT of some cached dylib and sets *index to the dylib index
@@ -336,8 +427,26 @@ public:
     std::string generateJSONDependents() const;
 #endif
 
+    // Note these enum entries are only valid for 64-bit archs.
+    enum class ConstantClasses {
+        cfStringAtomSize = 32
+    };
+
+    // Returns the start and size of the range in the shared cache of the ObjC constants, such as
+    // all of the CFString's which have been moved in to a contiguous range
+    std::pair<const void*, uint64_t> getObjCConstantRange() const;
+
+#if !(BUILDING_LIBDYLD || BUILDING_DYLD)
+    dyld3::MachOAnalyzer::VMAddrConverter makeVMAddrConverter(bool contentRebased) const;
+#endif
+
     dyld_cache_header header;
 
+    // The most mappings we could generate.
+    // For now its __TEXT, __DATA_CONST, __DATA_DIRTY, __DATA, __LINKEDIT,
+    // and optionally also __AUTH, __AUTH_CONST, __AUTH_DIRTY
+    static const uint32_t MaxMappings = 8;
+
 private:
     // Returns a variable of type "const T" which corresponds to the header field with the given unslid address
     template<typename T>
index 088b90106a4c50c61cb85b40ac64f4a89e37c858..051c6d7ef77a03c32bdc079064a56075765d2ba1 100644 (file)
 #include "FileUtils.h"
 #include "StringUtils.h"
 #include "Diagnostics.h"
-
-#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101200
-extern "C" int rootless_check_trusted_fd(int fd) __attribute__((weak_import));
-#endif
+#include "JSONReader.h"
 
 
 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)
@@ -63,7 +60,7 @@ void iterateDirectoryTree(const std::string& pathPrefix, const std::string& path
     }
     while (dirent* entry = readdir(dir)) {
         struct stat statBuf;
-        std::string dirAndFile = path + "/" + entry->d_name;
+        std::string dirAndFile = path + (path.back() != '/' ? "/" : "") + entry->d_name;
         std::string fullDirAndFile = pathPrefix + dirAndFile;
          switch ( entry->d_type ) {
             case DT_REG:
@@ -136,49 +133,6 @@ const void* mapFileReadOnly(const char* path, size_t& mappedSize)
     return nullptr;
 }
 
-static bool sipIsEnabled()
-{
-    static bool             rootlessEnabled;
-    static dispatch_once_t  onceToken;
-    // Check to make sure file system protections are on at all
-    dispatch_once(&onceToken, ^{
-        rootlessEnabled = (csr_check(CSR_ALLOW_UNRESTRICTED_FS) != 0);
-    });
-    return rootlessEnabled;
-}
-
-bool isProtectedBySIP(const std::string& path)
-{
-    if ( !sipIsEnabled() )
-        return false;
-
-    return (rootless_check_trusted(path.c_str()) == 0);
-}
-
-bool isProtectedBySIPExceptDyld(const std::string& path)
-{
-    if ( !sipIsEnabled() )
-        return false;
-
-    return (rootless_check_trusted_class(path.c_str(), "dyld") == 0);
-}
-
-bool isProtectedBySIP(int fd)
-{
-    if ( !sipIsEnabled() )
-        return false;
-
-#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
-    return (rootless_check_trusted_fd(fd) == 0);
-#else
-    // fallback to using rootless_check_trusted
-    char realPath[MAXPATHLEN];
-    if ( fcntl(fd, F_GETPATH, realPath) == 0 )
-        return (rootless_check_trusted(realPath) == 0);
-    return false;
-#endif
-}
-
 bool fileExists(const std::string& path)
 {
     struct stat statBuf;
index 6bc5410a2d9ee8d129308f7f19f861d649027643..86e2649b0d7287344ca44bc7e1bbefbef6f18f9f 100644 (file)
@@ -36,6 +36,7 @@
 #include <dispatch/dispatch.h>
 
 #include "DyldSharedCache.h"
+#include "JSON.h"
 
 class Diagnostics;
 
@@ -73,10 +74,6 @@ bool safeSave(const void* buffer, size_t bufferLen, const std::string& path);
 
 const void* mapFileReadOnly(const char* path, size_t& mappedSize);
 
-bool isProtectedBySIP(const std::string& path);
-bool isProtectedBySIPExceptDyld(const std::string& path);
-bool isProtectedBySIP(int fd);
-
 bool fileExists(const std::string& path);
 
 std::unordered_map<std::string, uint32_t> parseOrderFile(const std::string& orderFileData);
diff --git a/dyld3/shared-cache/IMPCaches.cpp b/dyld3/shared-cache/IMPCaches.cpp
new file mode 100644 (file)
index 0000000..5b7b7e8
--- /dev/null
@@ -0,0 +1,2194 @@
+//
+//  IMPCaches.cpp
+//  dyld_shared_cache_builder
+//
+//  Created by Thomas Deniau on 18/12/2019.
+//
+
+#include "FileAbstraction.hpp"
+#include "IMPCaches.hpp"
+#include "IMPCachesBuilder.hpp"
+#include "JSONReader.h"
+
+#include <unordered_map>
+#include <vector>
+#include <string>
+#include <iostream>
+#include <sstream>
+#include <iomanip>
+#include <numeric>
+#include <random>
+#include <map>
+
+namespace IMPCaches {
+
+std::string ClassData::description() const
+{
+    std::stringstream ss;
+    ss << name << " modulo:" << modulo();
+    if (isMetaclass) ss << " (metaclass)";
+    return ss.str();
+}
+
+std::string ClassData::PlacementAttempt::description() const
+{
+    std::stringstream stream;
+    stream << "needed bits: " << neededBits << ", shift: " << shift;
+    return stream.str();
+}
+
+typename ClassData::PlacementAttempt ClassData::attemptForShift(int shiftToTry, int neededBitsToTry) const
+{
+    int totalNumberOfBitsToSet = 0;
+    int mask = (1 << neededBitsToTry) - 1;
+    for (const Method& method : methods) {
+        totalNumberOfBitsToSet += method.selector->numberOfBitsToSet(shiftToTry, mask);
+    }
+
+    return ClassData::PlacementAttempt(totalNumberOfBitsToSet, shiftToTry, neededBitsToTry);
+}
+
+std::vector<typename ClassData::PlacementAttempt> ClassData::attempts() const
+{
+    // We have 26 MB of selectors, and among them only ~ 7MB are deemed
+    // "interesting" to include in our hash tables.
+
+    // So we should be able to fit on 24 bits of address space (~ 16 MB of selectors),
+    // but need to keep the low 7 bits available so that we
+    // don't have to worry about selector length and so that
+    // we don't have to worry about collisions. (i.e. multiple selectors
+    // end up at the same address with this algorithm and then we play
+    // in another step with
+    // the low 7 bits to give them all an unique address).
+    // Then if there are holes given this 128-byte alignment, we can
+    // fill the holes with selectors excluded from the algorithm.
+
+    // Let us grow the hash tables to one more bit if needed
+    // as the full problem is too difficult.
+    std::vector<int>              allowedNeededBits { neededBits, neededBits + 1 };
+    std::vector<PlacementAttempt> attempts;
+    for (auto i : allowedNeededBits) {
+        // Go thrugh all the possible shifts, starting at 0, knowing that
+        // shift+neededBits needs to fit on 17 bits.
+        
+        for (int shiftToTry = 0; shiftToTry <= 17 - i; shiftToTry++) {
+            attempts.push_back(attemptForShift(shiftToTry, i));
+        }
+    }
+    sort(attempts.begin(), attempts.end());
+    return attempts;
+}
+
+void ClassData::resetSlots()
+{
+#if DEBUG_SLOTS
+    slots.assign(slots.size(), nullptr);
+#else
+    slots.assign(slots.size(), false);
+#endif
+}
+
+void ClassData::backtrack(typename ClassData::PlacementAttempt::Result& resultToBacktrackFrom)
+{
+    if (!resultToBacktrackFrom.success) {
+        // We backtrack from a failure if we decided to skip a class that was too difficult to place.
+        // In this case there's nothing to do.
+        
+        return;
+    }
+    
+    // Restore the addresses and masks we had in place before we did the step that let to resultToBacktrackFrom
+    typename PlacementAttempt::PreviousState previousState = resultToBacktrackFrom.previousState;
+    for (const Method& method : methods) {
+        Selector* selector = method.selector;
+        typename PlacementAttempt::PreviousMethodAddress previousMethod = previousState.methods[selector];
+        selector->inProgressBucketIndex = previousMethod.address;
+        selector->fixedBitsMask = previousMethod.fixedBitsMask;
+    }
+
+    shift = previousState.shift;
+    neededBits = previousState.neededBits;
+}
+
+// Compute the number of needed bits for the hash table now that all the methods have been added
+void ClassData::didFinishAddingMethods()
+{
+    if (methods.size() == 0) {
+        neededBits = 0;
+    } else {
+        neededBits = (int)ceil(log2(double(methods.size())));
+    }
+}
+
+bool ClassData::hadToIncreaseSize() const
+{
+    if (methods.size() == 0) return false;
+    return neededBits > (int)(ceil(log2((double)(methods.size()))));
+}
+
+/// Try to see if we can make the shift and mask in @param attempt work.
+typename ClassData::PlacementAttempt::Result ClassData::applyAttempt(const PlacementAttempt& attempt, std::minstd_rand & randomNumberGenerator)
+{
+    std::vector<Selector*> sortedMethods;
+    sortedMethods.reserve(methods.size());
+    for (const Method& method : methods) {
+        sortedMethods.push_back(method.selector);
+    }
+
+    // Solve from most constrained to least constrained
+    std::sort(sortedMethods.begin(), sortedMethods.end(), [attempt](Selector* m1, Selector* m2) {
+        return m1->numberOfBitsToSet(attempt.shift, attempt.mask()) < m2->numberOfBitsToSet(attempt.shift, attempt.mask());
+    });
+
+    if (slots.size() < (1 << attempt.neededBits)) {
+#if DEBUG_SLOTS
+        slots.resize(1 << attempt.neededBits, nullptr);
+#else
+        slots.resize(1 << attempt.neededBits, false);
+#endif
+    }
+
+    resetSlots();
+
+    std::vector<int> addresses;
+    for (auto m : sortedMethods) {
+        bool found = false;
+
+        // Check if all the bits are already assigned
+        int shiftedMask = attempt.mask() << attempt.shift;
+        if ((m->fixedBitsMask & shiftedMask) == shiftedMask) {
+            int index = (m->inProgressBucketIndex >> attempt.shift) & attempt.mask();
+            if (slots[index]) {
+                typename ClassData::PlacementAttempt::Result result;
+                result.success = false;
+                return result;
+            }
+#if DEBUG_SLOTS
+            slots[index] = m;
+#else
+            slots[index] = true;
+#endif
+            found = true;
+            addresses.push_back(index);
+        } else {
+            // Some bits are not assigned yet, so try to find an address that would be compatible
+            // with the existing bits for this method.
+            
+            int attemptModulo = 1 << attempt.neededBits;
+
+            // possibleAddresses = 0..<attemptModulo
+            std::vector<int> possibleAddresses(attemptModulo);
+            std::iota(possibleAddresses.begin(), possibleAddresses.end(), 0);
+
+            // We randomize the addresses to try so that two random selectors
+            // have as many ranges of different bits as possible, in order
+            // to find a satisfying shift for every class.
+
+            std::shuffle(possibleAddresses.begin(), possibleAddresses.end(), randomNumberGenerator);
+            for (auto i : possibleAddresses) {
+                int futureAddress = m->inProgressBucketIndex | (i << attempt.shift);
+                int slot = (futureAddress >> attempt.shift) & attempt.mask();
+
+                // Make sure the new address is compatible with the existing bits
+                bool addressesMatch = (futureAddress & m->fixedBitsMask) == (m->inProgressBucketIndex & m->fixedBitsMask);
+                if (addressesMatch && !slots[slot]) {
+#if DEBUG_SLOTS
+                    slots[slot] = m;
+#else
+                    slots[slot] = true;
+#endif
+                    found = true;
+                    addresses.push_back(i);
+                    break;
+                }
+            }
+            if (!found) {
+                typename ClassData::PlacementAttempt::Result result;
+                result.success = false;
+                return result;
+            }
+        }
+    }
+
+    // We succeeded, record the state so that we can backtrack if needed
+    std::unordered_map<Selector*, typename PlacementAttempt::PreviousMethodAddress> previousMethods;
+    for (unsigned long i = 0; i < sortedMethods.size(); i++) {
+        Selector* m = sortedMethods[i];
+        int         previousAddress = m->inProgressBucketIndex;
+        int         previousMask = m->fixedBitsMask;
+        m->inProgressBucketIndex |= addresses[i] << attempt.shift;
+        m->fixedBitsMask |= attempt.mask() << attempt.shift;
+        previousMethods[m] = typename PlacementAttempt::PreviousMethodAddress {
+            .address = previousAddress,
+            .fixedBitsMask = previousMask
+        };
+    }
+
+    typename PlacementAttempt::PreviousState previousState {
+        .neededBits = neededBits,
+        .shift = shift,
+        .methods = previousMethods
+    };
+    shift = attempt.shift;
+    neededBits = attempt.neededBits;
+
+    typename  PlacementAttempt::Result result {
+        .success = true,
+        .previousState = previousState
+    };
+
+    return result;
+}
+
+bool ClassData::checkConsistency() {
+    resetSlots();
+    for (const Method& method : methods) {
+        const Selector* s = method.selector;
+        int slotIndex = (s->inProgressBucketIndex >> shift) & mask();
+        if (slots[slotIndex]) {
+            return false;
+        }
+#if DEBUG_SLOTS
+        slots[slotIndex] = s;
+#else
+        slots[slotIndex] = true;
+#endif
+    }
+    return true;
+}
+
+Constraint ClassData::constraintForMethod(const Selector* method) {
+    resetSlots();
+    allowedValues.clear();
+    
+    // Fill the slots with all our methods except `method`
+    for (const Method& m : methods) {
+        const Selector* s = m.selector;
+        if (s == method) {
+            continue;
+        }
+
+        int slotIndex = (s->inProgressBucketIndex >> shift) & mask();
+#if DEBUG_SLOTS
+        assert(slots[slotIndex] == nullptr);
+        slots[slotIndex] = s;
+#else
+        assert(!slots[slotIndex]);
+        slots[slotIndex] = true;
+#endif
+    }
+
+    // What are the remaining empty slots in which we could put `method`?
+    int max = 1 << neededBits;
+    for (int i = 0 ; i < max ; i++) {
+        if (!slots[i]) {
+            allowedValues.push_back(i);
+        }
+    }
+
+    auto allowedSet = std::unordered_set<int>(allowedValues.begin(), allowedValues.end());
+    return Constraint {
+        .mask = mask(),
+        .shift = shift,
+        .allowedValues = allowedSet
+    };
+}
+
+size_t ClassData::sizeInSharedCache() const {
+    return IMPCaches::sizeForImpCacheWithCount(modulo());
+}
+
+int AddressSpace::sizeAtIndex(int idx) const
+{
+    if (sizes.find(idx) != sizes.end()) {
+        return sizes.at(idx);
+    } else {
+        return 0;
+    }
+}
+
+void AddressSpace::removeUninterestingSelectors() {
+    for (auto& [k,v] : methodsByIndex) {
+        v.erase(std::remove_if(v.begin(), v.end(), [](const Selector* s){
+            return s->classes.size() == 0;
+        }), v.end());
+    }
+}
+
+int AddressSpace::sizeAvailableAfterIndex(int idx) const
+{
+    int availableAfterThisAddress = bagSizeAtIndex(idx) - sizeAtIndex(idx);
+    for (int j = idx + 1; j < maximumIndex; j++) {
+        if (methodsByIndex.find(j) != methodsByIndex.end()) {
+            break;
+        } else {
+            availableAfterThisAddress += bagSizeAtIndex(j);
+        }
+    }
+
+    return availableAfterThisAddress;
+}
+
+// Because some selectors are longer than 128 bytes, we have to sometimes let
+// them overflow into the next 128-byte bucket. This method tells you if you
+// can place a method in a bucket without colliding with an overflowing selector
+// from one of the previous buckets.
+bool AddressSpace::canPlaceWithoutFillingOverflowCellAtIndex(int idx) const
+{
+    if ((idx == 0) || (sizeAtIndex(idx) > 0)) {
+        return true;
+    }
+
+    int j = idx;
+    int availableOnOrBefore = 0;
+
+    while (j > 0 && (sizeAtIndex(j) == 0)) {
+        availableOnOrBefore += bagSizeAtIndex(j);
+        j -= 1;
+    }
+
+    int sizeOfFirstNonEmptyCellBefore = sizeAtIndex(j);
+    return (sizeOfFirstNonEmptyCellBefore < availableOnOrBefore);
+}
+
+bool AddressSpace::canPlaceMethodAtIndex(const Selector* method, int idx) const
+{
+    int  existingSize = sizeAtIndex(idx);
+    bool canPlaceWithoutFillingOverflow = canPlaceWithoutFillingOverflowCellAtIndex(idx);
+
+    if (!canPlaceWithoutFillingOverflow) {
+        return false;
+    }
+
+    int  available = bagSizeAtIndex(idx) - existingSize;
+    int  methodSize = method->size();
+    bool enoughRoom = available > methodSize;
+
+    if (enoughRoom) {
+        return true;
+    }
+
+    bool tooBigButOverflowExists = (methodSize > 64) && (available > 0) && (sizeAvailableAfterIndex(idx) > methodSize);
+
+    return tooBigButOverflowExists;
+}
+
+void AddressSpace::placeMethodAtIndex(Selector* method, int idx)
+{
+    auto [it, success] = methodsByIndex.try_emplace(idx, std::vector<Selector*>());
+    it->second.push_back(method);
+
+    auto [it2, success2] = sizes.try_emplace(idx, 0);
+    it2->second += method->size();
+}
+
+// At this point selected are already sorted into 128-byte buckets.
+// Now fill in the low 7 bits of each address, and return a list
+// of intervals [  ..... selector data....][ ...... hole ....][.... selector data...]
+// so that we can stuff in selectors that don't participate in
+// static IMP caches
+void AddressSpace::computeLowBits(HoleMap& selectors) const {
+    int currentEndOffset =  magicSelector.length() + 1;
+
+    std::set<IMPCaches::Hole> & holes = selectors.holes;
+    holes.clear();
+
+    std::vector<int> orderedIndices;
+    for (const auto& [index, selectors] : methodsByIndex) {
+        orderedIndices.push_back(index);
+    }
+    std::sort(orderedIndices.begin(), orderedIndices.end());
+    for (int index : orderedIndices) {
+        const std::vector<Selector*> & selectorsAtThisIndex = methodsByIndex.at(index);
+        int bucketOffset = index << bagSizeShift;
+        if (bucketOffset  > currentEndOffset) {
+            holes.insert(Hole {
+                .startAddress = currentEndOffset,
+                .endAddress = bucketOffset,
+            });
+            currentEndOffset = bucketOffset;
+        }
+        for (Selector* s : selectorsAtThisIndex) {
+            s->offset = currentEndOffset;
+            currentEndOffset += s->size();
+        }
+    }
+
+    selectors.endAddress = currentEndOffset;
+}
+
+int HoleMap::addStringOfSize(unsigned size) {
+//    static int i = 0;
+//    if (i++ % 1000 == 0) {
+//        printf("Inserted 1000 more strings, number of holes = %lu\n", holes.size());
+//    }
+    Hole neededHole = Hole {
+        .startAddress = 0,
+        .endAddress = static_cast<int>(size)
+    };
+    std::set<Hole>::iterator it = holes.lower_bound(neededHole);
+    if (it == holes.end()) {
+        // insert at the end
+        int end = endAddress;
+        endAddress += size;
+        return end;
+    } else {
+        // Remove this hole and insert a smaller one instead
+
+        int address = it->startAddress;
+        Hole updatedHole = *it;
+        updatedHole.startAddress += size;
+        holes.erase(it);
+
+        // Don't insert if the hole is empty or won't fit any selector
+        if (updatedHole.size() > 1) {
+            holes.insert(updatedHole);
+        }
+        return address;
+    }
+}
+
+void HoleMap::clear() {
+    holes.clear();
+    endAddress = 0;
+}
+
+unsigned long HoleMap::totalHoleSize() const {
+    unsigned long result = 0;
+    for (const Hole& hole : holes) {
+        result += hole.size();
+    }
+    return result;
+}
+
+std::ostream& operator<<(std::ostream& o, const HoleMap& m) {
+    int size = 0;
+    int count = 0;
+    for (const Hole& h : m.holes) {
+        if (h.size() == size) {
+            count++;
+        } else {
+            o << count << " holes of size " << size << std::endl;
+            size = h.size();
+            count = 1;
+        }
+    }
+    return o;
+}
+
+std::ostream& operator<<(std::ostream& o, const AddressSpace& a)
+{
+    int maximumIndex = 0;
+    for (const auto& kvp : a.methodsByIndex) {
+        maximumIndex = std::max(maximumIndex, kvp.first);
+    }
+
+    std::vector<double>                     lengths;
+    std::map<int, std::vector<Selector*>> sortedMethodsByAddress;
+    sortedMethodsByAddress.insert(a.methodsByIndex.begin(), a.methodsByIndex.end());
+
+    std::map<int, int> sortedSizes;
+    sortedSizes.insert(a.sizes.begin(), a.sizes.end());
+
+    for (const auto& kvp : sortedSizes) {
+        lengths.push_back(kvp.second);
+    }
+
+    for (const auto& kvp : sortedMethodsByAddress) {
+        o << std::setw(5) << kvp.first << ": ";
+        for (Selector* m : kvp.second) {
+            o << m->name << " ";
+        }
+        o << "\n";
+    }
+
+    o << "Max address " << maximumIndex << "\n";
+    o << "Average length " << (double)std::accumulate(lengths.begin(), lengths.end(), 0) / lengths.size() << "\n";
+
+    return o;
+}
+
+// Returns a constraint that is the intersection of "this" and "other", i.e. a constraint for which the allowed values
+// are the intersection of the allowed values of "this" and "other" (taking into account shift and mask)
+Constraint Constraint::intersecting(const Constraint& other) const
+{
+    if ((mask == other.mask) && (shift == other.shift)) {
+        // fast path
+        std::unordered_set<int> intersection;
+        for (int allowedValue : allowedValues) {
+            if (other.allowedValues.find(allowedValue) != other.allowedValues.end()) {
+                intersection.insert(allowedValue);
+            }
+        }
+
+        return Constraint {
+            .mask = mask,
+            .shift = shift,
+            .allowedValues = intersection
+        };
+    }
+
+    int                  shiftedMask = mask << shift;
+    int                  otherShift = other.shift;
+    int                  otherMask = other.mask;
+    int                  otherShiftedMask = other.mask << otherShift;
+    int                  intersectionMask = shiftedMask & otherShiftedMask;
+    const std::unordered_set<int>& otherAllowedValues = other.allowedValues;
+
+    // Always make sure we start with the left-most mask as self
+    if (shiftedMask < otherShiftedMask) {
+        return other.intersecting(*this);
+    }
+
+    // If there are no real constraints on our side, just return the other
+    if ((mask == 0) && (allowedValues.size() == 1) && (*(allowedValues.begin()) == 0)) {
+        return other;
+    }
+
+    // If there are no real constraints on the other side, just return our constraint
+    if ((otherMask == 0) && (otherAllowedValues.size() == 1) && (*(otherAllowedValues.begin()) == 0)) {
+        return *this;
+    }
+
+    if (otherShift >= shift) {
+        // [self..[other]..self]
+        // Restrict the allowed values to make sure they have the right bits.
+        int           shiftDifference = otherShift - shift;
+        std::unordered_set<int> combinedAllowedValues;
+        for (int v : allowedValues) {
+            int val = (v >> shiftDifference) & otherMask;
+            if (otherAllowedValues.find(val) != otherAllowedValues.end()) {
+                combinedAllowedValues.insert(v);
+            }
+        }
+
+        return Constraint {
+            .mask = mask,
+            .shift = shift,
+            .allowedValues = combinedAllowedValues
+        };
+    }
+
+    int highestBit = (int)fls(shiftedMask) - 1;
+    int otherHighestBit = (int)fls(otherShiftedMask) - 1;
+    int otherMaskLength = fls(otherMask + 1) - 1;
+
+    if (otherShiftedMask < (1 << shift)) {
+        // [self]....[other]
+        // Start by shifting all the allowed values in self
+        int           numberOfUnconstrainedBits = shift - otherHighestBit - 1;
+        int           maxUnconstrained = 1 << numberOfUnconstrainedBits;
+        std::set<int> includingUnrestrictedBits;
+
+        if (numberOfUnconstrainedBits > 0) {
+            for (const int allowed : allowedValues) {
+                int shifted = allowed << numberOfUnconstrainedBits;
+                for (int unconstrained = 0; unconstrained < maxUnconstrained; unconstrained++) {
+                    // Mix in unrestricted bits, then shift by [other]'s length
+                    includingUnrestrictedBits.insert((shifted | unconstrained) << otherMaskLength);
+                }
+            };
+        } else {
+            for (const int allowed : allowedValues) {
+                // Shift all the values by [other]'s length
+                includingUnrestrictedBits.insert(allowed << otherMaskLength);
+            }
+        }
+
+        // Or in the values for [other]
+        std::unordered_set<int> finalAllowedValues;
+        for (const int allowed : includingUnrestrictedBits) {
+            for (const int otherValue : otherAllowedValues) {
+                finalAllowedValues.insert(allowed | otherValue);
+            }
+        }
+
+        return Constraint {
+            .mask = ((1 << (highestBit + 1)) - 1) >> otherShift,
+            .shift = otherShift,
+            .allowedValues = finalAllowedValues
+        };
+
+    } else {
+        // Overlap.
+        // [self....[other....self].....other].......
+        // We need to
+        // * determine the set of bits allowed in the intersection
+        // * filter each set of values to keep only these
+        // * do the cross-product
+
+        // Bits in the intersection
+
+        int           shiftDifference = shift - otherShift;
+        std::set<int> selfIntersectingBits;
+        for (const int v : allowedValues) {
+            selfIntersectingBits.insert(((v << shift) & intersectionMask) >> shift);
+        }
+        std::set<int> otherIntersectingBits;
+        for (const int v : otherAllowedValues) {
+            otherIntersectingBits.insert(((v << otherShift) & intersectionMask) >> shift);
+        }
+
+        std::set<int> intersectingBits;
+        std::set_intersection(selfIntersectingBits.begin(), selfIntersectingBits.end(), otherIntersectingBits.begin(), otherIntersectingBits.end(), std::inserter(intersectingBits, intersectingBits.begin()));
+
+        std::unordered_set<int> values;
+        // Swift here was constructing a list of values for self and other
+        // filtered on which elements had the right values for intersectionMask
+        // This would avoid the n^3 loop at the expense of some storage... FIXME
+        for (const int intersecting : intersectingBits) {
+            int intersectingShifted = intersecting << shift;
+            for (int selfAllowed : allowedValues) {
+                if (((selfAllowed << shift) & intersectionMask) == intersectingShifted) {
+                    for (int otherAllowed : otherAllowedValues) {
+                        if (((otherAllowed << otherShift) & intersectionMask) == intersectingShifted) {
+                            values.insert((selfAllowed << shiftDifference) | otherAllowed);
+                        }
+                    }
+                }
+            }
+        }
+
+        return Constraint {
+            .mask = (shiftedMask | otherShiftedMask) >> otherShift,
+            .shift = otherShift,
+            .allowedValues = values
+        };
+    }
+}
+
+std::ostream& operator << (std::ostream& o, const std::unordered_set<int> & s) {
+    o << "{";
+    for (int i : s) {
+        o << i << ", ";
+    }
+    o << "}";
+    return o;
+}
+
+std::ostream& operator << (std::ostream& o, const Constraint& c) {
+    o << "(x >> " << c.shift << " & " << c.mask << " == " << c.allowedValues;
+    return o;
+}
+
+bool ConstraintSet::add(const Constraint& c) {
+    if (constraints.find(c) != constraints.end()) {
+        return false;
+    }
+    constraints.insert(c);
+    if (mergedConstraint.has_value()) {
+        mergedConstraint = mergedConstraint->intersecting(c);
+    } else {
+        mergedConstraint = c;
+    }
+    return true;
+}
+
+void ConstraintSet::clear() {
+    constraints.clear();
+    mergedConstraint.reset();
+}
+
+bool IMPCachesBuilder::isClassInteresting(const ObjCClass& theClass) const {
+    std::string_view classNameStr(theClass.className);
+    if (theClass.isMetaClass) {
+        return neededMetaclasses.find(classNameStr) != neededMetaclasses.end();
+    } else {
+        return neededClasses.find(classNameStr) != neededClasses.end();
+    }
+}
+
+bool IMPCachesBuilder::isClassInterestingOrTracked(const ObjCClass& theClass) const {
+    std::string_view classNameStr(theClass.className);
+    auto& neededArray = theClass.isMetaClass ? neededMetaclasses : neededClasses;
+    auto& trackedArray = theClass.isMetaClass ? trackedMetaclasses : trackedClasses;
+
+    return (neededArray.find(classNameStr) != neededArray.end()) ||
+           (trackedArray.find(classNameStr) != trackedArray.end());
+}
+
+void IMPCachesBuilder::addMethod(IMPCaches::ClassData* classDataPtr, const char* methodName, const char* installName, const char* className, const char* catName, bool inlined, bool fromFlattening) {
+    std::string_view methodNameView(methodName);
+
+    auto [selectorIterator, success] = selectors.map.try_emplace(methodNameView, std::make_unique<Selector>());
+    IMPCaches::Selector* thisSelectorData = selectorIterator->second.get();
+    if (success) {
+        thisSelectorData->name = methodName;
+    }
+
+    std::vector<ClassData::Method> & methods = classDataPtr->methods;
+    // Check in the existing methods to see if the method already exists...
+    bool exists = false;
+    for (const ClassData::Method& m : methods) {
+        if (m.selector == thisSelectorData) {
+            // printf("Preventing insertion of duplicate %s.%s\n", classDataPtr->name, methodName);
+            exists = true;
+            break;
+        }
+    }
+
+    if (!exists) {
+        ClassData::Method m {
+            .installName = installName,
+            .className = className,
+            .categoryName = catName,
+            .selector = thisSelectorData,
+            .wasInlined = inlined,
+            .fromFlattening = fromFlattening
+        };
+
+        thisSelectorData->classes.push_back(classDataPtr);
+        classDataPtr->methods.push_back(m);
+    }
+}
+
+void IMPCachesBuilder::inlineMethodIfNeeded(IMPCaches::ClassData* classToInlineIn, const char* classToInlineFrom, const char* catToInlineFrom, const char* installNameToInlineFrom, const char* name, std::set<Selector*>& seenSelectors, bool isFlattening) {
+    std::string_view nameView(name);
+
+    if ((nameView == ".cxx_construct") || (nameView == ".cxx_destruct")) {
+        // These selectors should never be inherited.
+        // object_cxxConstructFromClass / object_cxxDestructFromClass walk the class hierarchy and call them all.
+
+        return;
+    }
+
+    bool shouldInline = isFlattening || (selectorsToInline.find(nameView) != selectorsToInline.end());
+    if (shouldInline) {
+        // The selector hasn't necessarily been seen at this point: eg. we don't build an IMP cache for UIView, so we haven't seen -[UIView superview] yet.
+
+        auto [it, inserted] = selectors.map.try_emplace(nameView, std::make_unique<Selector>());
+        IMPCaches::Selector* thisSelectorData = it->second.get();
+
+        if (inserted) {
+            thisSelectorData->name = name;
+        }
+
+        if (inserted || seenSelectors.find(thisSelectorData) == seenSelectors.end()) {
+            seenSelectors.insert(thisSelectorData);
+            const bool inlined = true;
+            addMethod(classToInlineIn, name, installNameToInlineFrom, classToInlineFrom, catToInlineFrom, inlined, isFlattening);
+        }
+    }
+}
+
+// This goes through all the superclasses of the interesting classes so that
+// we can track their methods for inlining.
+// Since it goes through the superclasses, we also take this opportunity
+// to add subclassses of duplicate classes to the duplicate classes set.
+void IMPCachesBuilder::buildTrackedClasses(CacheBuilder::DylibInfo& dylib, const dyld3::MachOAnalyzer* ma) {
+    dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = ma->makeVMAddrConverter(false);
+    ma->forEachObjCClass(_diagnostics, vmAddrConverter, ^(Diagnostics& diag, uint64_t classVMAddr, uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
+        const uint8_t* classLocation = (classVMAddr - ma->preferredLoadAddress()) + (uint8_t*)ma;
+
+        // The class might not be in the map as we exclude classes with missing weak superclasses
+        auto classIt = objcClasses.find(classLocation);
+        if ( classIt == objcClasses.end() )
+            return;
+        const auto& theClass = classIt->second;
+        ClassKey theClassKey {
+            .name = theClass.className,
+            .metaclass = theClass.isMetaClass
+        };
+
+        if (!isClassInteresting(theClass)) return;
+
+        // Go through superclasses and add them to the tracked set.
+        const IMPCaches::IMPCachesBuilder::ObjCClass* currentClass = &theClass;
+        bool rootClass = false;
+        do {
+            ClassKey k {
+                .name = currentClass->className,
+                .metaclass = currentClass->isMetaClass
+            };
+
+            // If one of the superclasses of theClass is in the duplicate classes set,
+            // add theClass to the duplicate classes as well.
+            if (duplicateClasses.find(k) != duplicateClasses.end()) {
+                duplicateClasses.insert(theClassKey);
+            }
+
+            if (currentClass->isMetaClass) {
+                trackedMetaclasses.insert(currentClass->className);
+            } else {
+                trackedClasses.insert(currentClass->className);
+            }
+            rootClass = currentClass->isRootClass;
+            if (!rootClass) {
+                // The superclass might not be in the map as we exclude classes with missing weak superclasses
+                auto superclassIt = objcClasses.find(currentClass->superclass);
+                if ( superclassIt == objcClasses.end() )
+                    break;
+                currentClass = &superclassIt->second;
+            }
+        } while (!rootClass);
+    });
+}
+
+/// Parses the method lists of all the classes in @param dylib so that we populate the methods we want in each IMP cache skeleton.
+void IMPCachesBuilder::populateMethodLists(CacheBuilder::DylibInfo& dylib, const dyld3::MachOAnalyzer* ma, int* duplicateClassCount) {
+    const uint32_t pointerSize = ma->pointerSize();
+    dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = ma->makeVMAddrConverter(false);
+
+    ma->forEachObjCClass(_diagnostics, vmAddrConverter, ^(Diagnostics& diag, uint64_t classVMAddr, uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
+
+        const uint8_t* classLocation = (classVMAddr - ma->preferredLoadAddress()) + (uint8_t*)ma;
+
+        // The class might not be in the map as we exclude classes with missing weak superclasses
+        auto classIt = objcClasses.find(classLocation);
+        if ( classIt == objcClasses.end() )
+            return;
+        const auto& theClass = classIt->second;
+
+        if (!isClassInterestingOrTracked(theClass)) return;
+        bool interesting = isClassInteresting(theClass);
+
+        std::string_view classNameString(theClass.className);
+        std::unique_ptr<IMPCaches::ClassData> thisData = std::make_unique<ClassData>();
+        IMPCaches::ClassData* thisDataPtr = thisData.get();
+        thisData->name = theClass.className;
+        thisData->isMetaclass = isMetaClass;
+        thisData->shouldGenerateImpCache = interesting;
+
+        ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), vmAddrConverter, ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
+            dyld3::MachOAnalyzer::PrintableStringResult printableStringResult;
+            const char* methodName = ma->getPrintableString(method.nameVMAddr, printableStringResult);
+            if (printableStringResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint) {
+                return;
+            }
+
+            const bool inlined = false;
+            const bool fromFlattening = false;
+            addMethod(thisDataPtr, methodName, ma->installName(), theClass.className, NULL, inlined, fromFlattening);
+        });
+
+        ClassKey key {
+            .name = classNameString,
+            .metaclass = isMetaClass
+        };
+        assert(dylib.impCachesClassData.find(key) == dylib.impCachesClassData.end());
+
+        if (duplicateClasses.find(key) != duplicateClasses.end()) {
+            // We can't just set shouldGenerateImpCache to false ; we do it later
+            // when we have built the flattening hierarchies in order to drop
+            // any related classes as well.
+
+            thisData->isPartOfDuplicateSet = true;
+            *duplicateClassCount += 1;
+        }
+
+        dylib.impCachesClassData[key] = std::move(thisData);
+    });
+}
+
+/// Parses all the categories within the same image as a class so that we can add the corresponding methods to the IMP cache skeletons, too.
+void IMPCachesBuilder::attachCategories(CacheBuilder::DylibInfo& dylib, const dyld3::MachOAnalyzer* ma) {
+    dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = ma->makeVMAddrConverter(false);
+    ma->forEachObjCCategory(_diagnostics, vmAddrConverter, ^(Diagnostics& diag, uint64_t categoryVMAddr, const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) {
+
+        const uint8_t* categoryLocation = (categoryVMAddr - ma->preferredLoadAddress()) + (uint8_t*)ma;
+        ObjCCategory previouslyFoundCategory = objcCategories.at(categoryLocation);
+
+        __block dyld3::MachOAnalyzer::PrintableStringResult printableStringResult;
+        const char* catName = ma->getPrintableString(objcCategory.nameVMAddr, printableStringResult);
+
+        if (previouslyFoundCategory.classMA != ma) {
+            // Cross-image category
+            return;
+        }
+
+        // The class might not be in the map as we exclude classes with missing weak superclasses
+        auto classIt = objcClasses.find(previouslyFoundCategory.cls);
+        if ( classIt == objcClasses.end() )
+            return;
+        const auto& theClass = classIt->second;
+
+        auto& theMetaClass = objcClasses[theClass.metaClass];
+        auto classNameString = std::string_view(theClass.className);
+
+        if (isClassInterestingOrTracked(theClass)) {
+            ClassKey key {
+                .name = classNameString,
+                .metaclass = false
+            };
+            ClassData* clsData = dylib.impCachesClassData.at(key).get();
+
+            ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, vmAddrConverter, ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
+                // The config file should specify only classes without cross-image categories, so we should have found a class here
+                assert(clsData != NULL);
+                const char* methodName = ma->getPrintableString(method.nameVMAddr, printableStringResult);
+                const bool inlined = false;
+                const bool fromFlattening = false;
+                addMethod(clsData, methodName, ma->installName(), theClass.className, catName, inlined, fromFlattening);
+            });
+        }
+        if (isClassInterestingOrTracked(theMetaClass)) {
+            ClassKey key {
+                .name = classNameString,
+                .metaclass = true
+            };
+            ClassData* metaclsData = dylib.impCachesClassData.at(key).get();
+
+            ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, vmAddrConverter, ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
+                assert(metaclsData != NULL);
+                const char* methodName = ma->getPrintableString(method.nameVMAddr, printableStringResult);
+                const bool inlined = false;
+                const bool fromFlattening = false;
+                addMethod(metaclsData, methodName, ma->installName(), theMetaClass.className, catName, inlined, fromFlattening);
+            });
+        }
+    });
+}
+
+struct FlatteningRootLookupResult {
+    bool isInFlatteningHierarchy;
+
+    const uint8_t * flatteningRootSuperclassLocation;
+    ClassData::ClassLocator flatteningRootSuperclass;
+    std::set<std::string_view> superclassesInFlatteningHierarchy;
+    const char * flatteningRootName;
+};
+
+static FlatteningRootLookupResult findFlatteningRoot(const uint8_t* classLocation,
+                                                     const std::unordered_map<const uint8_t*, IMPCachesBuilder::ObjCClass> & objcClasses,
+                                                     const std::unordered_set<std::string_view> & classHierarchiesToFlatten,
+                                                     const std::unordered_set<std::string_view>  & metaclassHierarchiesToFlatten,
+                                                     bool storeSuperclasses) {
+    FlatteningRootLookupResult result;
+    const uint8_t* superclassLocation = classLocation;
+    __block bool rootClass = false;
+    bool success = false;
+
+    while (!rootClass) {
+        const auto it = objcClasses.find(superclassLocation);
+        if (it == objcClasses.end()) {
+            break;
+        }
+        const IMPCachesBuilder::ObjCClass& iteratedClass = it->second;
+        rootClass = iteratedClass.isRootClass;
+        superclassLocation = iteratedClass.superclass;
+
+        if (storeSuperclasses) {
+            result.superclassesInFlatteningHierarchy.insert(iteratedClass.className);
+        }
+
+        bool metaClassBeingFlattened = iteratedClass.isMetaClass && metaclassHierarchiesToFlatten.find(iteratedClass.className) != metaclassHierarchiesToFlatten.end();
+        bool classBeingFlattened = !iteratedClass.isMetaClass && classHierarchiesToFlatten.find(iteratedClass.className) != classHierarchiesToFlatten.end();
+
+        if (metaClassBeingFlattened || classBeingFlattened) {
+            result.flatteningRootName = iteratedClass.className;
+            result.flatteningRootSuperclassLocation = iteratedClass.superclass;
+            result.flatteningRootSuperclass = iteratedClass.superclassLocator();
+            success = true;
+            break;
+        }
+    }
+
+    result.isInFlatteningHierarchy = success;
+
+    return result;
+}
+/// Inline selectors from parent classes into child classes for performance
+void IMPCachesBuilder::inlineSelectors(CacheBuilder::DylibInfo& dylib, std::unordered_map<std::string_view, CacheBuilder::DylibInfo*> & dylibsByInstallName, const dyld3::MachOAnalyzer* ma) {
+    dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = ma->makeVMAddrConverter(false);
+    ma->forEachObjCClass(_diagnostics, vmAddrConverter, ^(Diagnostics& diag, uint64_t classVMAddr, uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr, const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
+
+        const uint8_t* classLocation = (classVMAddr - ma->preferredLoadAddress()) + (uint8_t*)ma;
+
+        // The class might not be in the map as we exclude classes with missing weak superclasses
+        auto classIt = objcClasses.find(classLocation);
+        if ( classIt == objcClasses.end() )
+            return;
+        const auto& theClass = classIt->second;
+
+        if (!isClassInteresting(theClass)) {
+            return;
+        }
+
+        std::string_view classNameString(theClass.className);
+        ClassKey key {
+            .name = classNameString,
+            .metaclass = isMetaClass
+        };
+
+        IMPCaches::ClassData* thisDataPtr = dylib.impCachesClassData.at(key).get();
+        assert(thisDataPtr != NULL);
+
+        __block std::set<Selector*> seenSelectors;
+        for (const ClassData::Method& method : thisDataPtr->methods) {
+            seenSelectors.insert(method.selector);
+        };
+
+        // Check the superclass hierarchy to see if we're in a flattened hierarchy
+        // (meaning we should inline all of the selectors up to the flattening root)
+        FlatteningRootLookupResult flatteningInfo = findFlatteningRoot(classLocation, objcClasses, classHierarchiesToFlatten, metaclassHierarchiesToFlatten, false);
+
+        if (flatteningInfo.isInFlatteningHierarchy) {
+            // try again and record superclasses this time
+            // (maybe premature optimization, but given the small number of classes where flattening
+            //  is actually happening I did not want to gather this set every time)
+
+            flatteningInfo = findFlatteningRoot(classLocation, objcClasses, classHierarchiesToFlatten, metaclassHierarchiesToFlatten, true);
+
+            assert(flatteningInfo.isInFlatteningHierarchy);
+
+            thisDataPtr->flatteningRootSuperclass = flatteningInfo.flatteningRootSuperclass;
+            thisDataPtr->flatteningRootName = flatteningInfo.flatteningRootName;
+            thisDataPtr->flattenedSuperclasses = flatteningInfo.superclassesInFlatteningHierarchy;
+        }
+
+        // Iterate again to actually flatten/inline the selectors
+        const uint8_t *superclassLocation = classLocation;
+        __block const dyld3::MachOAnalyzer* currentMA = ma;
+        bool isRootClass = false;
+        bool isFlattening =  flatteningInfo.isInFlatteningHierarchy;
+
+        while (!isRootClass) {
+            const auto it = objcClasses.find(superclassLocation);
+            if (it == objcClasses.end()) {
+                break;
+            }
+            ObjCClass& iteratedClass = it->second;
+            isRootClass = iteratedClass.isRootClass;
+
+            CacheBuilder::DylibInfo* classDylib = dylibsByInstallName.at(currentMA->installName());
+            ClassKey keyForIteratedClass {
+                .name = std::string_view(iteratedClass.className),
+                .metaclass = iteratedClass.isMetaClass
+            };
+            auto classDataIt = classDylib->impCachesClassData.find(keyForIteratedClass);
+
+            // We should have added this class to our data in populateMethodLists()
+            assert(classDataIt != classDylib->impCachesClassData.end());
+
+            IMPCaches::ClassData* classData = classDataIt->second.get();
+
+            for (const ClassData::Method& m : classData->methods) {
+                // If the method found in the superclass was inlined from a further superclass, we'll inline it
+                // when we reach that class (otherwise the install name / class name the method is coming from will be wrong)
+                if (!m.wasInlined) {
+                    inlineMethodIfNeeded(thisDataPtr, m.className, m.categoryName, currentMA->installName(), m.selector->name, seenSelectors, isFlattening);
+                }
+            }
+
+            currentMA = iteratedClass.superclassMA;
+            assert(isRootClass || (currentMA != nullptr));
+            superclassLocation = iteratedClass.superclass;
+
+            if (isFlattening && (iteratedClass.superclass == flatteningInfo.flatteningRootSuperclassLocation)) {
+                // we reached the flattening root, turn flattening off
+                isFlattening = false;
+            }
+        }
+    });
+}
+
+/// Parses all the source dylibs to fill the IMP cache skeletons with all the methods we want to have there.
+bool IMPCachesBuilder::parseDylibs(Diagnostics& diag) {
+    std::unordered_map<std::string_view, CacheBuilder::DylibInfo*> dylibsByInstallName;
+    
+    for (CacheBuilder::DylibInfo& d : dylibs) {
+        const DyldSharedCache::MappedMachO& mapped = d.input->mappedFile;
+        const dyld3::MachOAnalyzer* ma = mapped.mh;
+
+        dylibsByInstallName.insert(std::make_pair(std::string_view(ma->installName()), &d));
+
+        // Build the set of tracked classes (interesting classes + their superclasses)
+        buildTrackedClasses(d, ma);
+    }
+
+    int totalDuplicateClassCount = 0;
+
+    for (CacheBuilder::DylibInfo& d : dylibs) {
+        const DyldSharedCache::MappedMachO& mapped = d.input->mappedFile;
+        const dyld3::MachOAnalyzer* ma = mapped.mh;
+
+        int duplicateClassCount = 0;
+        // First, go through all classes and populate their method lists.
+        populateMethodLists(d, ma, &duplicateClassCount);
+        totalDuplicateClassCount += duplicateClassCount;
+
+        // Now go through all categories and attach them as well
+        attachCategories(d, ma);
+    }
+    
+    _diagnostics.verbose("[IMP caches] Not generating caches for %d duplicate classes or children of duplicate classes\n", totalDuplicateClassCount);
+
+    // Ensure that all the selectors will fit on 16 MB as that's the constant
+    // embedded in the placement algorithm
+    uint32_t totalSize = 0;
+    for (const auto& [k,v] : selectors.map) {
+        totalSize += v->size();
+    }
+    if (totalSize >= (1 << 24)) {
+        diag.warning("Dropping all IMP caches ; too many selectors\n");
+        return false;
+    }
+
+    for (CacheBuilder::DylibInfo& d : dylibs) {
+        const DyldSharedCache::MappedMachO& mapped = d.input->mappedFile;
+        const dyld3::MachOAnalyzer* ma = mapped.mh;
+
+        // Now that all categories are attached, handle any selector inheritance if needed
+        // (Do this after category attachment so that inlined selectors don't override categories)
+        
+        inlineSelectors(d, dylibsByInstallName, ma);
+    }
+
+    removeUninterestingClasses();
+
+    unsigned count = 0;
+    for (CacheBuilder::DylibInfo& d : dylibs) {
+        count += d.impCachesClassData.size();
+    }
+    
+    constexpr bool logAllSelectors = false;
+
+    diag.verbose("[IMP Caches] parsed %u classes\n", count);
+    for (CacheBuilder::DylibInfo& d : dylibs) {
+        for (auto it = d.impCachesClassData.begin() ; it != d.impCachesClassData.end() ; it++) {
+            auto& c = it->second;
+            c->didFinishAddingMethods();
+            if (logAllSelectors) {
+                printf("%s\n", c->description().c_str());
+                std::vector<ClassData::Method> sortedMethods(c->methods.begin(), c->methods.end());
+                std::sort(sortedMethods.begin(), sortedMethods.end(), [](const ClassData::Method& a, const ClassData::Method& b) {
+                    return strcmp(a.selector->name, b.selector->name) < 0;
+                });
+                for (const ClassData::Method& m : sortedMethods) {
+                    const Selector* s = m.selector;
+                    printf("  %s", s->name);
+                    if (m.categoryName != nullptr) {
+                        printf("  (from %s::%s+%s)\n", m.installName, m.className, m.categoryName);
+                    } else if (m.className != c->name) {
+                        printf(" (from %s::%s)\n", m.installName, m.className);
+                    } else {
+                        printf("\n");
+                    }
+                }
+            }
+        }
+    }
+
+    for (auto& s : selectorsToInline) {
+        auto selectorsIt = selectors.map.find(s);
+        if (selectorsIt == selectors.map.end()) {
+            diag.warning("Requested selector to inline not found in any classes: %s\n", s.data());
+            continue;
+        }
+        inlinedSelectors.push_back(selectors.map.at(s).get());
+    }
+    std::sort(inlinedSelectors.begin(), inlinedSelectors.end(), [](const Selector* a, const Selector *b) {
+        return a->offset < b->offset;
+    });
+
+    return true;
+}
+
+IMPCachesBuilder::TargetClassFindingResult IMPCachesBuilder::findTargetClass(const dyld3::MachOAnalyzer* ma, uint64_t targetClassVMAddr, uint64_t targetClassPointerVMAddr,const char* logContext, const std::unordered_map<uint64_t, uint64_t> & bindLocations, std::vector<BindTarget> & bindTargets, std::unordered_map<std::string, DylibAndDeps> &dylibMap) {
+    constexpr bool log = false;
+    uint64_t loadAddress = ma->preferredLoadAddress();
+
+    uint64_t superclassRuntimeOffset = targetClassPointerVMAddr - loadAddress;
+    auto bindIt = bindLocations.find(superclassRuntimeOffset);
+
+    if ( bindIt == bindLocations.end() ) {
+        if (targetClassVMAddr != 0) {
+            // A rebase, ie, a fixup to a class in this dylib
+            if ( log )
+                printf("%s: %s -> this dylib\n", ma->installName(), logContext);
+            superclassRuntimeOffset = targetClassVMAddr - loadAddress;
+            return TargetClassFindingResult {
+                .success = true,
+                .foundInDylib = ma,
+                .location = (const uint8_t*)ma + superclassRuntimeOffset
+            };
+        } else {
+            // A bind, ie, a fixup to a class in another dylib
+            return TargetClassFindingResult { .success = false };
+        }
+    }
+
+    const BindTarget& bindTarget = bindTargets[bindIt->second];
+    if ( log )
+        printf("%s: %s -> %s: %s\n", ma->installName(), logContext,
+               bindTarget.targetDylib->installName(), bindTarget.symbolName.c_str());
+
+    dyld3::MachOLoaded::DependentToMachOLoaded finder = ^(const dyld3::MachOLoaded* mh, uint32_t depIndex) {
+        auto dylibIt = dylibMap.find(mh->installName());
+        if ( dylibIt == dylibMap.end() ) {
+            // Missing weak dylib?
+            return (const dyld3::MachOLoaded*)nullptr;
+        }
+
+        if ( depIndex >= dylibIt->second.dependentLibraries.size() ) {
+            // Out of bounds dependent
+            assert(false);
+        }
+
+        auto depIt = dylibMap.find(dylibIt->second.dependentLibraries[depIndex]);
+        if ( depIt == dylibMap.end() ) {
+            // Missing weak dylib?
+            return (const dyld3::MachOLoaded*)nullptr;
+        }
+        return (const dyld3::MachOLoaded*)depIt->second.ma;
+    };
+
+    if (bindTarget.targetDylib != nullptr) {
+        dyld3::MachOAnalyzer::FoundSymbol foundInfo;
+        bool found = bindTarget.targetDylib->findExportedSymbol(_diagnostics, bindTarget.symbolName.c_str(),
+                                                                false, foundInfo, finder);
+        if ( !found ) {
+            if ( !bindTarget.isWeakImport ) {
+                _diagnostics.verbose("Could not find non-weak target class: '%s'\n", bindTarget.symbolName.c_str());
+            }
+            // We couldn't find the target class.  This is an error if the symbol is not a weak-import, but
+            // we'll let symbol resolution work that out later if we really have a missing symbol which matters
+            // For IMP caches, we'll just ignore this class.
+            return TargetClassFindingResult { .success = false };
+        }
+        assert(found);
+        assert(foundInfo.kind == dyld3::MachOAnalyzer::FoundSymbol::Kind::headerOffset);
+        if (foundInfo.foundInDylib == nullptr) {
+            if ( log )
+                printf("null foundInDylib\n");
+        }
+        return TargetClassFindingResult {
+            .success = true,
+            .foundInDylib = foundInfo.foundInDylib,
+            .location = (uint8_t*)foundInfo.foundInDylib + foundInfo.value,
+        };
+    } else {
+        if ( log )
+            printf("No target dylib found for %s\n", logContext);
+        return TargetClassFindingResult { .success = false };
+    }
+}
+
+void IMPCachesBuilder::buildClassesMap(Diagnostics& diag) {
+    const uint32_t pointerSize = 8;
+
+    __block std::unordered_map<std::string, DylibAndDeps> dylibMap;
+
+    for (CacheBuilder::DylibInfo& dylib : dylibs) {
+        const dyld3::MachOAnalyzer* ma = dylib.input->mappedFile.mh;
+        __block std::vector<std::string> dependentLibraries;
+        ma->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport,
+                                    bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+            dependentLibraries.push_back(loadPath);
+        });
+        dylibMap[ma->installName()] = { ma, dependentLibraries };
+    }
+
+    const bool log = false;
+
+    __block ClassSet seenClasses;
+
+    for (CacheBuilder::DylibInfo& dylib : dylibs) {
+        const dyld3::MachOAnalyzer* ma = dylib.input->mappedFile.mh;
+        __block std::vector<const dyld3::MachOAnalyzer*> dependentLibraries;
+        ma->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport,
+                                    bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+            auto it = dylibMap.find(loadPath);
+            if (it == dylibMap.end()) {
+                char resolvedSymlinkPath[PATH_MAX];
+
+                // The dylib may link on a dylib which has moved and has a symlink to a new path.
+                if (_fileSystem.getRealPath(loadPath, resolvedSymlinkPath)) {
+                    it = dylibMap.find(resolvedSymlinkPath);
+                }
+            }
+            if (it == dylibMap.end()) {
+                assert(isWeak);
+                dependentLibraries.push_back(nullptr);
+            } else {
+                dependentLibraries.push_back(it->second.ma);
+            }
+        });
+        __block std::vector<BindTarget> bindTargets;
+
+        // Map from vmOffsets in this binary to which bind target they point to
+        __block std::unordered_map<uint64_t, uint64_t> bindLocations;
+        if ( ma->hasChainedFixups() ) {
+            // arm64e binaries
+            ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool &stop) {
+                if (libOrdinal == BIND_SPECIAL_DYLIB_SELF) {
+                   bindTargets.push_back({ symbolName, ma, weakImport });
+                } else if ( libOrdinal < 0 ) {
+                    // A special ordinal such as weak.  Just put in a placeholder for now
+                    bindTargets.push_back({ symbolName, nullptr, weakImport });
+                } else {
+                    assert(libOrdinal <= (int)dependentLibraries.size());
+                    const dyld3::MachOAnalyzer *target = dependentLibraries[libOrdinal-1];
+                    assert(weakImport || (target != nullptr));
+                    bindTargets.push_back({ symbolName, target, weakImport });
+                }
+            });
+            ma->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* startsInfo) {
+                ma->forEachFixupInAllChains(diag, startsInfo, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc,
+                                                                       const dyld_chained_starts_in_segment* segInfo, bool& fixupsStop) {
+                    uint64_t fixupOffset = (uint8_t*)fixupLoc - (uint8_t*)ma;
+                    uint32_t bindOrdinal;
+                    int64_t addend;
+                    if ( fixupLoc->isBind(segInfo->pointer_format, bindOrdinal, addend) ) {
+                        if ( bindOrdinal < bindTargets.size() ) {
+                            bindLocations[fixupOffset] = bindOrdinal;
+                        }
+                        else {
+                            diag.error("out of range bind ordinal %d (max %lu)", bindOrdinal, bindTargets.size());
+                            fixupsStop = true;
+                        }
+                    }
+                });
+            });
+        } else {
+            // Non-arm64e (for now...)
+            ma->forEachBind(diag, ^(uint64_t runtimeOffset, int libOrdinal, const char* symbolName,
+                                    bool weakImport, bool lazyBind, uint64_t addend, bool &stop) {
+                if ( log )
+                    printf("0x%llx %s: %s\n", runtimeOffset, ma->installName(), symbolName);
+                if ( libOrdinal < 0 ) {
+                    // A special ordinal such as weak.  Just put in a placeholder for now
+                    bindTargets.push_back({ symbolName, nullptr, weakImport });
+                } else if (libOrdinal == BIND_SPECIAL_DYLIB_SELF) {
+                    bindTargets.push_back({ symbolName, ma, weakImport });
+                } else {
+                    assert(libOrdinal <= (int)dependentLibraries.size());
+                    bindTargets.push_back({ symbolName, dependentLibraries[libOrdinal - 1], weakImport });
+                }
+                bindLocations[runtimeOffset] = bindTargets.size() - 1;
+            }, ^(const char* symbolName) {
+                // We don't need this as its only for weak def coalescing
+            });
+        }
+        const uint64_t loadAddress = ma->preferredLoadAddress();
+        dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = ma->makeVMAddrConverter(false);
+        ma->forEachObjCClass(diag, vmAddrConverter, ^(Diagnostics& maDiag, uint64_t classVMAddr,
+                                            uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
+                                            const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
+            uint64_t classNameVMAddr = objcClass.nameVMAddr(pointerSize);
+            dyld3::MachOAnalyzer::PrintableStringResult classNameResult = dyld3::MachOAnalyzer::PrintableStringResult::UnknownSection;
+            const char* className = ma->getPrintableString(classNameVMAddr, classNameResult);
+            assert(classNameResult == dyld3::MachOAnalyzer::PrintableStringResult::CanPrint);
+            if ( log )
+                printf("%s: %s\n", ma->installName(), className);
+
+            const uint32_t RO_ROOT = (1<<1);
+            bool isRootClass = (objcClass.flags(pointerSize) & RO_ROOT) != 0;
+            auto result = findTargetClass(ma, objcClass.superclassVMAddr, classSuperclassVMAddr, className, bindLocations, bindTargets, dylibMap);
+
+            if (result.success || isRootClass) {
+                uint64_t classRuntimeOffset = classVMAddr - loadAddress;
+                const uint8_t* classLocation = (const uint8_t*)ma + classRuntimeOffset;
+                objcClasses[classLocation] = ObjCClass {
+                    .superclassMA = (const dyld3::MachOAnalyzer*) result.foundInDylib,
+                    .metaClass = isMetaClass ? nullptr : ((const uint8_t*)ma + objcClass.isaVMAddr - loadAddress),
+                    .superclass = result.location,
+                    .methodListVMaddr = objcClass.baseMethodsVMAddr(pointerSize),
+                    .className = className,
+                    .isRootClass = isRootClass,
+                    .isMetaClass = isMetaClass,
+                };
+
+                ClassKey k {
+                    .name = className,
+                    .metaclass = isMetaClass
+                };
+
+                auto [it, success] = seenClasses.insert(k);
+                if (!success) {
+                    duplicateClasses.insert(k);
+                }
+            }
+        });
+
+        ma->forEachObjCCategory(diag, vmAddrConverter, ^(Diagnostics& maDiag, uint64_t categoryVMAddr, const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) {
+            dyld3::MachOAnalyzer::PrintableStringResult catNameResult = dyld3::MachOAnalyzer::PrintableStringResult::UnknownSection;
+            const char *catName = ma->getPrintableString(objcCategory.nameVMAddr, catNameResult);
+
+            uint64_t clsVMAddr = categoryVMAddr + pointerSize;
+            TargetClassFindingResult result = findTargetClass(ma, objcCategory.clsVMAddr, clsVMAddr, catName, bindLocations, bindTargets, dylibMap);
+            uint64_t catRuntimeOffset = categoryVMAddr - loadAddress;
+            const uint8_t *catLocation = (const uint8_t*)ma + catRuntimeOffset;
+            if (result.success) {
+                objcCategories[catLocation] = ObjCCategory {
+                    .classMA = (const dyld3::MachOAnalyzer*)result.foundInDylib,
+                    .cls = result.location
+                };
+            } else {
+                // This happens for categories on weak classes that may be missing.
+                objcCategories[catLocation] = ObjCCategory {
+                    .classMA = (const dyld3::MachOAnalyzer*)nullptr,
+                    .cls = nullptr
+                };
+            }
+        });
+    }
+
+    // Print the class hierarchy just to see that we found everything
+    if (log) {
+        for (const auto& [location, theClass] : objcClasses) {
+            printf("%p %c%s", location, theClass.isMetaClass ? '+' : '-', theClass.className);
+            bool isRoot = theClass.isRootClass;
+            const uint8_t* superclass = theClass.superclass;
+            while ( !isRoot ) {
+                auto it = objcClasses.find(superclass);
+                // assert(it != objcClasses.end());
+                if (it == objcClasses.end()) {
+                    printf(": missing");
+                    break;
+                }
+                printf(" : %c%s", it->second.isMetaClass ? '+' : '-', it->second.className);
+                isRoot = it->second.isRootClass;
+                superclass = it->second.superclass;
+            }
+            printf("\n");
+        }
+    }
+}
+
+const std::string * IMPCachesBuilder::nameAndIsMetaclassPairFromNode(const dyld3::json::Node & node, bool* metaclass) {
+    const dyld3::json::Node& metaclassNode = dyld3::json::getRequiredValue(_diagnostics, node, "metaclass");
+    if (_diagnostics.hasError()) return nullptr;
+
+    if (metaclass != nullptr) *metaclass  = dyld3::json::parseRequiredInt(_diagnostics, metaclassNode) != 0;
+    const dyld3::json::Node& nameNode = dyld3::json::getRequiredValue(_diagnostics, node, "name");
+    if (_diagnostics.hasError()) return nullptr;
+
+    return &(dyld3::json::parseRequiredString(_diagnostics, nameNode));
+}
+
+IMPCachesBuilder::IMPCachesBuilder(std::vector<CacheBuilder::DylibInfo>& allDylibs, const dyld3::json::Node& optimizerConfiguration, Diagnostics& diag, TimeRecorder& timeRecorder, const dyld3::closure::FileSystem& fileSystem) : dylibs(allDylibs), _diagnostics(diag), _timeRecorder(timeRecorder), _fileSystem(fileSystem) {
+    const dyld3::json::Node * version = dyld3::json::getOptionalValue(diag, optimizerConfiguration, "version");
+    int64_t versionInt = (version != NULL) ? dyld3::json::parseRequiredInt(diag, *version) : 1;
+    if (versionInt == 2) {
+        // v2 has a single neededClasses array, with a key to know if it's a metaclass
+        // or class. This lets us order them by importance so that we handle the important
+        // cases first in the algorithm, while it's still easy to place things (as we process
+        // more classes, constraints build up and we risk dropping difficult classes)
+
+        const dyld3::json::Node& classes = dyld3::json::getRequiredValue(diag, optimizerConfiguration, "neededClasses");
+        if (diag.hasError()) return;
+
+        int i = 0;
+        for (const dyld3::json::Node& n : classes.array) {
+            bool metaclass = false;
+            const std::string *name = nameAndIsMetaclassPairFromNode(n, &metaclass);
+            if (name != nullptr) {
+                if (metaclass) {
+                    neededMetaclasses[*name] = i++;
+                } else {
+                    neededClasses[*name] = i++;
+                }
+            } else {
+                // nameAndIsMetaclassPairFromNode already logs an error in this case,
+                // so nothing to do here
+            }
+        }
+    } else {
+        auto metaclasses = optimizerConfiguration.map.find("neededMetaclasses");
+        int i = 0;
+
+        if (metaclasses != optimizerConfiguration.map.cend()) {
+            for (const dyld3::json::Node& n : metaclasses->second.array) {
+                neededMetaclasses[n.value] = i++;
+            }
+        }
+
+        auto classes = optimizerConfiguration.map.find("neededClasses");
+
+        if (classes != optimizerConfiguration.map.cend()) {
+            for (const dyld3::json::Node& n : classes->second.array) {
+                neededClasses[n.value] = i++;
+            }
+        }
+    }
+
+    auto sels = optimizerConfiguration.map.find("selectorsToInline");
+    if (sels != optimizerConfiguration.map.cend()) {
+        for (const dyld3::json::Node& n : sels->second.array) {
+            selectorsToInline.insert(n.value);
+        }
+    }
+
+    const dyld3::json::Node* classHierarchiesToFlattenNode = dyld3::json::getOptionalValue(diag, optimizerConfiguration, "flatteningRoots");
+    if (classHierarchiesToFlattenNode != nullptr) {
+        for (const dyld3::json::Node& n : classHierarchiesToFlattenNode->array) {
+            bool metaclass = false;
+            const std::string *name = nameAndIsMetaclassPairFromNode(n, &metaclass);
+            if (metaclass) {
+                metaclassHierarchiesToFlatten.insert(*name);
+            } else {
+                classHierarchiesToFlatten.insert(*name);
+            }
+        }
+    } else {
+        // For old files, we assume we should flatten OS_object, this was implied
+        // before we decided to extend this set.
+
+        metaclassHierarchiesToFlatten.insert("OS_object");
+        classHierarchiesToFlatten.insert("OS_object");
+    }
+}
+
+struct BacktrackingState {
+    // Index into the next array that we are currently trying
+    int currentAttemptIndex;
+
+    // Possible placement attempts for this class
+    std::vector<typename IMPCaches::ClassData::PlacementAttempt> attempts;
+
+    // What we had to modify to attempt the current placement. This needs
+    // to be reversed if we backtrack.
+    std::optional<IMPCaches::ClassData::PlacementAttempt::Result> result;
+
+    // We also need to store the state of the random number generator,
+    // because when reverting to a snapshot we need to apply exactly the same
+    // steps as last time.
+    std::minstd_rand randomNumberGenerator;
+
+    bool operator== (const BacktrackingState & other) {
+        // Don't test attempts, they will be the same as long as the class index is
+        // the same, and we never compare states for different indices
+        return (currentAttemptIndex == other.currentAttemptIndex) && (randomNumberGenerator == other.randomNumberGenerator);
+    }
+
+    bool operator!= (const BacktrackingState & other) {
+        return !(*this == other);
+    }
+};
+
+static void backtrack(std::vector<BacktrackingState>& backtrackingStack, int& numberOfDroppedClasses, std::vector<IMPCaches::ClassData*>& allClasses) {
+    int i = (int)backtrackingStack.size() - 1;
+    assert(i>0);
+    BacktrackingState & last = backtrackingStack.back();
+    if (!last.result) {
+        // backtracking over a skipped class
+        numberOfDroppedClasses--;
+    }
+    allClasses[i]->backtrack(*(last.result));
+    backtrackingStack.pop_back();
+};
+
+template <typename Func>
+static void forEachClassInFlatteningHierarchy(const IMPCaches::ClassData *parentClass, const std::vector<IMPCaches::ClassData*>& allClasses, const Func &callback) {
+    if (!parentClass->flatteningRootSuperclass.has_value()) {
+        return;
+    }
+    for (IMPCaches::ClassData * c : allClasses) {
+        // If c has parentClass in its flattening hierarchy
+        if ((c != parentClass)
+            && c->flatteningRootSuperclass.has_value()
+            && (*(c->flatteningRootSuperclass) == *(parentClass->flatteningRootSuperclass))
+            && (c->flatteningRootName == parentClass->flatteningRootName)
+            && (c->flattenedSuperclasses.find(parentClass->name) != c->flattenedSuperclasses.end())) {
+            callback(c);
+        }
+    }
+}
+
+static void dropClass(Diagnostics& diagnostics, unsigned long& currentClassIndex, int& numberOfDroppedClasses, std::vector<BacktrackingState>& backtrackingStack, std::minstd_rand& randomNumberGenerator, std::vector<IMPCaches::ClassData*>& allClasses, const char* reason) {
+    IMPCaches::ClassData* droppedClass = allClasses[currentClassIndex];
+
+    diagnostics.verbose("%lu: dropping class %s (%s) because %s\n", currentClassIndex, droppedClass->name, droppedClass->isMetaclass ? "metaclass" : "class", reason);
+    droppedClass->shouldGenerateImpCache = false;
+
+    // If we are inside a flattened hierarchy, we need to also drop any classes inheriting from us,
+    // as objc relies on all classes inside a flattened hierarchy having constant caches to do invalidation
+    // properly.
+    forEachClassInFlatteningHierarchy(droppedClass, allClasses, [&numberOfDroppedClasses, &diagnostics, currentClassIndex](IMPCaches::ClassData *c){
+        // Drop it as well.
+        // We could undrop them if we undrop droppedClass while backtracking or restoring
+        // a snapshot, but it's not worth the effort.
+
+        if (c->shouldGenerateImpCache) {
+            numberOfDroppedClasses++;
+            c->shouldGenerateImpCache = false;
+            c->droppedBecauseFlatteningSuperclassWasDropped = true;
+            diagnostics.verbose("%lu: also dropping %s (%s) in the same flattening hierarchy\n", currentClassIndex, c->name, c->isMetaclass ? "metaclass" : "class");
+        }
+    });
+
+    currentClassIndex++;
+    numberOfDroppedClasses++;
+
+    BacktrackingState state = {
+        .currentAttemptIndex = 0,
+        .randomNumberGenerator = randomNumberGenerator
+    };
+    
+    backtrackingStack.push_back(state);
+};
+
+static void resetToSnapshot(std::vector<BacktrackingState>& backtrackingStack, std::vector<BacktrackingState>& bestSolutionSnapshot, std::vector<IMPCaches::ClassData*>& allClasses, int& numberOfDroppedClasses) {
+
+    // First, backtrack if needed until we reach the first different step.
+    int firstDifferentStep = -1;
+    for (int i = 0 ; i < backtrackingStack.size() && i < bestSolutionSnapshot.size() ; i++) {
+        if (backtrackingStack[i] != bestSolutionSnapshot[i]) {
+            firstDifferentStep = i;
+            break;
+        }
+    }
+
+    if (firstDifferentStep == -1) {
+        firstDifferentStep = MIN((int)backtrackingStack.size(), (int)bestSolutionSnapshot.size());
+    }
+    
+    while (backtrackingStack.size() > firstDifferentStep) {
+        backtrack(backtrackingStack, numberOfDroppedClasses, allClasses);
+    }
+
+    // Then apply the steps needed to get to the snapshot.
+    if (firstDifferentStep < bestSolutionSnapshot.size()) {
+        for (int i = (int)backtrackingStack.size() ; i < bestSolutionSnapshot.size() ; i++) {
+            BacktrackingState & state = bestSolutionSnapshot[i];
+
+            // Make a copy in order not to mutate it should we need to go back...
+            std::minstd_rand stateRandomNumberGenerator = state.randomNumberGenerator;
+            if (state.result) {
+                assert(state.currentAttemptIndex < state.attempts.size());
+                typename IMPCaches::ClassData::PlacementAttempt::Result result = allClasses[i]->applyAttempt(state.attempts[state.currentAttemptIndex], stateRandomNumberGenerator);
+                assert(result.success);
+
+                if (!allClasses[i]->droppedBecauseFlatteningSuperclassWasDropped) {
+                    // shouldGenerateImpCache might have been flipped to false during backtracking
+                    // we're restoring to a snapshot where we did place this class, so restore
+                    // the success bit...
+                    // ... unless we had decided to drop it because other classes were dropped
+                    // (in that case give up and don't attempt to generate a cache for it,
+                    //  but still apply the attempt above in order to set the right constraints
+                    //  on each selector, which is necessary for snapshot reproducibility)
+
+                    allClasses[i]->shouldGenerateImpCache = true;
+                }
+            } else {
+                numberOfDroppedClasses++;
+            }
+
+            backtrackingStack.push_back(state);
+        }
+    }
+}
+
+/// Finds a shift and mask for each class, and start assigning the bits of the selector addresses
+int IMPCachesBuilder::findShiftsAndMasks() {
+    // Always seed the random number generator with 0 to get reproducibility.
+    // Note: in overflow scenarios, findShiftsAndMasks can be called more than once,
+    // so make sure to always use the same value when we enter this method.
+    std::minstd_rand randomNumberGenerator(0);
+    
+    // This is a backtracking algorithm, so we need a stack to store our state
+    // (It goes too deep to do it recursively)
+    std::vector<BacktrackingState> backtrackingStack;
+
+    // Index of the class we're currently looking at.
+    unsigned long currentClassIndex = 0;
+    
+    // This lets us backtrack by more than one step, going back eg. 4 classes at a time.
+    // Yes, this means we're not exploring the full solution space, but it's OK because
+    // there are many solutions out there and we prefer dropping a few classes here and
+    // there rather than take hours to find the perfect solution.
+    unsigned long backtrackingLength = 1;
+
+    // Indices of the attempt we had chosen for each class last time we reached the maximum
+    // number of classes placed so far.
+    std::vector<BacktrackingState> bestSolutionSnapshot;
+
+#if 0
+    // Debugging facilities where we store the full state corresponding
+    // to bestSolutionSnapshot to make sure restoring snapshots works.
+    std::vector<IMPCaches::ClassData> bestSolutionSnapshotClasses;
+    std::unordered_map<std::string_view, IMPCaches::Selector> bestSolutionSnapshotSelectors;
+#endif
+
+    // Number of times we have backtracked. When this becomes too high, we go back to the
+    // previous snapshot and drop the faulty class.
+    unsigned long backtrackingAttempts = 0;
+
+    // Go through all the classes and find a shift and mask for each,
+    // backtracking if needed.
+    std::vector<IMPCaches::ClassData*> allClasses;
+    fillAllClasses(allClasses);
+    
+    int numberOfDroppedClasses = 0;
+
+    while (currentClassIndex < allClasses.size()) {
+        /* for (int i = 0 ; i < backtrackingStack.size() ; i++) {
+            assert(backtrackingStack[i].attempts.size() > 0 || !allClasses[i]->shouldGenerateImpCache);
+        } */
+
+        assert(// Either we are adding a new state...
+               (currentClassIndex == backtrackingStack.size())
+               // Or we are backtracking and building on the last state recorded
+               || (currentClassIndex == (backtrackingStack.size() - 1)));
+
+        IMPCaches::ClassData* c = allClasses[currentClassIndex];
+
+        if (!c->shouldGenerateImpCache) {
+            // We have decided to drop this one before, so don't waste time.
+            dropClass(_diagnostics, currentClassIndex, numberOfDroppedClasses, backtrackingStack, randomNumberGenerator, allClasses, "we have dropped it before");
+            continue;
+        }
+
+        if (c->isPartOfDuplicateSet) {
+            dropClass(_diagnostics, currentClassIndex, numberOfDroppedClasses, backtrackingStack, randomNumberGenerator, allClasses, "it is part of a duplicate set");
+            continue;
+        }
+
+        if (currentClassIndex >= backtrackingStack.size()) {
+            // We're at the top of the stack. Make a fresh state.
+
+            BacktrackingState state;
+            state.attempts = c->attempts();
+            state.currentAttemptIndex = 0;
+            state.randomNumberGenerator = randomNumberGenerator;
+            backtrackingStack.push_back(state);
+        } else {
+            // We are backtracking ; don't retry the attempt we tried before, use the next one.
+            backtrackingStack[currentClassIndex].currentAttemptIndex++;
+
+            // Note that we do not reset randomNumberGenerator to state.randomNumberGenerator
+            // here, because when backtracking we want to explore a different set of
+            // possibilities, so let's try other placements.
+        }
+
+        assert(backtrackingStack.size() == currentClassIndex + 1);
+
+        BacktrackingState& state = backtrackingStack[currentClassIndex];
+
+        bool placed = false;
+
+        // Go through all the possible placement attempts for this class.
+        // If one succeeds, place the next class, and if needed we'll backtrack and try the next attempt, etc.
+        // This is basically an iterative backtracking because
+        // we don't want the stack to get too deep.
+        std::vector<typename IMPCaches::ClassData::PlacementAttempt> & attempts = state.attempts;
+        for (int operationIndex = state.currentAttemptIndex ; operationIndex < (int)attempts.size() ; operationIndex++) {
+            // Save the state of the random number generator so that we can save its
+            // state before applying the attempt in the backtracking stack if needed.
+            std::minstd_rand maybeSuccessfulRNG = randomNumberGenerator;
+            typename IMPCaches::ClassData::PlacementAttempt::Result result = c->applyAttempt(attempts[operationIndex], randomNumberGenerator);
+            if (result.success) {
+                if (currentClassIndex % 1000 == 0) {
+                    _diagnostics.verbose("[IMP Caches] Placed %lu / %lu classes\n", currentClassIndex, allClasses.size());
+                }
+
+                //fprintf(stderr, "%lu / %lu: placed %s with operation %d/%lu (%s)\n", currentClassIndex, allClasses.size(), c->description().c_str(), operationIndex, attempts.size(), attempts[operationIndex].description().c_str());
+                placed = true;
+                state.result = result;
+                state.currentAttemptIndex = operationIndex;
+                state.randomNumberGenerator = maybeSuccessfulRNG;
+                break;
+            }
+        }
+
+        if (placed) {
+            currentClassIndex += 1;
+        } else {
+            // Remove the current state, which has just failed and does not matter.
+            // (It was never applied).
+            backtrackingStack.pop_back();
+
+            backtrackingAttempts++;
+            if (backtrackingAttempts > 10) {
+                // Reset to the best snapshot and drop the next class
+
+                resetToSnapshot(backtrackingStack, bestSolutionSnapshot, allClasses, numberOfDroppedClasses);
+
+#if 0
+// Expensive book-keeping to make sure that resetting to the snapshot worked.
+                for (const auto & [k,v] : selectors.map) {
+                    const IMPCaches::Selector & theoretical = bestSolutionSnapshotSelectors[k];
+                    const IMPCaches::Selector & actual = *v;
+
+                    if (theoretical != actual) {
+                        fprintf(stderr, "Failed to restore snapshot of %lu classes; method %s differs, (%x, %x) vs (%x, %x)\n", bestSolutionSnapshot.size(), k.data(), theoretical.inProgressBucketIndex, theoretical.fixedBitsMask, actual.inProgressBucketIndex, actual.fixedBitsMask);
+                        assert(theoretical == actual);
+                    }
+                }
+#endif
+
+                _diagnostics.verbose("*** SNAPSHOT: successfully reset to snapshot of size %lu\n", bestSolutionSnapshot.size());
+
+                currentClassIndex = backtrackingStack.size();
+                dropClass(_diagnostics, currentClassIndex, numberOfDroppedClasses, backtrackingStack, randomNumberGenerator, allClasses, "it's too difficult to place");
+
+                // FIXME: we should consider resetting backtrackingLength to the value it had when we snapshotted here (the risk makes this not worth trying at this point in the release).
+
+                backtrackingAttempts = 0;
+                continue;
+            } else {
+                if (currentClassIndex > bestSolutionSnapshot.size()) {
+                    _diagnostics.verbose("*** SNAPSHOT *** %lu / %lu (%s)\n", currentClassIndex, allClasses.size(), c->description().c_str());
+                    bestSolutionSnapshot = backtrackingStack;
+
+#if 0
+                    // Make a full copy of the state so that we can debug resetting to snapshots
+                    bestSolutionSnapshotClasses.clear();
+                    bestSolutionSnapshotSelectors.clear();
+                    for (const auto & [k,v] : selectors.map) {
+                        bestSolutionSnapshotSelectors[k] = *v;
+                    }
+#endif
+                }
+
+                _diagnostics.verbose("%lu / %lu (%s): backtracking\n", currentClassIndex, allClasses.size(), c->description().c_str());
+                assert(currentClassIndex != 0); // Backtracked all the way to the beginning, no solution
+
+                for (unsigned long j = 0 ; j < backtrackingLength ; j++) {
+                    backtrack(backtrackingStack, numberOfDroppedClasses, allClasses);
+                    currentClassIndex--;
+                }
+
+                backtrackingLength = std::max(1ul,std::min(std::min(currentClassIndex, backtrackingLength * 2), 1024ul));
+            }
+        }
+    }
+    
+    if (numberOfDroppedClasses > 0) {
+        _diagnostics.verbose("Dropped %d classes that were too difficult to place\n", numberOfDroppedClasses);
+    }
+    
+    return numberOfDroppedClasses;
+}
+
+void IMPCachesBuilder::fillAllClasses(std::vector<IMPCaches::ClassData*> & allClasses) {
+    for (const CacheBuilder::DylibInfo & d : dylibs) {
+        typedef typename decltype(d.impCachesClassData)::value_type classpair;
+
+        for (const auto& [key, thisClassData]: d.impCachesClassData) {
+            if (thisClassData->methods.size() > 0 && thisClassData->shouldGenerateImpCache) {
+                allClasses.push_back(thisClassData.get());
+            }
+        }
+    }
+
+
+    // Only include the classes for which there is actual work to do,
+    // otherwise we have classes with only 1 choice which makes our
+    // partial backtracking more difficult.
+
+    std::sort(allClasses.begin(), allClasses.end(), [this](IMPCaches::ClassData* a, IMPCaches::ClassData* b) {
+        int indexA = a->isMetaclass ? neededMetaclasses[a->name] : neededClasses[a->name];
+        int indexB = b->isMetaclass ? neededMetaclasses[b->name] : neededClasses[b->name];
+
+        return (indexA < indexB);
+    });
+}
+
+void IMPCachesBuilder::removeUninterestingClasses() {
+    // Remove any empty classes and classes for which we don't generate IMP caches now that we've inlined all selectors
+    // (These classes were just used for inlining purposes)
+    
+    for (CacheBuilder::DylibInfo& d : dylibs) {
+        for (auto it = d.impCachesClassData.begin() ; it != d.impCachesClassData.end() ; /* no increment here */) {
+            auto& c = it->second;
+            if (((c->methods.size() == 0) && !(c->flatteningRootSuperclass.has_value()))
+                || !c->shouldGenerateImpCache) {
+                // Remove this useless class: delete it from the selectors, and from the master class map
+                // Note that it is not useless if it is in a flattening hierarchy: all classes in a flattening
+                // hierarchy must have preopt caches so that objc correctly invalidates the caches on children
+                // when you attach a category to one of the classes in a flattening hierarchy
+                
+                for (auto& m : c->methods) {
+                    auto& classes = m.selector->classes;
+                    classes.erase(std::remove(classes.begin(), classes.end(), c.get()), classes.end());
+                }
+                
+                it = d.impCachesClassData.erase(it);
+            } else {
+                it++;
+            }
+        }
+    }
+
+    
+    // Now remove from the selector map any selectors that are not used by any classes
+
+    addressSpace.removeUninterestingSelectors();
+    for (auto it = selectors.map.begin() ; it != selectors.map.end() ; /* no increment */ ) {
+        Selector & s = *(it->second);
+        
+        if ((s.classes.size() == 0) && (it->first != magicSelector)) {
+            it = selectors.map.erase(it);
+        } else {
+            it++;
+        }
+    }
+}
+
+void IMPCachesBuilder::fillAllMethods(std::vector<IMPCaches::Selector*> & allMethods) {
+    typedef typename decltype(selectors.map)::value_type methodpair;
+    for (auto& [name, selectorData] : selectors.map) {
+        // Remove all non-interesting classes that were added only for inlining tracking.
+        if (selectorData->classes.size() > 0) {
+            allMethods.push_back(selectorData.get());
+        }
+    }
+}
+
+// Main entry point of the algorithm, chaining all the steps.
+void IMPCachesBuilder::buildPerfectHashes(IMPCaches::HoleMap& holeMap, Diagnostics& diag) {
+    _timeRecorder.pushTimedSection();
+    int droppedClasses = findShiftsAndMasks();
+    _timeRecorder.recordTime("find shifts and masks");
+    
+    if (droppedClasses > 0) {
+        removeUninterestingClasses();
+    }
+    
+    droppedClasses = solveGivenShiftsAndMasks();
+
+    if (droppedClasses > 0) {
+        removeUninterestingClasses();
+    }
+
+    computeLowBits(holeMap);
+
+    _timeRecorder.recordTime("assign selector addresses");
+    _timeRecorder.popTimedSection();
+}
+
+size_t IMPCachesBuilder::totalIMPCachesSize() const {
+    size_t size = 0;
+    for (CacheBuilder::DylibInfo& d : dylibs) {
+        for (const auto& [k,v] : d.impCachesClassData) {
+            assert(v->shouldGenerateImpCache);
+            size += v->sizeInSharedCache();
+        }
+    }
+    return size;
+}
+
+void IMPCachesBuilder::computeLowBits(IMPCaches::HoleMap& holeMap) {
+    holeMap.clear();
+    addressSpace.computeLowBits(holeMap);
+}
+
+// Shuffles selectors around to satisfy size constraints
+int IMPCachesBuilder::solveGivenShiftsAndMasks() {
+    std::vector<IMPCaches::ClassData*> allClasses;
+    fillAllClasses(allClasses);
+
+    int hadToIncreaseSizeCount = 0;
+    int droppedClasses = 0;
+    
+    // Sanity check: all methods should have a fixed bits mask
+    // that at least encompasses the masks of all the classes they are in.
+    for (const IMPCaches::ClassData* c : allClasses) {
+        for (const ClassData::Method& m : c->methods) {
+            assert(((m.selector->fixedBitsMask >> c->shift) & c->mask()) == c->mask());
+        }
+        if (c->hadToIncreaseSize()) {
+            hadToIncreaseSizeCount++;
+        }
+    }
+
+    // Sanity check: all classes should have a valid shift and mask.
+    for (IMPCaches::ClassData* c : allClasses) {
+        assert(c->checkConsistency());
+    }
+
+
+    // Now that everything is placed, try to adjust placement within the
+    // constraints so that we can respect alignment
+
+    _diagnostics.verbose("[IMP Caches] Placed %lu classes, increasing hash table size for %d\n", allClasses.size(), hadToIncreaseSizeCount);
+
+    std::vector<IMPCaches::Selector*> methodsSortedByNumberOfFixedBits;
+    fillAllMethods(methodsSortedByNumberOfFixedBits);
+
+    std::sort(methodsSortedByNumberOfFixedBits.begin(), methodsSortedByNumberOfFixedBits.end(), [](const IMPCaches::Selector* a, const IMPCaches::Selector* b) -> bool {
+
+        // Place the methods with the greatest number of fixed bits first
+        // as they will have the most constraints
+
+        // If we have the same number of fixed bits, place the methods
+        // in the largest number of classes first, as they will likely
+        // have more constraints on their bits
+
+        std::tuple<int, int, std::string_view> ta = std::make_tuple(a->numberOfSetBits(), a->classes.size(), std::string_view(a->name));
+        std::tuple<int, int, std::string_view> tb = std::make_tuple(b->numberOfSetBits(), b->classes.size(), std::string_view(b->name));
+
+        return ta > tb;
+    });
+
+    std::default_random_engine generator;
+
+    _diagnostics.verbose("[IMP Caches] Rearranging selectors in 128-byte buckets…\n");
+
+    IMPCaches::ConstraintSet cs;
+    for (unsigned long methodIndex = 0 ; methodIndex < methodsSortedByNumberOfFixedBits.size() ; methodIndex++) {
+        IMPCaches::Selector* m = methodsSortedByNumberOfFixedBits[methodIndex];
+        if (addressSpace.canPlaceMethodAtIndex(m, m->inProgressBucketIndex)) {
+            addressSpace.placeMethodAtIndex(m, m->inProgressBucketIndex);
+        } else {
+            // Try to find another address for m
+            cs.clear();
+
+#if DEBUG
+            std::vector<IMPCaches::ClassData*> sortedClasses = m->classes;
+
+            // Sort the classes so that we can always debug the same thing.
+            std::sort(sortedClasses.begin(), sortedClasses.end(), [](const IMPCaches::ClassData* a, const IMPCaches::ClassData* b) {
+                return *a < *b;
+            });
+            
+            std::vector<IMPCaches::ClassData*> & classes = sortedClasses;
+#else
+            std::vector<IMPCaches::ClassData*> & classes = m->classes;
+#endif
+
+            bool atLeastOneConstraint = false;
+            
+            // Go through all the classes the method is used in and add constraints
+            for (IMPCaches::ClassData* c : classes) {
+                if (!c->shouldGenerateImpCache) continue;
+                atLeastOneConstraint = true;
+                IMPCaches::Constraint constraint = c->constraintForMethod(m);
+                cs.add(constraint);
+            }
+            
+            if (!atLeastOneConstraint) {
+                // This method is only used in classes we have just dropped.
+                continue;
+            }
+            
+            auto dropClassesWithThisMethod = [this, &classes, &allClasses, &droppedClasses](){
+                for (IMPCaches::ClassData* c : classes) {
+                    c->shouldGenerateImpCache = false;
+                    _diagnostics.verbose("Dropping class %s, selectors too difficult to place\n", c->name);
+                    droppedClasses++;
+                    forEachClassInFlatteningHierarchy(c, allClasses, [this](IMPCaches::ClassData *toDrop) {
+                        if (toDrop->shouldGenerateImpCache) {
+                            toDrop->shouldGenerateImpCache = false;
+                            toDrop->droppedBecauseFlatteningSuperclassWasDropped = true;
+                            _diagnostics.verbose("Dropping class %s in the same flattening hierarchy\n", toDrop->name);
+                        }
+                    });
+                }
+            };
+
+            IMPCaches::Constraint& mergedConstraint = *(cs.mergedConstraint);
+
+            if (mergedConstraint.allowedValues.size() == 0) {
+                dropClassesWithThisMethod();
+                continue;
+            }
+
+            bool foundValue = false;
+            std::unordered_set<int> & allowedValues = mergedConstraint.allowedValues;
+            int modulo = mergedConstraint.mask + 1;
+            int multiplier = 1 << mergedConstraint.shift;
+            // We want to go through:
+            // [((0 + allowedValues) << shift) + k, ((modulo + allowedValues) << shift) + k, ((2*modulo + allowedValue) << shift) + k, ....] etc.
+            // but we want to randomize this so that we don't completely
+            // fill up the small addresses. If we do, and we end up with a
+            // constraint that forces us to zero the high bits, we'll fail
+            // to find room for the selector.
+
+            // Range for the multiplier of the modulo above
+            int addressesCount = std::max(((addressSpace.maximumIndex + 1) >> mergedConstraint.shift) / modulo, 1);
+
+            // Fill "addresses" with [0, addressesCount[ so that we can shuffle it below
+            std::vector<int> addresses(addressesCount);
+            std::iota(addresses.begin(), addresses.end(), 0);
+
+            for (int i = 0 ; i < addressesCount ; i++) {
+                if (foundValue) {
+                    break;
+                }
+
+                // Manual Fisher-Yates:
+                // Pick a random element in [i, end[. Swap it with the i^th element. Repeat if the random element didn't work.
+                // We don't do std::shuffle because it wastes time to shuffle the whole range if we find happiness in the beginning.
+                std::uniform_int_distribution<int> distribution(i,addressesCount-1);
+                int rd = distribution(generator);
+                int baseAddress = addresses[rd];
+                std::swap(addresses[i], addresses[rd]);
+
+                for (int j : allowedValues) {
+                    if (foundValue) {
+                        break;
+                    }
+
+                    for (int k = 0 ; k < multiplier; k++) {
+                        int bucketIndex = ((baseAddress * modulo + j) << mergedConstraint.shift) | k;
+                        if (bucketIndex >= addressSpace.maximumIndex) {
+                            continue;
+                        }
+
+                        bool canPlace = addressSpace.canPlaceMethodAtIndex(m, bucketIndex);
+                        if (!canPlace) {
+                            continue;
+                        }
+
+                        foundValue = true;
+                        //std::cerr << methodIndex << "/" << methodsSortedByNumberOfFixedBits.size() << " Moving " << m->name << " to " << address << "\n";
+                        m->inProgressBucketIndex = bucketIndex;
+                        addressSpace.placeMethodAtIndex(m, bucketIndex);
+                        break;
+                    }
+                }
+            }
+            
+            if (!foundValue) {
+                _diagnostics.verbose("Failed to place %s\n", m->name);
+                dropClassesWithThisMethod();
+            }
+        }
+
+        if (methodIndex % 1000 == 0) {
+            _diagnostics.verbose("  %lu/%lu…\n", methodIndex, methodsSortedByNumberOfFixedBits.size());
+        }
+
+    }
+
+    if (droppedClasses == 0) {
+        _diagnostics.verbose("[IMP Caches] Placed all methods\n");
+    } else {
+        _diagnostics.verbose("[IMP Caches] Finished placing methods, dropping %d classes\n", droppedClasses);
+    }
+
+    constexpr bool log = false;
+    if (log) {
+        std::cerr << addressSpace << "\n";
+    }
+    
+    return droppedClasses;
+}
+
+SelectorMap::SelectorMap() {
+    std::unique_ptr<IMPCaches::Selector> magicSelectorStruct = std::make_unique<IMPCaches::Selector>();
+    magicSelectorStruct->name = magicSelector.data();
+    magicSelectorStruct->offset = 0;
+    map[magicSelector] = std::move(magicSelectorStruct);
+}
+
+HoleMap::HoleMap() {
+    addStringOfSize(magicSelector.length() + 1);
+}
+
+bool ClassData::ClassLocator::operator==(const ClassData::ClassLocator & other) {
+    return (segmentIndex == other.segmentIndex)
+        && (segmentOffset == other.segmentOffset)
+        && (strcmp(installName, other.installName) == 0);
+}
+
+bool ClassData::operator==(const ClassData & other) {
+    if (strcmp(name, other.name) != 0) {
+        return false;
+    }
+    if (methods.size() != other.methods.size()) {
+        return false;
+    }
+    for (unsigned i = 0 ; i < methods.size() ; i++) {
+        if (*(methods[i].selector) != *(other.methods[i].selector)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+};
diff --git a/dyld3/shared-cache/IMPCaches.hpp b/dyld3/shared-cache/IMPCaches.hpp
new file mode 100644 (file)
index 0000000..40ca59a
--- /dev/null
@@ -0,0 +1,380 @@
+//
+//  IMPCaches.hpp
+//  dyld_shared_cache_builder
+//
+//  Created by Thomas Deniau on 18/12/2019.
+//
+
+#ifndef IMPCaches_hpp
+#define IMPCaches_hpp
+
+#include <vector>
+#include <unordered_map>
+#include <unordered_set>
+#include <set>
+#include <map>
+#include <string>
+#include <random>
+
+#define DEBUG_SLOTS 1
+
+template <typename P> class objc_method_t;
+
+namespace IMPCaches {
+class Selector;
+class Constraint;
+
+class ClassData {
+private:
+#if DEBUG_SLOTS
+    std::vector<const Selector*> slots;
+#else
+    std::vector<bool> slots;
+#endif
+
+    // Scratchpad for constraintForMethod
+    std::vector<int> allowedValues;
+
+    void resetSlots();
+
+public:
+    bool isMetaclass;
+
+    // True most of the time, but can also be false in 2 cases:
+    // * We have entries for classes for which we don't want to generate IMP caches
+    //   when they are superclasses of "interesting" (= for which we want an IMP cache)
+    //   classes, so that we can properly attach non-cross-image categories to them and
+    //   reference the right IMP for child classes which are actually interesting
+    // * We can also have failed to generate a cache for this class, or for a class
+    //   in its flattening superclass hierarchy
+    bool shouldGenerateImpCache;
+
+    // Class has duplicates or is a child of a class with duplicates.
+    bool isPartOfDuplicateSet = false;
+
+    // This is set when we drop a class because a class in its flattening superclasss
+    // hierarchy was dropped. In that case, we won't try to flip its shouldGenerateImpCache
+    // value back to true when restoring a snapshot. (We could keep track of all the
+    // dependencies but it would be very messy and the reward is only a few classes here and there).
+    bool droppedBecauseFlatteningSuperclassWasDropped = false;
+
+    uint8_t backtracks = 0;
+
+    // For debug purposes
+    const char* name;
+
+    struct Method {
+        const char* installName = nullptr;
+        const char* className = nullptr;
+        const char* categoryName = nullptr;
+        Selector* selector = nullptr;
+        bool wasInlined = false;
+        bool fromFlattening = false;
+    };
+
+    std::vector<Method> methods;
+
+    std::string description() const;
+
+    int neededBits;
+
+    void didFinishAddingMethods();
+
+    // Not const because this uses the slots as a scratchpad
+    Constraint constraintForMethod(const Selector* m);
+
+    bool operator<(const ClassData& r) const {
+        if (isMetaclass != r.isMetaclass) return isMetaclass;
+        return strcmp(name, r.name) < 0;
+    }
+
+    struct PlacementAttempt {
+        int numberOfBitsToSet;
+        int shift;
+        int neededBits;
+
+        PlacementAttempt(int _numberOfBitsToSet, int _shift, int _neededBits) : numberOfBitsToSet(_numberOfBitsToSet), shift(_shift), neededBits(_neededBits) {
+
+        }
+
+        std::string description() const;
+
+        friend bool operator<(const PlacementAttempt& l, const PlacementAttempt& r)
+        {
+            return  std::tie(l.neededBits, l.numberOfBitsToSet, l.shift)
+            < std::tie(r.neededBits, r.numberOfBitsToSet, r.shift);
+        }
+
+        int mask() const {
+            return (1 << neededBits) - 1;
+        }
+
+        struct PreviousMethodAddress {
+            int address;
+            int fixedBitsMask;
+        };
+
+        struct PreviousState {
+            int neededBits;
+            int shift;
+            int mask;
+            std::unordered_map<Selector*, PreviousMethodAddress> methods;
+        };
+
+        struct Result {
+            bool success;
+            PreviousState previousState;
+        };
+    };
+
+    // Possibilities we go through in the greedy backtracking algorithm findShiftsAndMask()
+    // Each class has a set (attempts()) of shift-mask possibilities, ordered, and we go through them
+    // sequentially.
+    PlacementAttempt attemptForShift(int shift, int neededBits) const;
+    std::vector<PlacementAttempt> attempts() const;
+
+    int shift;
+
+    inline int modulo() const {
+        return 1 << neededBits;
+    }
+
+    inline int mask() const {
+        return modulo() - 1;
+    }
+
+    // Attempt to place the class with the shift/mask in the attempt argument.
+    typename PlacementAttempt::Result applyAttempt(const PlacementAttempt& attempt, std::minstd_rand & randomNumberGenerator);
+
+    // Reassign the addresses as they were before we produced resultToBacktrackFrom
+    void backtrack(typename PlacementAttempt::Result& resultToBacktrackFrom);
+
+    // Did we have to grow the size of the hash table to one more bit when attempting to place it?
+    bool hadToIncreaseSize() const;
+
+    // Not const because this stomps over the slots array for checking
+    bool checkConsistency();
+
+    // Size in bytes needed for this hash table in the shared cache.
+    size_t sizeInSharedCache() const;
+
+    // Used to store the location of a flattening root's superclass, so that
+    // we can compute its final vmAddr once we are in the ObjC optimizer.
+    struct ClassLocator {
+        const char *installName;
+        unsigned segmentIndex;
+        unsigned segmentOffset;
+        bool operator==(const ClassLocator & other);
+    };
+
+    std::string_view flatteningRootName;
+    std::set<std::string_view> flattenedSuperclasses;
+    std::optional<ClassLocator> flatteningRootSuperclass;
+
+    // For debug purposes
+    bool operator==(const ClassData &other);
+};
+
+/// A unique selector. Has a name, a list of classes it's used in, and an address (which is actually an offset from
+/// the beginning of the selectors section). Due to how the placement algorithm work, it also has a current
+/// partial address and the corresponding bitmask of fixed bits. The algorithm adds bits to the partial address
+/// as it makes progress and updates the mask accordingly
+class Selector {
+public:
+    // For debug purposes
+    const char* name;
+    
+    /// Classes the selector is used in
+    std::vector<IMPCaches::ClassData*> classes;
+    
+    /// Which bits of address are already set
+    int fixedBitsMask;
+    
+    /// Current 128-byte bucket index for this selector. Only the bits in fixedBitsMask are actually frozen.
+    int inProgressBucketIndex;
+    
+    /// Full offset of the selector, including its low 7 bits (so, not the bucket's index ; inProgressBucketIndex (assuming all bits are setr by now) << 7 + some low bits)
+    int offset; // including low bits
+
+    /// Number of bits that you would need to freeze if you were to use this selector with this shift and mask.
+    int numberOfBitsToSet(int shift, int mask) const {
+        int fixedBits = (fixedBitsMask >> shift) & mask;
+        return __builtin_popcount(mask) - __builtin_popcount(fixedBits);
+    }
+
+    int numberOfSetBits() const {
+        return __builtin_popcount(fixedBitsMask);
+    }
+
+    unsigned int size() const {
+        return (unsigned int)strlen(name) + 1;
+    }
+
+    // For debug purposes
+    bool operator==(const Selector &other) const {
+        return (strcmp(name, other.name) == 0)
+            && (inProgressBucketIndex == other.inProgressBucketIndex)
+            && (fixedBitsMask == other.fixedBitsMask);
+    }
+
+    bool operator!=(const Selector &other) const {
+        return !(*this == other);
+    }
+
+};
+
+class AddressSpace;
+std::ostream& operator<<(std::ostream& o, const AddressSpace& c);
+
+struct Hole {
+    // [startAddress, endAddress[
+    int startAddress;
+    int endAddress;
+    int size() const {
+        return endAddress - startAddress;
+    }
+
+    // All our intervals are non-overlapping
+    bool operator<(const Hole& other) const {
+        auto a = std::make_tuple(size(), startAddress);
+        auto b = std::make_tuple(other.size(), other.startAddress);
+        return a < b;
+    }
+};
+
+/// Represents the holes left by the selector placement algorithm, to be filled later with other selectors we did not target.
+class HoleMap {
+public:
+    
+    /// Returns the position at which we should place a string of size `size`.
+    int addStringOfSize(unsigned size);
+    
+    /// Total size of all the holes
+    unsigned long totalHoleSize() const;
+    
+    // Empty the hole map.
+    void clear();
+
+    HoleMap();
+
+private:
+    friend class AddressSpace;
+    friend std::ostream& operator<<  (std::ostream& o, const HoleMap& m);
+
+    int endAddress = 0;
+    std::set<IMPCaches::Hole> holes;
+};
+
+// A selector that is known to be at offset 0, to let objc easily compute
+// the offset of a selector given the SEL.
+constexpr std::string_view magicSelector = "\xf0\x9f\xa4\xaf";
+
+/// This is used to place the selectors in 128-byte buckets.
+/// The "indices" below are the indices of the 128-byte bucket. To get an actual selector offset from this,
+/// we shift the index to the left by 7 bits, and assign low bits depending on the length of each selector (this happens
+/// in @see computeLowBits()).
+/// The goal of this class is to validate that selectors can actually be placed in the buckets without overflowing
+/// the 128-byte total length limit (based on the length of each individual selector)
+class AddressSpace {
+public:
+    int sizeAtIndex(int idx) const;
+    int sizeAvailableAfterIndex(int idx) const;
+    bool canPlaceMethodAtIndex(const Selector* method, int idx) const;
+    void placeMethodAtIndex(Selector* method, int idx);
+    
+    // If we decided to drop any classes, remove the selectors that were only present in them
+    void removeUninterestingSelectors();
+    
+    // Once all selectors are placed in their 128-byte buckets,
+    // actually assign the low 7 bits for each, and make a map of the
+    // holes so that we can fill them with other selectors later.
+    void computeLowBits(HoleMap& selectorsHoleMap) const;
+
+    std::string description() const;
+
+    static const int maximumIndex = (1 << 17) - 1;
+    static constexpr int bagSizeShift = 7;
+
+    friend std::ostream& operator<<  (std::ostream& o, const AddressSpace& c);
+private:
+    inline int bagSizeAtIndex(int idx) const {
+        static constexpr int bagSize = 1 << bagSizeShift;
+        static constexpr int bag0Size = bagSize - (magicSelector.length() + 1);
+        return idx ? bagSize : bag0Size;
+    }
+    bool canPlaceWithoutFillingOverflowCellAtIndex(int idx) const;
+    std::unordered_map<int, std::vector<Selector*>> methodsByIndex;
+    std::unordered_map<int, int> sizes;
+};
+
+/// Represents a constraint on some of the bits of an address
+/// It stores a set of allowed values for a given range of bits (shift and mask)
+class Constraint {
+public:
+    int mask;
+    int shift;
+    std::unordered_set<int> allowedValues;
+
+    Constraint intersecting(const Constraint& other) const;
+    friend std::ostream& operator << (std::ostream& o, const Constraint& c);
+
+    bool operator==(const Constraint& other) const {
+        return mask == other.mask &&
+                shift == other.shift &&
+                allowedValues == other.allowedValues;
+    }
+
+    struct Hasher {
+        size_t operator()(const IMPCaches::Constraint& c) const {
+            return c.shift << 24 | c.mask << 16 | c.allowedValues.size() << 8 | *c.allowedValues.begin();
+        }
+    };
+};
+
+/// Merges several Constraints together to generate a simplified constraint equivalent to the original set of constraints
+class ConstraintSet {
+    std::unordered_set<Constraint, Constraint::Hasher> constraints;
+
+public:
+    std::optional<Constraint> mergedConstraint;
+
+    bool add(const Constraint& c);
+    void clear();
+};
+
+class SelectorMap {
+public:
+    using UnderlyingMap = std::map<std::string_view, std::unique_ptr<IMPCaches::Selector>>;
+    UnderlyingMap map;
+    SelectorMap();
+};
+
+// Implemented in OptimizerObjC
+size_t sizeForImpCacheWithCount(int entries);
+
+struct ClassKey {
+    std::string_view name;
+    bool metaclass;
+
+    size_t hash() const {
+        std::size_t seed = 0;
+        seed ^= std::hash<std::string_view>()(name) + 0x9e3779b9 + (seed<<6) + (seed>>2);
+        seed ^= std::hash<bool>()(metaclass) + 0x9e3779b9 + (seed<<6) + (seed>>2);
+        return seed;
+    }
+
+    bool operator==(const ClassKey &other) const {
+        return (name == other.name) && (metaclass == other.metaclass);
+    }
+};
+
+struct ClassKeyHasher {
+    size_t operator()(const ClassKey& k) const {
+        return k.hash();
+    }
+};
+
+}
+
+
+#endif /* IMPCaches_hpp */
diff --git a/dyld3/shared-cache/IMPCachesBuilder.hpp b/dyld3/shared-cache/IMPCachesBuilder.hpp
new file mode 100644 (file)
index 0000000..2301318
--- /dev/null
@@ -0,0 +1,186 @@
+//
+//  IMPCachesBuilder.hpp
+//  dyld
+//
+//  Created by Thomas Deniau on 20/01/2020.
+//
+
+#pragma once
+
+#include "CacheBuilder.h"
+#include "IMPCaches.hpp"
+#include <random>
+
+namespace IMPCaches {
+
+class IMPCachesBuilder {
+public:
+    SelectorMap selectors;
+
+    /// Parses source dylibs to figure out which methods end up in which caches
+    bool parseDylibs(Diagnostics& diag);
+
+    /// Builds a map of the class hierarchy across all dylibs. This is especially used to resolve
+    /// cross-dylib dependencies for superclasses and categories.
+    void buildClassesMap(Diagnostics& diag);
+
+    /// The entry point of the algorithm
+    void buildPerfectHashes(HoleMap& holeMap, Diagnostics& diag);
+
+    /// Regenerate the hole map if we needed to evict dylibs.
+    void computeLowBits(HoleMap& holeMap);
+    
+    // Total size the hash tables will need.
+    size_t totalIMPCachesSize() const;
+
+    void clear() {
+        // FIXME: Implement this
+    }
+
+    /** Classes for which we want to generate IMP caches according to the input JSON config laid down by OrderFiles
+     * The value is the index of the class in the json file (they are ordered by decreasing order of importance)
+     * This isn't just a vector because we also need to test for membership quickly */
+    std::unordered_map<std::string_view, int> neededClasses;
+    std::unordered_map<std::string_view, int> neededMetaclasses;
+
+    // Classes for which we don't generate IMP caches, but which we need to track
+    // to attach categories to them and find the right implementation for
+    // inlined selectors
+    std::unordered_set<std::string_view> trackedClasses;
+    std::unordered_set<std::string_view> trackedMetaclasses;
+
+    // List of classes with the same name that appear in different images.
+    // We should not try to play with fire and try to support duplicated
+    // classes in IMP caches.
+
+    using ClassSet = std::unordered_set<ClassKey, ClassKeyHasher>;
+    ClassSet duplicateClasses;
+
+    /// Selectors which we want to inline into child classes' caches.
+    std::unordered_set<std::string_view> selectorsToInline;
+    std::vector<const Selector*> inlinedSelectors;
+
+    // Class hierarchies to flatten:
+    // In every class, include every selector including
+    // the ones from superclasses up to the flattening root.
+    // This lets us enable constant caches for some of the classes which are not leaves.
+    // We avoid the pyramid of doom by making sure selectors from superclasses are
+    // included in child caches, up until some flattening root, and msgSend will
+    // fallback to the superclass of the flattening root if it can't find the selector
+    // it expects.
+    std::unordered_set<std::string_view> metaclassHierarchiesToFlatten;
+    std::unordered_set<std::string_view> classHierarchiesToFlatten;
+
+    /// All the dylibs the algorith, works on.
+    std::vector<CacheBuilder::DylibInfo> & dylibs;
+
+    IMPCachesBuilder(std::vector<CacheBuilder::DylibInfo>& allDylibs, const dyld3::json::Node& optimizerConfiguration, Diagnostics& diag, TimeRecorder& timeRecorder, const dyld3::closure::FileSystem& fileSystem);
+
+    struct ObjCClass {
+
+        const dyld3::MachOAnalyzer* superclassMA = nullptr;
+        const uint8_t*      metaClass           = nullptr;
+        const uint8_t*      superclass          = nullptr;
+        uint64_t            methodListVMaddr    = 0;
+        const char*         className           = nullptr;
+        bool                isRootClass         = false;
+        bool                isMetaClass         = false;
+
+        const IMPCaches::ClassData::ClassLocator superclassLocator() const {
+            uint64_t loadAddress = superclassMA->preferredLoadAddress();
+            __block std::optional<unsigned> segmentIndex;
+            __block std::optional<unsigned> segmentOffset;
+            uint64_t superclassVMAddr = (superclass - (const uint8_t*)superclassMA) + loadAddress;
+            superclassMA->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
+                if (info.vmAddr <= superclassVMAddr && (info.vmAddr + info.vmSize) > superclassVMAddr) {
+                    segmentIndex = info.segIndex;
+                    segmentOffset = (unsigned)(superclassVMAddr - info.vmAddr);
+                    stop = true;
+                    return;
+                }
+            });
+            assert(segmentIndex.has_value() && segmentOffset.has_value());
+            return IMPCaches::ClassData::ClassLocator {
+                .installName = superclassMA->installName(),
+                .segmentIndex = *segmentIndex,
+                .segmentOffset = *segmentOffset,
+            };
+        }
+    };
+    struct ObjCCategory {
+        const dyld3::MachOAnalyzer* classMA = nullptr;
+        const uint8_t* cls = nullptr;
+    };
+
+private:
+
+    /// Is this a class for which we want to generate an IMP cache?
+    bool isClassInteresting(const ObjCClass& theClass) const;
+
+    /// Is this a class for which we want to generate an IMP cache, or on which we want to track method attachment by categories?
+    bool isClassInterestingOrTracked(const ObjCClass& theClass) const;
+
+    /// Adds a method to a given class's cache.
+    void addMethod(IMPCaches::ClassData* classDataPtr, const char* methodName, const char* installName, const char* className, const char* catName, bool inlined, bool fromFlattening);
+
+    /// Inline a method from a parent's cache to a child's cache.
+    void inlineMethodIfNeeded(IMPCaches::ClassData* classToInlineIn, const char* classToInlineFrom, const char* catToInlineFrom, const char* installNameToInlineFrom, const char* name, std::set<Selector*> & seenSelectors, bool isFlattening);
+
+    /// Map from location (address in the mmapped source dylib) to class info built by buildClassesMap()
+    std::unordered_map<const uint8_t*, ObjCClass> objcClasses;
+
+    /// Map from location (address in the mmapped source dylib) to category info built by buildClassesMap()
+    std::unordered_map<const uint8_t*, ObjCCategory> objcCategories;
+
+    /// Address space where the selectors are going to live. Used to determine at which address we'll actually layout each selector.
+    IMPCaches::AddressSpace addressSpace;
+
+    /// Find a shift and a mask for each class. Returns the number of classes that we could not place.
+    int findShiftsAndMasks();
+    
+    /// Shuffles selectors around to satisfy size constraints.  Returns the number of classes that we could not place.
+    int solveGivenShiftsAndMasks();
+
+    /// Determine classes we need to track category attachment on (for later inlining)
+    void buildTrackedClasses(CacheBuilder::DylibInfo& dylib, const dyld3::MachOAnalyzer* ma);
+
+    /// Parses the method lists in the source dylibs to determine what will end up in which IMP cache.
+    void populateMethodLists(CacheBuilder::DylibInfo& dylib, const dyld3::MachOAnalyzer* ma, int* duplicateClassCount);
+
+    /// Go through categories and add the methods from the category to the corresponding class's IMP cache
+    void attachCategories(CacheBuilder::DylibInfo& dylib, const dyld3::MachOAnalyzer* ma);
+
+    // Inline some selectors (driven by the OrderFiles) from parent caches into child caches.
+    void inlineSelectors(CacheBuilder::DylibInfo& dylib, std::unordered_map<std::string_view, CacheBuilder::DylibInfo*> & dylibsByInstallName, const dyld3::MachOAnalyzer* ma);
+
+    void fillAllClasses(std::vector<IMPCaches::ClassData*> & allClasses);
+    void fillAllMethods(std::vector<IMPCaches::Selector*> & allMethods);
+    void removeUninterestingClasses();
+
+    struct TargetClassFindingResult {
+        bool success;
+        const dyld3::MachOLoaded* foundInDylib;
+        const uint8_t* location;
+    };
+
+    struct BindTarget {
+        std::string                 symbolName      = "";
+        const dyld3::MachOAnalyzer* targetDylib     = nullptr;
+        bool                        isWeakImport    = false;
+    };
+
+    struct DylibAndDeps {
+        const dyld3::MachOAnalyzer*         ma = nullptr;
+        __block std::vector<std::string>    dependentLibraries;
+    };
+
+    TargetClassFindingResult findTargetClass(const dyld3::MachOAnalyzer* ma, uint64_t targetClassVMAddr, uint64_t targetClassPointerVMAddr, const char* logContext, const std::unordered_map<uint64_t, uint64_t> & bindLocations, std::vector<BindTarget> & bindTargets, std::unordered_map<std::string, DylibAndDeps> &dylibMap);
+
+    const std::string * nameAndIsMetaclassPairFromNode(const dyld3::json::Node & node, bool* metaclass);
+
+    Diagnostics& _diagnostics;
+    TimeRecorder& _timeRecorder;
+    const dyld3::closure::FileSystem& _fileSystem;
+};
+
+}
index 0657d6fb0340fd00151a7c911b3d6ffe6a779c0a..8f34a6d191421fb92ecba8dc11338365955b6984 100644 (file)
 #define DYLD_CACHE_ADJ_V2_IMAGE_OFF_32                 0x0C
 #define DYLD_CACHE_ADJ_V2_THREADED_POINTER_64   0x0D
 
+#ifndef LC_FILESET_ENTRY
+#define LC_FILESET_ENTRY      (0x35 | LC_REQ_DYLD) /* used with fileset_entry_command */
+struct fileset_entry_command {
+    uint32_t        cmd;        /* LC_FILESET_ENTRY */
+    uint32_t        cmdsize;    /* includes id string */
+    uint64_t        vmaddr;     /* memory address of the dylib */
+    uint64_t        fileoff;    /* file offset of the dylib */
+    union lc_str    entry_id;   /* contained entry id */
+    uint32_t        reserved;   /* entry_id is 32-bits long, so this is the reserved padding */
+};
+#endif
+
+
 #include "FileAbstraction.hpp"
 //#include "Architectures.hpp"
 
@@ -853,6 +866,77 @@ private:
        dyld_info_command       fields;
 };
 
+
+//
+// mach-o build version load command
+//
+template <typename P>
+class macho_build_version_command {
+public:
+       uint32_t                cmd() const                             INLINE { return E::get32(fields.cmd); }
+       void                    set_cmd(uint32_t value)                 INLINE { E::set32(fields.cmd, value); }
+
+       uint32_t                cmdsize() const                                                 INLINE { return E::get32(fields.cmdsize); }
+       void                    set_cmdsize(uint32_t value)                             INLINE { E::set32(fields.cmdsize, value); }
+
+    uint32_t        platform() const                        INLINE { return E::get32(fields.platform); }
+    void            set_platform(uint32_t value)            INLINE { E::set32(fields.platform, value); }
+
+    uint32_t        minos() const                           INLINE { return E::get32(fields.minos); }
+    void            set_minos(uint32_t value)               INLINE { E::set32(fields.minos, value); }
+
+    uint32_t        sdk() const                             INLINE { return E::get32(fields.sdk); }
+    void            set_sdk(uint32_t value)                 INLINE { E::set32(fields.sdk, value); }
+
+    uint32_t        ntools() const                          INLINE { return E::get32(fields.ntools); }
+    void            set_ntools(uint32_t value)              INLINE { E::set32(fields.ntools, value); }
+
+       typedef typename P::E           E;
+private:
+       build_version_command   fields;
+};
+
+//
+// mach-o routines load command
+//
+template <typename P> struct macho_fileset_entry_command_content {};
+template <> struct macho_fileset_entry_command_content<Pointer32<BigEndian> >      { fileset_entry_command             fields; enum { CMD = LC_FILESET_ENTRY   }; };
+template <> struct macho_fileset_entry_command_content<Pointer64<BigEndian> >      { fileset_entry_command         fields; enum { CMD = LC_FILESET_ENTRY       }; };
+template <> struct macho_fileset_entry_command_content<Pointer32<LittleEndian> >   { fileset_entry_command             fields; enum { CMD = LC_FILESET_ENTRY   }; };
+template <> struct macho_fileset_entry_command_content<Pointer64<LittleEndian> >   { fileset_entry_command         fields; enum { CMD = LC_FILESET_ENTRY       }; };
+
+template <typename P>
+class macho_fileset_entry_command {
+public:
+       uint32_t                cmd() const                                                     INLINE { return E::get32(cache_entry_id.fields.cmd); }
+       void                    set_cmd(uint32_t value)                         INLINE { E::set32(cache_entry_id.fields.cmd, value); }
+
+       uint32_t                cmdsize() const                                         INLINE { return E::get32(cache_entry_id.fields.cmdsize); }
+       void                    set_cmdsize(uint32_t value)                     INLINE { E::set32(cache_entry_id.fields.cmdsize, value); }
+
+       uint64_t                vmaddr() const                                  INLINE { return P::getP(cache_entry_id.fields.vmaddr); }
+       void                    set_vmaddr(uint64_t value)              INLINE { P::setP(cache_entry_id.fields.vmaddr, value); }
+
+       uint64_t                fileoff() const                                 INLINE { return P::getP(cache_entry_id.fields.fileoff); }
+       void                    set_fileoff(uint64_t value)             INLINE { P::setP(cache_entry_id.fields.fileoff, value); }
+
+    uint32_t        entry_id_offset() const             INLINE { return E::get32(cache_entry_id.fields.entry_id.offset); }
+    void            set_entry_id_offset(uint32_t value) INLINE { E::set32(cache_entry_id.fields.entry_id.offset, value);  }
+
+    const char*     entry_id() const                    INLINE { return (const char*)&cache_entry_id.fields + entry_id_offset(); }
+    void            set_entry_id(const char* value)     INLINE {
+        set_entry_id_offset(sizeof(cache_entry_id.fields));
+        strcpy(((char*)&cache_entry_id.fields) + sizeof(cache_entry_id.fields), value);
+    }
+
+       typedef typename P::E           E;
+       enum {
+               CMD = macho_fileset_entry_command_content<P>::CMD
+       };
+private:
+       macho_fileset_entry_command_content<P>  cache_entry_id;
+};
+
 #ifndef NO_ULEB 
 inline uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) {
        uint64_t result = 0;
@@ -888,7 +972,7 @@ inline int64_t read_sleb128(const uint8_t*& p, const uint8_t* end)
                bit += 7;
        } while (byte & 0x80);
        // sign extend negative numbers
-       if ( (byte & 0x40) != 0 )
+    if ( ((byte & 0x40) != 0) && (bit < 64) )
                result |= (~0ULL) << bit;
        return result;
 }
index ef2cd13b56321d6ab87b177aaea3bd080e322ba8..a0e581ba2254c92a4ddc5c1eeae2ad0f1d096238 100644 (file)
@@ -44,7 +44,7 @@ struct entsize_iterator {
     entsize_iterator() { } 
     
     entsize_iterator(const Tlist& list, uint32_t start = 0)
-        : entsize(list.getEntsize()), index(start), current(&list.get(start)) 
+        : entsize(list.getEntsize()), index(start), current((T*)list.get(start))
     { }
     
     const entsize_iterator<P,T,Tlist>& operator += (ptrdiff_t count) {
@@ -152,101 +152,403 @@ public:
     }
 };
 
-
 template <typename P>
-class objc_method_list_t;  // forward reference
-
+class objc_method_list_t {
 
-template <typename P>
-class objc_method_t {
     typedef typename P::uint_t pint_t;
-    pint_t name;   // SEL
-    pint_t types;  // const char *
-    pint_t imp;    // IMP
-    friend class objc_method_list_t<P>;
-public:
-    pint_t getName() const { return (pint_t)P::getP(name); }
-    void setName(pint_t newName) { P::setP(name, newName); }
 
-    struct SortBySELAddress : 
-        public std::binary_function<const objc_method_t<P>&, 
-                                    const objc_method_t<P>&, bool>
+    template <typename PtrTy>
+    class objc_method_small_t {
+        typedef typename PtrTy::uint_t pint_t;
+        int32_t name;   // SEL
+        int32_t types;  // const char *
+        int32_t imp;    // IMP
+        friend class objc_method_list_t<PtrTy>;
+
+        objc_method_small_t() = delete;
+        ~objc_method_small_t() = delete;
+        objc_method_small_t(const objc_method_small_t& other) = delete;
+        objc_method_small_t(objc_method_small_t&& other) = delete;
+        objc_method_small_t& operator=(const objc_method_small_t& other) = delete;
+        objc_method_small_t& operator=(objc_method_small_t&& other) = delete;
+
+    public:
+
+        pint_t getName(ContentAccessor* cache, bool isOffsetToSel) const {
+            // We want to return the VM address of the "const char*" our selector
+            // reference is pointing at.
+            pint_t* nameRef = (pint_t*)((uint8_t*)&name + name);
+            if ( isOffsetToSel ) {
+                // Offset is directly to the SEL, not a selRef
+                return (pint_t)cache->vmAddrForContent(nameRef);
+            } else {
+                return (pint_t)PtrTy::getP(*nameRef);
+            }
+        }
+        // We want to update the selRef we are pointing at with the new content
+        // We may share the same selRef with other method lists or @SEL expressions, but as
+        // all of them want the same uniqued selector anyway, its safe to overwrite it here for
+        // everyone.
+        void setName(ContentAccessor* cache, pint_t newNameVMAddr, bool isOffsetToSel) {
+            if ( isOffsetToSel ) {
+                // Offset is directly to the SEL, not a selRef
+                void* namePtr = cache->contentForVMAddr(newNameVMAddr);
+                this->name = (int32_t)(intptr_t)((uint8_t*)namePtr - (uint8_t*)&this->name);
+            } else {
+                pint_t* selRef = (pint_t*)((uint8_t*)&name + name);
+                PtrTy::setP(*selRef, newNameVMAddr);
+            }
+        }
+        // Returns the vmAddr of the types
+        pint_t getTypes(ContentAccessor* cache) const {
+            pint_t* typesRef = (pint_t*)((uint8_t*)&types + types);
+            return (pint_t)cache->vmAddrForContent(typesRef);
+        }
+        void setTypes(ContentAccessor* cache, pint_t newTypesVMAddr) {
+            void* typesPtr = cache->contentForVMAddr(newTypesVMAddr);
+            this->types = (int32_t)(intptr_t)((uint8_t*)typesPtr - (uint8_t*)&this->types);
+        }
+        // Returns the vmAddr of the IMP
+        pint_t getIMP(ContentAccessor* cache) const {
+            pint_t* impRef = (pint_t*)((uint8_t*)&imp + imp);
+            return (pint_t)cache->vmAddrForContent(impRef);
+        }
+        void setIMP(ContentAccessor* cache, pint_t newIMPVMAddr) {
+            void* impPtr = cache->contentForVMAddr(newIMPVMAddr);
+            this->imp = (int32_t)(intptr_t)((uint8_t*)impPtr - (uint8_t*)&this->imp);
+        }
+
+        // Swap the contents of this value and other
+        // This has to recompute all of the relative offsets
+        void swap(objc_method_small_t<PtrTy>* other) {
+            // Get our targets
+            uint8_t* ourNameTarget  = (uint8_t*)&this->name + this->name;
+            uint8_t* ourTypesTarget = (uint8_t*)&this->types + this->types;
+            uint8_t* ourIMPTarget   = (uint8_t*)&this->imp + this->imp;
+            // Get their targets
+            uint8_t* theirNameTarget  = (uint8_t*)&other->name + other->name;
+            uint8_t* theirTypesTarget = (uint8_t*)&other->types + other->types;
+            uint8_t* theirIMPTarget   = (uint8_t*)&other->imp + other->imp;
+            // Set our targets
+            this->name = (int32_t)(intptr_t)(theirNameTarget - (uint8_t*)&this->name);
+            this->types = (int32_t)(intptr_t)(theirTypesTarget - (uint8_t*)&this->types);
+            this->imp = (int32_t)(intptr_t)(theirIMPTarget - (uint8_t*)&this->imp);
+            // Set their targets
+            other->name = (int32_t)(intptr_t)(ourNameTarget - (uint8_t*)&other->name);
+            other->types = (int32_t)(intptr_t)(ourTypesTarget - (uint8_t*)&other->types);
+            other->imp = (int32_t)(intptr_t)(ourIMPTarget - (uint8_t*)&other->imp);
+        }
+
+        struct SortBySELAddress :
+            public std::binary_function<const objc_method_small_t<PtrTy>&,
+                                        const objc_method_small_t<PtrTy>&, bool>
+        {
+            SortBySELAddress(ContentAccessor* cache, bool isOffsetToSel)
+                : cache(cache), isOffsetToSel(isOffsetToSel) { }
+
+            bool operator() (const objc_method_small_t<PtrTy>& lhs,
+                             const objc_method_small_t<PtrTy>& rhs)
+            {
+                return lhs.getName(cache, isOffsetToSel) < rhs.getName(cache, isOffsetToSel);
+            }
+
+            ContentAccessor* cache  = nullptr;
+            bool isOffsetToSel      = false;
+        };
+    };
+
+    template <typename PtrTy>
+    class objc_method_large_t {
+        typedef typename PtrTy::uint_t pint_t;
+        pint_t name;   // SEL
+        pint_t types;  // const char *
+        pint_t imp;    // IMP
+        friend class objc_method_list_t<PtrTy>;
+    public:
+        pint_t getName() const {
+            return (pint_t)PtrTy::getP(name);
+        }
+        void setName(pint_t newName) {
+            PtrTy::setP(name, newName);
+        }
+        pint_t getTypes() const {
+            return (pint_t)PtrTy::getP(types);
+        }
+        void setTypes(pint_t newTypes) {
+            PtrTy::setP(types, newTypes);
+        }
+        pint_t getIMP() const {
+            return (pint_t)PtrTy::getP(imp);
+        }
+        void setIMP(pint_t newIMP) {
+            PtrTy::setP(imp, newIMP);
+        }
+
+        struct SortBySELAddress :
+            public std::binary_function<const objc_method_large_t<PtrTy>&,
+                                        const objc_method_large_t<PtrTy>&, bool>
+        {
+            bool operator() (const objc_method_large_t<PtrTy>& lhs,
+                             const objc_method_large_t<PtrTy>& rhs)
+            {
+                return lhs.getName() < rhs.getName();
+            }
+        };
+    };
+
+    // Temporary struct to use when sorting small methods as their int32_t offsets can't reach
+    // from the stack where temporary values are placed, in to the shared cache buffer where the data lives
+    struct TempMethod {
+        // Relative methods in the shared cache always use direct offsets to the SEL
+        // at the point where this is running.  That means we don't need to indirect through
+        // a SEL reference.
+        pint_t selVMAddr;
+        pint_t typesVMAddr;
+        pint_t impVMAddr;
+    };
+
+    template <typename PtrTy>
+    struct SortBySELAddress :
+        public std::binary_function<const TempMethod&,
+                                    const TempMethod&, bool>
     {
-        bool operator() (const objc_method_t<P>& lhs, 
-                         const objc_method_t<P>& rhs)
+        SortBySELAddress(ContentAccessor* cache) : cache(cache) { }
+
+        bool operator() (const TempMethod& lhs,
+                         const TempMethod& rhs)
         {
-            return lhs.getName() < rhs.getName();
+            return lhs.selVMAddr < rhs.selVMAddr;
         }
+
+        ContentAccessor* cache = nullptr;
     };
-};
 
-template <typename P>
-class objc_method_list_t {
     uint32_t entsize;
     uint32_t count;
-    objc_method_t<P> first;
+    union {
+        objc_method_small_t<P> small;
+        objc_method_large_t<P> large;
+    } first;
 
     void* operator new (size_t, void* buf) { return buf; }
 
-public:
+    enum : uint32_t {
+        // If this is set, the relative method lists name_offset field is an
+        // offset directly to the SEL, not a SEL ref.
+        relativeMethodSelectorsAreDirectFlag    = 0x40000000,
 
-    typedef entsize_iterator<P, objc_method_t<P>, objc_method_list_t<P> > method_iterator;
+        // If this is set, then method lists are the new relative format, not
+        // the old pointer based format
+        relativeMethodFlag                      = 0x80000000,
 
-    uint32_t getCount() const { return P::E::get32(count); }
+        // The upper 16-bits are all defined to be flags
+        methodListFlagsMask                     = 0xFFFF0000
+    };
+
+    uint32_t getFlags() const {
+        return (P::E::get32(entsize) & methodListFlagsMask);
+    }
+
+    typedef entsize_iterator<P, objc_method_small_t<P>, objc_method_list_t<P> > small_method_iterator;
+    typedef entsize_iterator<P, objc_method_large_t<P>, objc_method_list_t<P> > large_method_iterator;
 
-    uint32_t getEntsize() const {return P::E::get32(entsize)&~(uint32_t)3;}
+    small_method_iterator beginSmall() {
+        assert(usesRelativeMethods());
+        return small_method_iterator(*this, 0);
+    }
+    small_method_iterator endSmall() {
+        assert(usesRelativeMethods());
+        return small_method_iterator(*this, getCount());
+    }
 
-    objc_method_t<P>& get(uint32_t i) const { return *(objc_method_t<P> *)((uint8_t *)&first + i * getEntsize()); }
+    large_method_iterator beginLarge() {
+        assert(!usesRelativeMethods());
+        return large_method_iterator(*this, 0);
+    }
+    large_method_iterator endLarge() {
+        assert(!usesRelativeMethods());
+        return large_method_iterator(*this, getCount());
+    }
+
+public:
+
+    uint32_t getCount() const { return P::E::get32(count); }
+
+    uint32_t getEntsize() const {
+        return P::E::get32(entsize) & ~(uint32_t)3 & ~methodListFlagsMask;
+    }
 
     uint32_t byteSize() const { 
         return byteSizeForCount(getCount(), getEntsize()); 
     }
 
-    static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_method_t<P>)) { 
-        return sizeof(objc_method_list_t<P>) - sizeof(objc_method_t<P>) + c*e;
+    static uint32_t byteSizeForCount(uint32_t c, uint32_t e) {
+        return sizeof(entsize) + sizeof(count) + c*e;
     }
 
-    method_iterator begin() { return method_iterator(*this, 0); }
-    method_iterator end() { return method_iterator(*this, getCount()); }
-    const method_iterator begin() const { return method_iterator(*this, 0); }
-    const method_iterator end() const { return method_iterator(*this, getCount()); }
+    bool usesRelativeMethods() const {
+        return (P::E::get32(entsize) & relativeMethodFlag) != 0;
+    }
 
-    void setFixedUp() { P::E::set32(entsize, getEntsize() | 3); }
+    void setFixedUp() {
+        P::E::set32(entsize, getEntsize() | 3 | getFlags());
+    }
 
-    void getPointers(std::set<void*>& pointersToRemove) {
-        for(method_iterator it = begin(); it != end(); ++it) {
-            objc_method_t<P>& entry = *it;
-            pointersToRemove.insert(&(entry.name));
-            pointersToRemove.insert(&(entry.types));
-            pointersToRemove.insert(&(entry.imp));
+    void setMethodListSelectorsAreDirect() {
+        P::E::set32(entsize, getEntsize() | getFlags() | relativeMethodSelectorsAreDirectFlag);
+    }
+
+    void sortMethods(ContentAccessor* cache, pint_t *typelist, bool isOffsetToSel) {
+        if ( usesRelativeMethods() ) {
+            // At this point we assume we are using offsets directly to selectors.  This
+            // is so that the TempMethod struct can also use direct offsets and not track the
+            // SEL reference VMAddrs
+            assert(isOffsetToSel);
+
+            if ( typelist == nullptr ) {
+                // This is the case when we are sorting the methods on a class.
+                // Only protocols have a type list which causes the other sort to be used
+                // We can't sort the small methods in place as their 32-bit offsets can't reach
+                // the VM space where the shared cache is being created.  Instead create a list
+                // of large methods and sort those.
+
+                std::vector<TempMethod> largeMethods;
+                for (unsigned i = 0 ; i != count; ++i) {
+                    const objc_method_small_t<P>* smallMethod = (const objc_method_small_t<P>*)get(i);
+                    TempMethod largeMethod;
+                    largeMethod.selVMAddr = smallMethod->getName(cache, isOffsetToSel);
+                    largeMethod.typesVMAddr = smallMethod->getTypes(cache);
+                    largeMethod.impVMAddr = smallMethod->getIMP(cache);
+                    largeMethods.push_back(largeMethod);
+                }
+
+                SortBySELAddress<P> sorter(cache);
+                std::stable_sort(largeMethods.begin(), largeMethods.end(), sorter);
+
+                for (unsigned i = 0 ; i != count; ++i) {
+                    const TempMethod& largeMethod = largeMethods[i];
+                    objc_method_small_t<P>* smallMethod = (objc_method_small_t<P>*)get(i);
+                    smallMethod->setName(cache, largeMethod.selVMAddr, isOffsetToSel);
+                    smallMethod->setTypes(cache, largeMethod.typesVMAddr);
+                    smallMethod->setIMP(cache, largeMethod.impVMAddr);
+                }
+
+#if 0
+                // Check the method lists are sorted
+                {
+                    typename objc_method_small_t<P>::SortBySELAddress sorter(cache);
+                    for (uint32_t i = 0; i < getCount(); i++) {
+                        for (uint32_t j = i+1; j < getCount(); j++) {
+                            objc_method_small_t<P>* mi = (objc_method_small_t<P>*)get(i);
+                            objc_method_small_t<P>* mj = (objc_method_small_t<P>*)get(j);
+                            if ( mi->getName(cache) == mj->getName(cache) )
+                                continue;
+                            if (! sorter(*mi, *mj)) {
+                                assert(false);
+                            }
+                        }
+                    }
+                }
+#endif
+            }
+            else {
+                typename objc_method_small_t<P>::SortBySELAddress sorter(cache, isOffsetToSel);
+                // can't easily use std::stable_sort here
+                for (uint32_t i = 0; i < getCount(); i++) {
+                    for (uint32_t j = i+1; j < getCount(); j++) {
+                        objc_method_small_t<P>* mi = (objc_method_small_t<P>*)get(i);
+                        objc_method_small_t<P>* mj = (objc_method_small_t<P>*)get(j);
+                        if (! sorter(*mi, *mj)) {
+                            mi->swap(mj);
+                            if (typelist) std::swap(typelist[i], typelist[j]);
+                        }
+                    }
+                }
+            }
+        } else {
+            typename objc_method_large_t<P>::SortBySELAddress sorter;
+
+            if ( typelist == nullptr ) {
+                // This is the case when we are sorting the methods on a class.
+                // Only protocols have a type list which causes the other sort to be used
+                std::stable_sort(beginLarge(), endLarge(), sorter);
+            }
+            else {
+                // can't easily use std::stable_sort here
+                for (uint32_t i = 0; i < getCount(); i++) {
+                    for (uint32_t j = i+1; j < getCount(); j++) {
+                        objc_method_large_t<P>* mi = (objc_method_large_t<P>*)get(i);
+                        objc_method_large_t<P>* mj = (objc_method_large_t<P>*)get(j);
+                        if (! sorter(*mi, *mj)) {
+                            std::swap(*mi, *mj);
+                            if (typelist) std::swap(typelist[i], typelist[j]);
+                        }
+                    }
+                }
+            }
         }
+        // mark method list as sorted
+        this->setFixedUp();
     }
-    
-    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;
-            aslrTracker.add(&(entry.name));
-            aslrTracker.add(&(entry.types));
-            aslrTracker.add(&(entry.imp));
+
+    pint_t getName(ContentAccessor* cache, uint32_t i, bool isOffsetToSel) {
+        pint_t name = 0;
+        if ( usesRelativeMethods() ) {
+            small_method_iterator it = beginSmall() + i;
+            objc_method_small_t<P>& method = *it;
+            name = method.getName(cache, isOffsetToSel);
+        } else {
+            large_method_iterator it = beginLarge() + i;
+            objc_method_large_t<P>& method = *it;
+            name = method.getName();
         }
+        return name;
     }
 
-    static objc_method_list_t<P>* newMethodList(size_t newCount, uint32_t newEntsize) {
-        void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
-        return new (buf) objc_method_list_t<P>(newCount, newEntsize);
+    void setName(ContentAccessor* cache, uint32_t i, pint_t name, bool isOffsetToSel) {
+        if ( usesRelativeMethods() ) {
+            small_method_iterator it = beginSmall() + i;
+            objc_method_small_t<P>& method = *it;
+            method.setName(cache, name, isOffsetToSel);
+        } else {
+            large_method_iterator it = beginLarge() + i;
+            objc_method_large_t<P>& method = *it;
+            method.setName(name);
+        }
+    }
+
+    const char* getStringName(ContentAccessor* cache, uint32_t i, bool isOffsetToSel) {
+        return (const char*)cache->contentForVMAddr(getName(cache, i, isOffsetToSel));
+    }
+
+    pint_t getImp(uint32_t i, ContentAccessor* cache) {
+        pint_t name = 0;
+        if ( usesRelativeMethods() ) {
+            small_method_iterator it = beginSmall() + i;
+            objc_method_small_t<P>& method = *it;
+            name = method.getIMP(cache);
+        } else {
+            large_method_iterator it = beginLarge() + i;
+            objc_method_large_t<P>& method = *it;
+            name = method.getIMP();
+        }
+        return name;
+    }
+
+    void* get(uint32_t i) const {
+        if ( usesRelativeMethods() ) {
+            return (void*)(objc_method_small_t<P> *)((uint8_t *)&first + i * getEntsize());
+        } else {
+            return (void*)(objc_method_large_t<P> *)((uint8_t *)&first + i * getEntsize());
+        }
     }
 
     void operator delete(void * p) { 
         ::free(p); 
     }
 
-    objc_method_list_t(uint32_t newCount, 
-                       uint32_t newEntsize = sizeof(objc_method_t<P>))
-        : entsize(newEntsize), count(newCount) 
-    { }
-
 private:
+
     // use newMethodList instead
     void* operator new (size_t);
 };
@@ -293,7 +595,7 @@ public:
 
     uint32_t getEntsize() const { return P::E::get32(entsize); }
 
-    objc_ivar_t<P>& get(pint_t i) const { return *(objc_ivar_t<P> *)((uint8_t *)&first + i * P::E::get32(entsize)); }
+    void* get(pint_t i) const { return (void*)(objc_ivar_t<P> *)((uint8_t *)&first + i * P::E::get32(entsize)); }
 
     uint32_t byteSize() const { 
         return byteSizeForCount(getCount(), getEntsize()); 
@@ -358,7 +660,7 @@ public:
 
     uint32_t getEntsize() const { return P::E::get32(entsize); }
 
-    objc_property_t<P>& get(uint32_t i) const { return *(objc_property_t<P> *)((uint8_t *)&first + i * getEntsize()); }
+    void* get(uint32_t i) const { return (objc_property_t<P> *)((uint8_t *)&first + i * getEntsize()); }
 
     uint32_t byteSize() const { 
         return byteSizeForCount(getCount(), getEntsize()); 
@@ -432,6 +734,7 @@ class objc_protocol_t {
 public:
     pint_t getIsaVMAddr() const { return (pint_t)P::getP(isa); }
     void setIsaVMAddr(pint_t newIsa) { P::setP(isa, newIsa); }
+    void* getISALocation() const { return (void*)&isa; }
 
     const char *getName(ContentAccessor* cache) const { return (const char *)cache->contentForVMAddr(P::getP(name)); }
 
@@ -642,12 +945,15 @@ public:
     objc_class_t<P> *getIsa(ContentAccessor* cache) const { return (objc_class_t<P> *)cache->contentForVMAddr(P::getP(isa)); }
 
     objc_class_t<P> *getSuperclass(ContentAccessor* cache) const { return (objc_class_t<P> *)cache->contentForVMAddr(P::getP(superclass)); }
-    
     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 & ~0x3LL)); }
 
+    objc_class_t<P> *getVTable(ContentAccessor* cache) const { return (objc_class_t<P> *)cache->contentForVMAddr(P::getP(vtable)); }
+
+    pint_t* getVTableAddress() { return &vtable; }
+
     objc_method_list_t<P> *getMethodList(ContentAccessor* cache) const {
         objc_class_data_t<P>* d = getData(cache);
         return d->getMethodList(cache);
@@ -754,9 +1060,9 @@ public:
         objc_ivar_list_t<P> *ivars = data->getIvarList(cache);
         if (ivars) {
             for (pint_t i = 0; i < ivars->getCount(); i++) {
-                objc_ivar_t<P>& ivar = ivars->get(i);
+                objc_ivar_t<P>* ivar = (objc_ivar_t<P>*)ivars->get(i);
                 //fprintf(stderr, "visiting ivar: %s\n", ivar.getName(cache));
-                ivarVisitor.visitIvar(cache, header, cls, &ivar);
+                ivarVisitor.visitIvar(cache, header, cls, ivar);
             }
         } else {
             //fprintf(stderr, "no ivars\n");
@@ -769,14 +1075,20 @@ public:
     }
 };
 
+enum class ClassWalkerMode {
+    ClassesOnly,
+    ClassAndMetaclasses,
+};
+
 // Call visitor.visitClass() on every class.
 template <typename P, typename V>
 class ClassWalker {
     typedef typename P::uint_t pint_t;
     V& _visitor;
+    ClassWalkerMode _mode;
 public:
     
-    ClassWalker(V& visitor) : _visitor(visitor) { }
+    ClassWalker(V& visitor, ClassWalkerMode mode = ClassWalkerMode::ClassesOnly) : _visitor(visitor), _mode(mode) { }
     
     void walk(ContentAccessor* cache, const macho_header<P>* header)
     {   
@@ -784,8 +1096,14 @@ public:
         
         for (pint_t i = 0; i < classList.count(); i++) {
             objc_class_t<P>* cls = classList.get(i);
-            //fprintf(stderr, "visiting class: %s\n", cls->getName(cache));
-            if (cls) _visitor.visitClass(cache, header, cls);
+            if (cls) {
+                //fprintf(stderr, "visiting class: %s\n", cls->getName(cache));
+                _visitor.visitClass(cache, header, cls);
+                if (_mode == ClassWalkerMode::ClassAndMetaclasses) {
+                    //fprintf(stderr, "visiting metaclass: %s\n", cls->getIsa(cache)->getName(cache));
+                    _visitor.visitClass(cache, header, cls->getIsa(cache));
+                }
+            }
         }
     }
 };
@@ -901,10 +1219,10 @@ public:
             objc_class_t<P> *cls = classes.get(i);
             objc_method_list_t<P> *mlist;
             if ((mlist = cls->getMethodList(cache))) {
-                mVisitor.visitMethodList(mlist);
+                mVisitor.visitMethodList(cache, mlist);
             }
             if ((mlist = cls->getIsa(cache)->getMethodList(cache))) {
-                mVisitor.visitMethodList(mlist);
+                mVisitor.visitMethodList(cache, mlist);
             }
         }
         
@@ -915,10 +1233,10 @@ public:
             objc_category_t<P> *cat = cats.get(i);
             objc_method_list_t<P> *mlist;
             if ((mlist = cat->getInstanceMethods(cache))) {
-                mVisitor.visitMethodList(mlist);
+                mVisitor.visitMethodList(cache, mlist);
             }
             if ((mlist = cat->getClassMethods(cache))) {
-                mVisitor.visitMethodList(mlist);
+                mVisitor.visitMethodList(cache, mlist);
             }
         }
 
@@ -931,19 +1249,19 @@ public:
             pint_t *typelist = proto->getExtendedMethodTypes(cache);
 
             if ((mlist = proto->getInstanceMethods(cache))) {
-                mVisitor.visitProtocolMethodList(mlist, typelist);
+                mVisitor.visitProtocolMethodList(cache, mlist, typelist);
                 if (typelist) typelist += mlist->getCount();
             }
             if ((mlist = proto->getClassMethods(cache))) {
-                mVisitor.visitProtocolMethodList(mlist, typelist);
+                mVisitor.visitProtocolMethodList(cache, mlist, typelist);
                 if (typelist) typelist += mlist->getCount();
             }
             if ((mlist = proto->getOptionalInstanceMethods(cache))) {
-                mVisitor.visitProtocolMethodList(mlist, typelist);
+                mVisitor.visitProtocolMethodList(cache, mlist, typelist);
                 if (typelist) typelist += mlist->getCount();
             }
             if ((mlist = proto->getOptionalClassMethods(cache))) {
-                mVisitor.visitProtocolMethodList(mlist, typelist);
+                mVisitor.visitProtocolMethodList(cache, mlist, typelist);
                 if (typelist) typelist += mlist->getCount();
             }
         }
@@ -961,25 +1279,36 @@ class SelectorOptimizer {
     std::set<pint_t> selectorRefVMAddrs;
 
     friend class MethodListWalker<P, SelectorOptimizer<P,V> >;
-    void visitMethodList(objc_method_list_t<P> *mlist)
+    void visitMethodList(ContentAccessor* cache, objc_method_list_t<P> *mlist)
     {
         // Gather selectors. Update method names.
         for (uint32_t m = 0; m < mlist->getCount(); m++) {
-            pint_t oldValue = mlist->get(m).getName();
+            // Read names as relative offsets to selRefs
+            pint_t oldValue = mlist->getName(cache, m, false);
             pint_t newValue = mVisitor.visit(oldValue);
-            mlist->get(m).setName(newValue);
+            // And write names as relative offsets to SELs themselves.
+            mlist->setName(cache, m, newValue, true);
         }
+        // Set this method list as now being relative offsets directly to the selector string
+        if ( mlist->usesRelativeMethods() )
+            mlist->setMethodListSelectorsAreDirect();
+
         // Do not setFixedUp: the methods are not yet sorted.
     }
 
-    void visitProtocolMethodList(objc_method_list_t<P> *mlist, pint_t *types)
+    void visitProtocolMethodList(ContentAccessor* cache, objc_method_list_t<P> *mlist, pint_t *types)
     {
-        visitMethodList(mlist);
+        visitMethodList(cache, mlist);
     }
 
 public:
 
-    SelectorOptimizer(V& visitor) : mVisitor(visitor) { }
+    SelectorOptimizer(V& visitor, bool& relativeMethodListSelectorsAreDirect) : mVisitor(visitor) {
+        // This pass requires that relative method lists are initially indirected via the selector
+        // ref.  After this pass runs we'll use relative offsets to the selectors themselves
+        assert(!relativeMethodListSelectorsAreDirect);
+        relativeMethodListSelectorsAreDirect = true;
+    }
 
     void visitCoalescedStrings(const CacheBuilder::CacheCoalescedText& coalescedText) {
         mVisitor.visitCoalescedStrings(coalescedText);
@@ -1161,37 +1490,27 @@ class MethodListSorter {
     typedef typename P::uint_t pint_t;
 
     uint32_t _optimized;
+    bool     _isOffsetToSel;
 
     friend class MethodListWalker<P, MethodListSorter<P> >;
-    void visitMethodList(objc_method_list_t<P> *mlist)
-    {
-        typename objc_method_t<P>::SortBySELAddress sorter;
-        std::stable_sort(mlist->begin(), mlist->end(), sorter);
-        mlist->setFixedUp();
+
+    void sortMethodList(ContentAccessor* cache, objc_method_list_t<P> *mlist, pint_t *typelist) {
+        mlist->sortMethods(cache, typelist, _isOffsetToSel);
         _optimized++;
     }
 
-    void visitProtocolMethodList(objc_method_list_t<P> *mlist, pint_t *typelist)
+    void visitMethodList(ContentAccessor* cache, objc_method_list_t<P> *mlist)
     {
-        typename objc_method_t<P>::SortBySELAddress sorter;
-        // can't easily use std::stable_sort here
-        for (uint32_t i = 0; i < mlist->getCount(); i++) {
-            for (uint32_t j = i+1; j < mlist->getCount(); j++) {
-                objc_method_t<P>& mi = mlist->get(i);
-                objc_method_t<P>& mj = mlist->get(j);
-                if (! sorter(mi, mj)) {
-                    std::swap(mi, mj);
-                    if (typelist) std::swap(typelist[i], typelist[j]);
-                }
-            }
-        }
+        sortMethodList(cache, mlist, nullptr);
+    }
 
-        mlist->setFixedUp();
-        _optimized++;
+    void visitProtocolMethodList(ContentAccessor* cache, objc_method_list_t<P> *mlist, pint_t *typelist)
+    {
+        sortMethodList(cache, mlist, typelist);
     }
 
 public:
-    MethodListSorter() : _optimized(0) { }
+    MethodListSorter(bool isOffsetToSel) : _optimized(0), _isOffsetToSel(isOffsetToSel) { }
 
     size_t optimized() const { return _optimized; }
 
index b47a80ac520c9095467c0050ab201d176dce7d37..b62747c245f8ee5c0b2b844ccad2aed10ab51979 100644 (file)
@@ -42,7 +42,7 @@
 #include "MachOAnalyzer.h"
 #include "Diagnostics.h"
 #include "DyldSharedCache.h"
-#include "SharedCacheBuilder.h"
+#include "CacheBuilder.h"
 
 static const bool verbose = false;
 
@@ -52,11 +52,13 @@ static const bool verbose = false;
 template <typename P>
 class StubOptimizer {
 public:
-                            StubOptimizer(const DyldSharedCache* cache, macho_header<P>* mh, Diagnostics& diags);
+                            StubOptimizer(int64_t cacheSlide, uint64_t cacheUnslidAddr,
+                                          const std::string& archName, macho_header<P>* mh,
+                                          const char* dylibID, Diagnostics& diags);
     void                    buildStubMap(const std::unordered_set<std::string>& neverStubEliminate);
     void                    optimizeStubs();
     void                    optimizeCallSites(std::unordered_map<uint64_t, uint64_t>& targetAddrToOptStubAddr);
-    const char*             installName() { return _installName; }
+    const char*             dylibID() { return _dylibID; }
     const uint8_t*          exportsTrie() {
         if ( _dyldInfo != nullptr )
             return &_linkeditBias[_dyldInfo->export_off()];
@@ -79,7 +81,7 @@ public:
     uint32_t                _branchToReUsedOptimizedStubCount = 0;
 
 private:
-    Diagnostics             _diagnostics;
+    Diagnostics&            _diagnostics;
 
     typedef std::function<bool(uint8_t callSiteKind, uint64_t callSiteAddr, uint64_t stubAddr, uint32_t& instruction)> CallSiteHandler;
     typedef typename P::uint_t pint_t;
@@ -107,6 +109,7 @@ private:
     int32_t                 getDisplacementFromThumbBranch(uint32_t instruction, uint32_t instrAddr);
     uint32_t                setDisplacementInThumbBranch(uint32_t instruction,  uint32_t instrAddr,
                                                          int32_t displacement, bool targetIsThumb);
+    uint32_t                cpuSubtype() { return ((dyld3::MachOFile*)_mh)->maskedCpuSubtype(); }
 
 
     struct AddressAndName { pint_t targetVMAddr; const char* targetName; };
@@ -123,7 +126,7 @@ private:
     uint32_t                                _linkeditSize        = 0;
     uint64_t                                _linkeditAddr        = 0;
     const uint8_t*                          _linkeditBias        = nullptr;
-    const char*                             _installName         = nullptr;
+    const char*                             _dylibID             = nullptr;
     const macho_symtab_command<P>*          _symTabCmd           = nullptr;
     const macho_dysymtab_command<P>*        _dynSymTabCmd        = nullptr;
     const macho_dyld_info_command<P>*       _dyldInfo            = nullptr;
@@ -143,11 +146,14 @@ private:
 
 
 template <typename P>
-StubOptimizer<P>::StubOptimizer(const DyldSharedCache* cache, macho_header<P>* mh, Diagnostics& diags)
-: _mh(mh), _diagnostics(diags)
+StubOptimizer<P>::StubOptimizer(int64_t cacheSlide, uint64_t cacheUnslidAddr,
+                                const std::string& archName,
+                                macho_header<P>* mh, const char* dylibID,
+                                Diagnostics& diags)
+    : _mh(mh), _dylibID(dylibID),
+      _cacheSlide(cacheSlide), _cacheUnslideAddr(cacheUnslidAddr),
+      _diagnostics(diags)
 {
-    _cacheSlide = (long)cache - cache->unslidLoadAddress();
-    _cacheUnslideAddr = cache->unslidLoadAddress();
     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;
@@ -155,9 +161,6 @@ StubOptimizer<P>::StubOptimizer(const DyldSharedCache* cache, macho_header<P>* m
     const macho_load_command<P>* cmd = cmds;
     for (uint32_t i = 0; i < cmd_count; ++i) {
         switch (cmd->cmd()) {
-            case LC_ID_DYLIB:
-                _installName = ((macho_dylib_command<P>*)cmd)->name();
-                break;
             case LC_SYMTAB:
                 _symTabCmd = (macho_symtab_command<P>*)cmd;
                 break;
@@ -215,17 +218,17 @@ uint32_t StubOptimizer<P>::lazyPointerAddrFromArmStub(const uint8_t* stubInstruc
     int32_t  stubData   = E::get32(*(uint32_t*)(stubInstructions+12));
     if ( stubInstr1 != 0xe59fc004 ) {
         _diagnostics.warning("first instruction of stub (0x%08X) is not 'ldr ip, pc + 12' for stub at addr 0x%0llX in %s",
-                stubInstr1, (uint64_t)stubVMAddr, _installName);
+                stubInstr1, (uint64_t)stubVMAddr, _dylibID);
         return 0;
     }
     if ( stubInstr2 != 0xe08fc00c ) {
         _diagnostics.warning("second instruction of stub (0x%08X) is not 'add ip, pc, ip' for stub at addr 0x%0llX in %s",
-                stubInstr1, (uint64_t)stubVMAddr, _installName);
+                stubInstr1, (uint64_t)stubVMAddr, _dylibID);
         return 0;
     }
     if ( stubInstr3 != 0xe59cf000 ) {
         _diagnostics.warning("third instruction of stub (0x%08X) is not 'ldr pc, [ip]' for stub at addr 0x%0llX in %s",
-                stubInstr1, (uint64_t)stubVMAddr, _installName);
+                stubInstr1, (uint64_t)stubVMAddr, _dylibID);
         return 0;
     }
     return stubVMAddr + 12 + stubData;
@@ -238,7 +241,7 @@ uint64_t StubOptimizer<P>::lazyPointerAddrFromArm64Stub(const uint8_t* stubInstr
     uint32_t stubInstr1 = E::get32(*(uint32_t*)stubInstructions);
     if ( (stubInstr1 & 0x9F00001F) != 0x90000010 ) {
         _diagnostics.warning("first instruction of stub (0x%08X) is not ADRP for stub at addr 0x%0llX in %s",
-                stubInstr1, (uint64_t)stubVMAddr, _installName);
+                stubInstr1, (uint64_t)stubVMAddr, _dylibID);
         return 0;
     }
     int32_t adrpValue = ((stubInstr1 & 0x00FFFFE0) >> 3) | ((stubInstr1 & 0x60000000) >> 29);
@@ -247,7 +250,7 @@ uint64_t StubOptimizer<P>::lazyPointerAddrFromArm64Stub(const uint8_t* stubInstr
     uint32_t stubInstr2 = E::get32(*(uint32_t*)(stubInstructions + 4));
     if ( (stubInstr2 & 0xFFC003FF) != 0xF9400210 ) {
         _diagnostics.warning("second instruction of stub (0x%08X) is not LDR for stub at addr 0x%0llX in %s",
-                stubInstr2, (uint64_t)stubVMAddr, _installName);
+                stubInstr2, (uint64_t)stubVMAddr, _dylibID);
         return 0;
     }
     uint32_t ldrValue = ((stubInstr2 >> 10) & 0x00000FFF);
@@ -261,7 +264,7 @@ uint64_t StubOptimizer<P>::lazyPointerAddrFromArm64_32Stub(const uint8_t* stubIn
     uint32_t stubInstr1 = E::get32(*(uint32_t*)stubInstructions);
     if ( (stubInstr1 & 0x9F00001F) != 0x90000010 ) {
         _diagnostics.warning("first instruction of stub (0x%08X) is not ADRP for stub at addr 0x%0llX in %s",
-                stubInstr1, (uint64_t)stubVMAddr, _installName);
+                stubInstr1, (uint64_t)stubVMAddr, _dylibID);
         return 0;
     }
     int32_t adrpValue = ((stubInstr1 & 0x00FFFFE0) >> 3) | ((stubInstr1 & 0x60000000) >> 29);
@@ -270,7 +273,7 @@ uint64_t StubOptimizer<P>::lazyPointerAddrFromArm64_32Stub(const uint8_t* stubIn
     uint32_t stubInstr2 = E::get32(*(uint32_t*)(stubInstructions + 4));
     if ( (stubInstr2 & 0xFFC003FF) != 0xB9400210 ) {
         _diagnostics.warning("second instruction of stub (0x%08X) is not LDR for stub at addr 0x%0llX in %s",
-                stubInstr2, (uint64_t)stubVMAddr, _installName);
+                stubInstr2, (uint64_t)stubVMAddr, _dylibID);
         return 0;
     }
     uint32_t ldrValue = ((stubInstr2 >> 10) & 0x00000FFF);
@@ -288,7 +291,7 @@ uint64_t StubOptimizer<P>::lazyPointerAddrFromArm64eStub(const uint8_t* stubInst
     // 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);
+                stubInstr1, (uint64_t)stubVMAddr, _dylibID);
         return 0;
     }
     int32_t adrpValue = ((stubInstr1 & 0x00FFFFE0) >> 3) | ((stubInstr1 & 0x60000000) >> 29);
@@ -299,7 +302,7 @@ uint64_t StubOptimizer<P>::lazyPointerAddrFromArm64eStub(const uint8_t* stubInst
     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);
+                             stubInstr2, (uint64_t)stubVMAddr, _dylibID);
         return 0;
     }
     uint32_t addValue = ((stubInstr2 & 0x003FFC00) >> 10);
@@ -308,7 +311,7 @@ uint64_t StubOptimizer<P>::lazyPointerAddrFromArm64eStub(const uint8_t* stubInst
     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);
+                stubInstr2, (uint64_t)stubVMAddr, _dylibID);
         return 0;
     }
     return (stubVMAddr & (-4096)) + adrpValue*4096 + addValue;
@@ -350,19 +353,19 @@ void StubOptimizer<P>::buildStubMap(const std::unordered_set<std::string>& never
                             default:
                                 if ( symbolIndex >= _symTabCmd->nsyms() ) {
                                     _diagnostics.warning("symbol index out of range (%d of %d) for stub at addr 0x%0llX in %s",
-                                        symbolIndex, _symTabCmd->nsyms(), (uint64_t)stubVMAddr, _installName);
+                                        symbolIndex, _symTabCmd->nsyms(), (uint64_t)stubVMAddr, _dylibID);
                                     continue;
                                 }
                                 const macho_nlist<P>* sym = &symbolTable[symbolIndex];
                                 uint32_t stringOffset = sym->n_strx();
                                 if ( stringOffset > _symTabCmd->strsize() ) {
                                     _diagnostics.warning("symbol string offset out of range (%u of %u) for stub at addr 0x%0llX in %s",
-                                        stringOffset, sym->n_strx(), (uint64_t)stubVMAddr, _installName);
+                                        stringOffset, sym->n_strx(), (uint64_t)stubVMAddr, _dylibID);
                                     continue;
                                 }
                                 const char* symName = &symbolStrings[stringOffset];
                                 if ( neverStubEliminate.count(symName) ) {
-                                    //fprintf(stderr, "stubVMAddr=0x%llX, not bypassing stub to %s in %s because target is interposable\n", (uint64_t)stubVMAddr, symName, _installName);
+                                    //fprintf(stderr, "stubVMAddr=0x%llX, not bypassing stub to %s in %s because target is interposable\n", (uint64_t)stubVMAddr, symName, _dylibID);
                                     _stubsLeftInterposable++;
                                     continue;
                                 }
@@ -370,19 +373,19 @@ void StubOptimizer<P>::buildStubMap(const std::unordered_set<std::string>& never
                                 pint_t targetLPAddr = 0;
                                 switch ( _mh->cputype() ) {
                                     case CPU_TYPE_ARM64:
-                                    case CPU_TYPE_ARM64_32:
 #if SUPPORT_ARCH_arm64e
-                                        if (_mh->cpusubtype() == CPU_SUBTYPE_ARM64E)
+                                        if (cpuSubtype() == CPU_SUBTYPE_ARM64E)
                                             targetLPAddr = (pint_t)lazyPointerAddrFromArm64eStub(stubInstrs, stubVMAddr);
                                         else
 #endif
+                                            targetLPAddr = (pint_t)lazyPointerAddrFromArm64Stub(stubInstrs, stubVMAddr);
+                                        break;
 #if SUPPORT_ARCH_arm64_32
-                                        if (_mh->cputype() == CPU_TYPE_ARM64_32)
+                                    case CPU_TYPE_ARM64_32:
+                                        if (cpuSubtype() == CPU_TYPE_ARM64_32)
                                             targetLPAddr = (pint_t)lazyPointerAddrFromArm64_32Stub(stubInstrs, stubVMAddr);
-                                        else
-#endif
-                                            targetLPAddr = (pint_t)lazyPointerAddrFromArm64Stub(stubInstrs, stubVMAddr);
                                         break;
+#endif
                                     case CPU_TYPE_ARM:
                                         targetLPAddr = (pint_t)lazyPointerAddrFromArmStub(stubInstrs, (uint32_t)stubVMAddr);
                                         break;
@@ -412,23 +415,26 @@ void StubOptimizer<P>::buildStubMap(const std::unordered_set<std::string>& never
                                 lpVMAddr = (pint_t)sect->addr() + j * sizeof(pint_t);
                                 if ( symbolIndex >= _symTabCmd->nsyms() ) {
                                     _diagnostics.warning("symbol index out of range (%d of %d) for lazy pointer at addr 0x%0llX in %s",
-                                            symbolIndex, _symTabCmd->nsyms(), (uint64_t)lpVMAddr, _installName);
+                                            symbolIndex, _symTabCmd->nsyms(), (uint64_t)lpVMAddr, _dylibID);
                                     continue;
                                 }
                                 const macho_nlist<P>* sym = &symbolTable[symbolIndex];
                                 uint32_t stringOffset = sym->n_strx();
                                 if ( stringOffset > _symTabCmd->strsize() ) {
                                     _diagnostics.warning("symbol string offset out of range (%u of %u) for lazy pointer at addr 0x%0llX in %s",
-                                            stringOffset, sym->n_strx(), (uint64_t)lpVMAddr, _installName);
+                                            stringOffset, sym->n_strx(), (uint64_t)lpVMAddr, _dylibID);
                                     continue;
                                 }
                                 const char* symName = &symbolStrings[stringOffset];
                                 if ( (lpValue > textSegStartAddr) && (lpValue< textSegEndAddr) ) {
-                                    //fprintf(stderr, "skipping lazy pointer at 0x%0lX to %s in %s because target is within dylib\n", (long)lpVMAddr, symName, _installName);
+                                    //fprintf(stderr, "skipping lazy pointer at 0x%0lX to %s in %s because target is within dylib\n", (long)lpVMAddr, symName, _dylibID);
                                 }
                                 else if ( (sizeof(pint_t) == 8) && ((lpValue % 4) != 0) ) {
-                                    _diagnostics.warning("lazy pointer at 0x%0llX does not point to 4-byte aligned address(0x%0llX) in %s",
-                                                (uint64_t)lpVMAddr, (uint64_t)lpValue, _installName);
+                                    // Only warn on lazy pointers which correspond to call targets
+                                    if ( sectionType == S_LAZY_SYMBOL_POINTERS ) {
+                                        _diagnostics.warning("lazy pointer at 0x%0llX does not point to 4-byte aligned address(0x%0llX) for symbol '%s' in %s",
+                                                             (uint64_t)lpVMAddr, (uint64_t)lpValue, symName, _dylibID);
+                                    }
                                 }
                                 else {
                                    _lpAddrToTargetAddr[lpVMAddr] = lpValue;
@@ -453,7 +459,7 @@ void StubOptimizer<P>::forEachCallSiteToAStub(CallSiteHandler handler)
     const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()];
     const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize()];
     if ( *infoStart++ != DYLD_CACHE_ADJ_V2_FORMAT ) {
-        _diagnostics.error("malformed split seg info in %s", _installName);
+        _diagnostics.error("malformed split seg info in %s", _dylibID);
         return;
     }
 
@@ -477,7 +483,7 @@ void StubOptimizer<P>::forEachCallSiteToAStub(CallSiteHandler handler)
             for (uint64_t k=0; k < fromOffsetCount; ++k) {
                 uint64_t kind = read_uleb128(p, infoEnd);
                 if ( kind > 13 ) {
-                    _diagnostics.error("bad kind (%llu) value in %s\n", kind, _installName);
+                    _diagnostics.error("bad kind (%llu) value in %s\n", kind, _dylibID);
                 }
                 uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd);
                 uint64_t fromSectionOffset = 0;
@@ -531,7 +537,7 @@ template <typename P>
 uint32_t StubOptimizer<P>::setDisplacementInThumbBranch(uint32_t instruction,  uint32_t instrAddr,
                                                         int32_t displacement, bool targetIsThumb) {
     if ( (displacement > 16777214) || (displacement < (-16777216)) ) {
-        _diagnostics.error("thumb branch out of range at 0x%0X in %s", instrAddr, _installName);
+        _diagnostics.error("thumb branch out of range at 0x%0X in %s", instrAddr, _dylibID);
         return 0;
     }
     bool is_bl = ((instruction & 0xD000F800) == 0xD000F000);
@@ -551,12 +557,12 @@ uint32_t StubOptimizer<P>::setDisplacementInThumbBranch(uint32_t instruction,  u
     }
     else if (is_b) {
         if ( !targetIsThumb ) {
-            _diagnostics.error("no pc-rel thumb branch instruction that switches to arm mode at 0x%0X in %s", instrAddr, _installName);
+            _diagnostics.error("no pc-rel thumb branch instruction that switches to arm mode at 0x%0X in %s", instrAddr, _dylibID);
             return 0;
         }
     }
     else {
-        _diagnostics.error("not b/bl/blx at 0x%0X in %s", instrAddr, _installName);
+        _diagnostics.error("not b/bl/blx at 0x%0X in %s", instrAddr, _dylibID);
         return 0;
     }
     uint32_t s = (uint32_t)(displacement >> 24) & 0x1;
@@ -582,13 +588,13 @@ void StubOptimizer<P>::optimizeArmCallSites(std::unordered_map<uint64_t, uint64_
             bool is_blx = ((instruction & 0xD000F800) == 0xC000F000);
             bool is_b = ((instruction & 0xD000F800) == 0x9000F000);
             if ( !is_bl && !is_blx && !is_b ){
-                _diagnostics.warning("non-branch instruction at 0x%0llX in %s", callSiteAddr, _installName);
+                _diagnostics.warning("non-branch instruction at 0x%0llX in %s", callSiteAddr, _dylibID);
                 return false;
             }
             int32_t brDelta = getDisplacementFromThumbBranch(instruction, (uint32_t)callSiteAddr);
             pint_t targetAddr = (pint_t)callSiteAddr + 4 + brDelta;
             if ( targetAddr != stubAddr ) {
-                _diagnostics.warning("stub target mismatch at callsite 0x%0llX in %s", callSiteAddr, _installName);
+                _diagnostics.warning("stub target mismatch at callsite 0x%0llX in %s", callSiteAddr, _dylibID);
                 return false;
             }
             // ignore branch if not to a known stub
@@ -850,7 +856,7 @@ void StubOptimizer<P>::optimizeCallSites(std::unordered_map<uint64_t, uint64_t>&
         case CPU_TYPE_ARM64:
             optimizeArm64CallSites(targetAddrToOptStubAddr);
 #if SUPPORT_ARCH_arm64e
-            if (_mh->cpusubtype() == CPU_SUBTYPE_ARM64E)
+            if (cpuSubtype() == CPU_SUBTYPE_ARM64E)
                 optimizeArm64eStubs();
             else
 #endif
@@ -871,38 +877,47 @@ void StubOptimizer<P>::optimizeCallSites(std::unordered_map<uint64_t, uint64_t>&
         _diagnostics.verbose("dylib has %6u BLs to %4u stubs. Changed %5u, %5u, %5u BLs to use direct branch, optimized stub, neighbor's optimized stub. "
                              "%5u stubs left interposable, %4u stubs optimized. path=%s\n",
                             _branchToStubCount, _stubCount, _branchOptimizedToDirectCount, _branchToOptimizedStubCount, _branchToReUsedOptimizedStubCount,
-                            _stubsLeftInterposable, _stubOptimizedCount, _installName);
+                            _stubsLeftInterposable, _stubOptimizedCount, _dylibID);
     }
 
 }
 
 template <typename P>
-void bypassStubs(DyldSharedCache* cache, const std::string& archName, std::unordered_map<uint64_t, uint64_t>& targetAddrToOptStubAddr,
-                 const char* const neverStubEliminateDylibs[], const char* const neverStubEliminateSymbols[],
+void bypassStubs(std::vector<std::pair<const mach_header*, const char*>> images,
+                 const std::string& archName,
+                 int64_t cacheSlide, uint64_t cacheUnslidAddr,
+                 const DyldSharedCache* dyldCache,
+                 const char* const neverStubEliminateSymbols[],
                  Diagnostics& diags)
 {
+    std::unordered_map<uint64_t, uint64_t> targetAddrToOptStubAddr;
     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>(cache, (macho_header<P>*)mh, diags));
-    });
+    for (std::pair<const mach_header*, const char*> image : images) {
+        optimizers.push_back(new StubOptimizer<P>(cacheSlide, cacheUnslidAddr, archName,
+                                                  (macho_header<P>*)image.first, image.second,
+                                                  diags));
+    }
 
     // build set of functions to never stub-eliminate because tools may need to override them
     std::unordered_set<std::string> neverStubEliminate;
     for (const char* const* p=neverStubEliminateSymbols; *p != nullptr; ++p) {
         neverStubEliminate.insert(*p);
     }
-    for (const char* const* d=neverStubEliminateDylibs; *d != nullptr; ++d) {
+
+#if !BUILDING_APP_CACHE_UTIL
+    // Customer shared caches support overriding libdispatch
+    if ( dyldCache != nullptr ) {
         for (StubOptimizer<P>* op : optimizers) {
-            if ( strcmp(op->installName(), *d) == 0 ) {
+            if ( dyldCache->isOverridablePath(op->dylibID()) ) {
                 // add all exports
                 const uint8_t* exportsStart = op->exportsTrie();
                 const uint8_t* exportsEnd = exportsStart + op->exportsTrieSize();
                 std::vector<ExportInfoTrie::Entry> exports;
                 if ( !ExportInfoTrie::parseTrie(exportsStart, exportsEnd, exports) ) {
-                    diags.error("malformed exports trie in %s", *d);
+                    diags.error("malformed exports trie in %s", op->dylibID());
                     return;
                 }
                 for(const ExportInfoTrie::Entry& entry : exports) {
@@ -911,6 +926,7 @@ void bypassStubs(DyldSharedCache* cache, const std::string& archName, std::unord
             }
         }
     }
+#endif
 
     // build maps of stubs-to-lp and lp-to-target
     for (StubOptimizer<P>* op : optimizers)
@@ -934,20 +950,32 @@ void bypassStubs(DyldSharedCache* cache, const std::string& archName, std::unord
         delete op;
 }
 
-void SharedCacheBuilder::optimizeAwayStubs()
+void CacheBuilder::optimizeAwayStubs(const std::vector<std::pair<const mach_header*, const char*>>& images,
+                                     int64_t cacheSlide, uint64_t cacheUnslidAddr,
+                                     const DyldSharedCache* dyldCache,
+                                     const char* const neverStubEliminateSymbols[])
 {
     std::unordered_map<uint64_t, uint64_t> targetAddrToOptStubAddr;
-
-    DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
-    std::string archName = dyldCache->archName();
+    std::string archName = _options.archs->name();
 #if SUPPORT_ARCH_arm64_32
-    if ( startsWith(archName, "arm64_32") )
-        bypassStubs<Pointer32<LittleEndian> >(dyldCache, archName, targetAddrToOptStubAddr, _s_neverStubEliminateDylibs, _s_neverStubEliminateSymbols, _diagnostics);
-    else
+    if ( startsWith(archName, "arm64_32") ) {
+        bypassStubs<Pointer32<LittleEndian> >(images, archName, cacheSlide, cacheUnslidAddr,
+                                              dyldCache, neverStubEliminateSymbols,
+                                              _diagnostics);
+        return;
+    }
 #endif
-    if ( startsWith(archName, "arm64") )
-        bypassStubs<Pointer64<LittleEndian> >(dyldCache, archName, targetAddrToOptStubAddr, _s_neverStubEliminateDylibs, _s_neverStubEliminateSymbols, _diagnostics);
-    else if ( archName == "armv7k" )
-        bypassStubs<Pointer32<LittleEndian>>(dyldCache, archName, targetAddrToOptStubAddr, _s_neverStubEliminateDylibs, _s_neverStubEliminateSymbols, _diagnostics);
+    if ( startsWith(archName, "arm64") ) {
+        bypassStubs<Pointer64<LittleEndian> >(images, archName, cacheSlide, cacheUnslidAddr,
+                                              dyldCache, neverStubEliminateSymbols,
+                                              _diagnostics);
+        return;
+    }
+    if ( archName == "armv7k" ) {
+        bypassStubs<Pointer32<LittleEndian> >(images, archName, cacheSlide, cacheUnslidAddr,
+                                              dyldCache, neverStubEliminateSymbols,
+                                              _diagnostics);
+        return;
+    }
     // no stub optimization done for other arches
 }
index 967f8cbdf90584990a6a76deda4aaa7b27936a73..98e93337aeb2806c2c138320a1641e338efe6763 100644 (file)
@@ -53,7 +53,12 @@ class SortedStringPool
 public:
     // add a string and symbol table entry index to be updated later
     void add(uint32_t symbolIndex, const char* symbolName) {
-        _map[symbolName].push_back(symbolIndex);
+        _map[symbolName].push_back({ symbolIndex, false });
+    }
+
+    // add a string and symbol table entry index to be updated later
+    void addIndirect(uint32_t symbolIndex, const char* symbolName) {
+        _map[symbolName].push_back({ symbolIndex, true });
     }
 
     // copy sorted strings to buffer and update all symbol's string offsets
@@ -66,8 +71,13 @@ public:
             // append string to pool
             strcpy(&dstStringPool[poolOffset], symName.c_str());
             //  set each string offset of each symbol using it
-            for (uint32_t symbolIndex : entry.second) {
-                symbolTable[symbolIndex].set_n_strx(poolOffset);
+            for (std::pair<uint32_t, bool> symbolIndexAndIndirect : entry.second) {
+                if ( symbolIndexAndIndirect.second ) {
+                    // Indirect
+                    symbolTable[symbolIndexAndIndirect.first].set_n_value(poolOffset);
+                } else {
+                    symbolTable[symbolIndexAndIndirect.first].set_n_strx(poolOffset);
+                }
             }
             poolOffset += symName.size() + 1;
         }
@@ -85,7 +95,7 @@ public:
 
 
 private:
-    std::map<std::string, std::vector<uint32_t>> _map;
+    std::map<std::string, std::vector<std::pair<uint32_t, bool>>> _map;
 };
 
 
@@ -103,11 +113,12 @@ struct LocalSymbolInfo
 template <typename P>
 class LinkeditOptimizer {
 public:
-                    LinkeditOptimizer(void* cacheBuffer, macho_header<P>* mh, Diagnostics& diag);
+                    LinkeditOptimizer(const void* containerBuffer, macho_header<P>* mh, const char* dylibID,
+                                      Diagnostics& diag);
 
     uint32_t        linkeditSize() { return _linkeditSize; }
     uint64_t        linkeditAddr() { return _linkeditAddr; }
-    const char*     installName() { return _installName; }
+    const char*     dylibID() { return _dylibID; }
     void            copyWeakBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset);
     void            copyLazyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset);
     void            copyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset);
@@ -124,6 +135,9 @@ public:
                                        uint32_t sharedSymbolTableStartOffset, uint32_t sharedSymbolTableCount,
                                        uint32_t sharedSymbolStringsOffset, uint32_t sharedSymbolStringsSize);
 
+    typedef CacheBuilder::DylibStripMode DylibStripMode;
+    void            setStripMode(DylibStripMode stripMode);
+
     macho_header<P>*                        machHeader() { return _mh; }
     const std::vector<const char*>          getDownwardDependents() { return _downDependentPaths; }
     const std::vector<const char*>          getAllDependents() { return _allDependentPaths; }
@@ -138,8 +152,11 @@ 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);
+    static void optimizeLinkedit(CacheBuilder& builder, const void* containerBuffer,
+                                 CacheBuilder::UnmappedRegion* localSymbolsRegion,
+                                 const std::vector<std::tuple<const mach_header*, const char*, DylibStripMode>>& images);
+    static void mergeLinkedits(CacheBuilder& builder, CacheBuilder::UnmappedRegion* localSymbolsRegion,
+                               std::vector<LinkeditOptimizer<P>*>& optimizers);
 
 private:
 
@@ -147,12 +164,12 @@ private:
     typedef typename P::E E;
 
     macho_header<P>*                        _mh;
-    void*                                   _cacheBuffer;
+    const void*                             _containerBuffer;
     Diagnostics&                            _diagnostics;
     uint32_t                                _linkeditSize        = 0;
     uint64_t                                _linkeditAddr        = 0;
     const uint8_t*                          _linkeditBias       = nullptr;
-    const char*                             _installName        = nullptr;
+    const char*                             _dylibID            = nullptr;
     macho_symtab_command<P>*                _symTabCmd          = nullptr;
     macho_dysymtab_command<P>*              _dynSymTabCmd       = nullptr;
     macho_dyld_info_command<P>*             _dyldInfo           = nullptr;
@@ -182,13 +199,14 @@ private:
     uint32_t                                _newDataInCodeOffset            = 0;
     uint32_t                                _newIndirectSymbolTableOffset   = 0;
     uint64_t                                _dyldSectionAddr                = 0;
+    DylibStripMode                          _stripMode                 = DylibStripMode::stripAll;
 };
 
 
-
 template <typename P>
-LinkeditOptimizer<P>::LinkeditOptimizer(void* cacheBuffer, macho_header<P>* mh, Diagnostics& diag)
-: _mh(mh), _cacheBuffer(cacheBuffer), _diagnostics(diag)
+LinkeditOptimizer<P>::LinkeditOptimizer(const void* containerBuffer, macho_header<P>* mh,
+                                        const char* dylibID, Diagnostics& diag)
+: _mh(mh), _dylibID(dylibID), _containerBuffer(containerBuffer), _diagnostics(diag)
 {
     const unsigned origLoadCommandsSize = mh->sizeofcmds();
     unsigned bytesRemaining = origLoadCommandsSize;
@@ -204,9 +222,6 @@ LinkeditOptimizer<P>::LinkeditOptimizer(void* cacheBuffer, macho_header<P>* mh,
     for (uint32_t i = 0; i < cmdCount; ++i) {
         bool remove = false;
         switch (cmd->cmd()) {
-            case LC_ID_DYLIB:
-                _installName = ((macho_dylib_command<P>*)cmd)->name();
-                break;
             case LC_SYMTAB:
                 _symTabCmd = (macho_symtab_command<P>*)cmd;
                 break;
@@ -309,6 +324,11 @@ LinkeditOptimizer<P>::LinkeditOptimizer(void* cacheBuffer, macho_header<P>* mh,
     mh->set_sizeofcmds(origLoadCommandsSize - bytesRemaining);
 }
 
+template <typename P>
+void LinkeditOptimizer<P>::setStripMode(DylibStripMode stripMode) {
+    _stripMode = stripMode;
+}
+
 /*
 static void dumpLoadCommands(const uint8_t* mheader)
 {
@@ -412,20 +432,22 @@ void LinkeditOptimizer<P>::updateLoadCommands(uint32_t mergedLinkeditStartOffset
     _symTabCmd->set_strsize(sharedSymbolStringsSize);
 
     // update dynamic symbol table to have proper offsets into shared symbol table
-    _dynSymTabCmd->set_ilocalsym(0);
-    _dynSymTabCmd->set_nlocalsym(_newLocalSymbolCount);
-    _dynSymTabCmd->set_iextdefsym(_newExportedSymbolsStartIndex-_newLocalSymbolsStartIndex);
-    _dynSymTabCmd->set_nextdefsym(_newExportedSymbolCount);
-    _dynSymTabCmd->set_iundefsym(_newImportedSymbolsStartIndex-_newLocalSymbolsStartIndex);
-    _dynSymTabCmd->set_nundefsym(_newImportedSymbolCount);
-    _dynSymTabCmd->set_tocoff(0);
-    _dynSymTabCmd->set_ntoc(0);
-    _dynSymTabCmd->set_modtaboff(0);
-    _dynSymTabCmd->set_nmodtab(0);
-    _dynSymTabCmd->set_indirectsymoff(mergedLinkeditStartOffset + _newIndirectSymbolTableOffset);
-    _dynSymTabCmd->set_extreloff(0);
-    _dynSymTabCmd->set_locreloff(0);
-    _dynSymTabCmd->set_nlocrel(0);
+    if ( _dynSymTabCmd != nullptr ) {
+        _dynSymTabCmd->set_ilocalsym(0);
+        _dynSymTabCmd->set_nlocalsym(_newLocalSymbolCount);
+        _dynSymTabCmd->set_iextdefsym(_newExportedSymbolsStartIndex-_newLocalSymbolsStartIndex);
+        _dynSymTabCmd->set_nextdefsym(_newExportedSymbolCount);
+        _dynSymTabCmd->set_iundefsym(_newImportedSymbolsStartIndex-_newLocalSymbolsStartIndex);
+        _dynSymTabCmd->set_nundefsym(_newImportedSymbolCount);
+        _dynSymTabCmd->set_tocoff(0);
+        _dynSymTabCmd->set_ntoc(0);
+        _dynSymTabCmd->set_modtaboff(0);
+        _dynSymTabCmd->set_nmodtab(0);
+        _dynSymTabCmd->set_indirectsymoff(mergedLinkeditStartOffset + _newIndirectSymbolTableOffset);
+        _dynSymTabCmd->set_extreloff(0);
+        _dynSymTabCmd->set_locreloff(0);
+        _dynSymTabCmd->set_nlocrel(0);
+    }
 
     // update dyld info
     if ( _dyldInfo != nullptr ) {
@@ -533,11 +555,27 @@ void LinkeditOptimizer<P>::copyLocalSymbols(uint8_t* newLinkEditContent, SortedS
                                             bool redact, std::vector<LocalSymbolInfo>& localSymbolInfos,
                                             std::vector<macho_nlist<P>>& unmappedLocalSymbols, SortedStringPool<P>& localSymbolsStringPool)
 {
-    LocalSymbolInfo localInfo;
-    localInfo.dylibOffset = (uint32_t)(((uint8_t*)_mh) - (uint8_t*)_cacheBuffer);
+    localSymbolInfos.push_back(LocalSymbolInfo());
+
+    LocalSymbolInfo& localInfo = localSymbolInfos.back();
+    localInfo.dylibOffset = (uint32_t)(((uint8_t*)_mh) - (uint8_t*)_containerBuffer);
     localInfo.nlistStartIndex = (uint32_t)unmappedLocalSymbols.size();
     localInfo.nlistCount = 0;
     _newLocalSymbolsStartIndex = symbolIndex;
+    _newLocalSymbolCount = 0;
+
+    switch (_stripMode) {
+        case CacheBuilder::DylibStripMode::stripNone:
+        case CacheBuilder::DylibStripMode::stripExports:
+            break;
+        case CacheBuilder::DylibStripMode::stripLocals:
+        case CacheBuilder::DylibStripMode::stripAll:
+            return;
+    }
+
+    if ( _dynSymTabCmd == nullptr )
+        return;
+
     const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()];
     const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
     const macho_nlist<P>* const firstExport = &symbolTable[_dynSymTabCmd->ilocalsym()];
@@ -570,7 +608,6 @@ void LinkeditOptimizer<P>::copyLocalSymbols(uint8_t* newLinkEditContent, SortedS
     }
     _newLocalSymbolCount = symbolIndex - _newLocalSymbolsStartIndex;
     localInfo.nlistCount = (uint32_t)unmappedLocalSymbols.size() - localInfo.nlistStartIndex;
-    localSymbolInfos.push_back(localInfo);
 }
 
 
@@ -578,6 +615,20 @@ template <typename P>
 void LinkeditOptimizer<P>::copyExportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex)
 {
     _newExportedSymbolsStartIndex = symbolIndex;
+    _newExportedSymbolCount = 0;
+
+    switch (_stripMode) {
+        case CacheBuilder::DylibStripMode::stripNone:
+        case CacheBuilder::DylibStripMode::stripLocals:
+            break;
+        case CacheBuilder::DylibStripMode::stripExports:
+        case CacheBuilder::DylibStripMode::stripAll:
+            return;
+    }
+
+    if ( _dynSymTabCmd == nullptr )
+        return;
+
     const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()];
     const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
     const macho_nlist<P>* const firstExport = &symbolTable[_dynSymTabCmd->iextdefsym()];
@@ -606,6 +657,20 @@ template <typename P>
 void LinkeditOptimizer<P>::copyImportedSymbols(uint8_t* newLinkEditContent, SortedStringPool<P>& stringPool, uint32_t& offset, uint32_t& symbolIndex)
 {
     _newImportedSymbolsStartIndex = symbolIndex;
+    _newImportedSymbolCount = 0;
+
+    if ( _dynSymTabCmd == nullptr )
+        return;
+
+    switch (_stripMode) {
+        case CacheBuilder::DylibStripMode::stripNone:
+            break;
+        case CacheBuilder::DylibStripMode::stripLocals:
+        case CacheBuilder::DylibStripMode::stripExports:
+        case CacheBuilder::DylibStripMode::stripAll:
+            return;
+    }
+
     const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()];
     const macho_nlist<P>* const symbolTable = (macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
     const macho_nlist<P>* const firstImport = &symbolTable[_dynSymTabCmd->iundefsym()];
@@ -630,6 +695,10 @@ template <typename P>
 void LinkeditOptimizer<P>::copyIndirectSymbolTable(uint8_t* newLinkEditContent, uint32_t& offset)
 {
     _newIndirectSymbolTableOffset = offset;
+
+    if ( _dynSymTabCmd == nullptr )
+        return;
+
      const uint32_t* const indirectTable = (uint32_t*)&_linkeditBias[_dynSymTabCmd->indirectsymoff()];
     uint32_t* newIndirectTable = (uint32_t*)&newLinkEditContent[offset];
     for (uint32_t i=0; i < _dynSymTabCmd->nindirectsyms(); ++i) {
@@ -643,7 +712,9 @@ void LinkeditOptimizer<P>::copyIndirectSymbolTable(uint8_t* newLinkEditContent,
 }
 
 template <typename P>
-void LinkeditOptimizer<P>::mergeLinkedits(CacheBuilder& builder, std::vector<LinkeditOptimizer<P>*>& optimizers)
+void LinkeditOptimizer<P>::mergeLinkedits(CacheBuilder& builder,
+                                          CacheBuilder::UnmappedRegion* localSymbolsRegion,
+                                          std::vector<LinkeditOptimizer<P>*>& optimizers)
 {
     // allocate space for new linkedit data
     uint64_t totalUnoptLinkeditsSize = builder._readOnlyRegion.sizeInUse - builder._nonLinkEditReadOnlySize;
@@ -693,9 +764,11 @@ void LinkeditOptimizer<P>::mergeLinkedits(CacheBuilder& builder, std::vector<Lin
         builder._diagnostics.verbose("  lazy bindings size:      %5uKB\n", (offset-startLazyBindingsInfosOffset)/1024);
     }
 
+    bool unmapLocals = ( builder._options.localSymbolMode == DyldSharedCache::LocalSymbolsMode::unmap );
+
     // copy symbol table entries
     std::vector<macho_nlist<P>> unmappedLocalSymbols;
-    if ( builder._options.excludeLocalSymbols )
+    if ( unmapLocals )
         unmappedLocalSymbols.reserve(0x01000000);
     std::vector<LocalSymbolInfo> localSymbolInfos;
         localSymbolInfos.reserve(optimizers.size());
@@ -705,7 +778,7 @@ void LinkeditOptimizer<P>::mergeLinkedits(CacheBuilder& builder, std::vector<Lin
     uint32_t sharedSymbolTableExportsCount = 0;
     uint32_t sharedSymbolTableImportsCount = 0;
     for (LinkeditOptimizer<P>* op : optimizers) {
-         op->copyLocalSymbols(newLinkEdit, stringPool, offset, symbolIndex, builder._options.excludeLocalSymbols,
+         op->copyLocalSymbols(newLinkEdit, stringPool, offset, symbolIndex, unmapLocals,
                              localSymbolInfos, unmappedLocalSymbols, localSymbolsStringPool);
         uint32_t x = symbolIndex;
         op->copyExportedSymbols(newLinkEdit, stringPool, offset, symbolIndex);
@@ -747,7 +820,6 @@ void LinkeditOptimizer<P>::mergeLinkedits(CacheBuilder& builder, std::vector<Lin
     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
     builder._diagnostics.verbose("LINKEDITS optimized from %uMB to %uMB\n", (uint32_t)totalUnoptLinkeditsSize/(1024*1024), (uint32_t)newLinkeditUnalignedSize/(1024*1024));
@@ -756,7 +828,7 @@ void LinkeditOptimizer<P>::mergeLinkedits(CacheBuilder& builder, std::vector<Lin
     builder._readOnlyRegion.sizeInUse = builder._nonLinkEditReadOnlySize + newLinkeditAlignedSize;
 
     // overwrite end of un-opt linkedits to create a new unmapped region for local symbols
-    if ( builder._options.excludeLocalSymbols ) {
+    if ( unmapLocals ) {
         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
@@ -787,13 +859,10 @@ void LinkeditOptimizer<P>::mergeLinkedits(CacheBuilder& builder, std::vector<Lin
             ::memcpy(newLocalsSymbolTable, &unmappedLocalSymbols[0], nlistCount*sizeof(macho_nlist<P>));
             // copy string pool
             localSymbolsStringPool.copyPoolAndUpdateOffsets(((char*)infoHeader)+stringsOffset, newLocalsSymbolTable);
-            // update cache header
-            DyldSharedCache* cacheHeader = (DyldSharedCache*)builder._readExecuteRegion.buffer;
-            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;
+            localSymbolsRegion->buffer      = (uint8_t*)localsBuffer;
+            localSymbolsRegion->bufferSize  = localsBufferSize;
+            localSymbolsRegion->sizeInUse   = localsBufferSize;
         }
         else {
             builder._diagnostics.warning("could not allocate local symbols");
@@ -812,14 +881,16 @@ void LinkeditOptimizer<P>::mergeLinkedits(CacheBuilder& builder, std::vector<Lin
 
 
 template <typename P>
-void LinkeditOptimizer<P>::optimizeLinkedit(CacheBuilder& builder)
+void LinkeditOptimizer<P>::optimizeLinkedit(CacheBuilder& builder, const void* containerBuffer,
+                                            CacheBuilder::UnmappedRegion* localSymbolsRegion,
+                                            const std::vector<std::tuple<const mach_header*, const char*, DylibStripMode>>& images)
 {
-    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, builder._diagnostics));
-    });
+    for (std::tuple<const mach_header*, const char*, DylibStripMode> image : images) {
+        optimizers.push_back(new LinkeditOptimizer<P>(containerBuffer, (macho_header<P>*)std::get<0>(image), std::get<1>(image), builder._diagnostics));
+        optimizers.back()->setStripMode(std::get<2>(image));
+    }
 #if 0
     // add optimizer for each branch pool
     for (uint64_t poolOffset : branchPoolOffsets) {
@@ -828,20 +899,24 @@ void LinkeditOptimizer<P>::optimizeLinkedit(CacheBuilder& builder)
     }
 #endif
     // merge linkedit info
-    mergeLinkedits(builder, optimizers);
+    mergeLinkedits(builder, localSymbolsRegion, optimizers);
 
     // delete optimizers
     for (LinkeditOptimizer<P>* op : optimizers)
         delete op;
 }
 
-void CacheBuilder::optimizeLinkedit()
+void CacheBuilder::optimizeLinkedit(UnmappedRegion* localSymbolsRegion,
+                                    const std::vector<std::tuple<const mach_header*, const char*, DylibStripMode>>& images)
 {
+    const void* buffer = (const void*)_fullAllocatedBuffer;
     if ( _is64 ) {
-        return LinkeditOptimizer<Pointer64<LittleEndian>>::optimizeLinkedit(*this);
+        return LinkeditOptimizer<Pointer64<LittleEndian>>::optimizeLinkedit(*this, buffer,
+                                                                            localSymbolsRegion, images);
     }
     else {
-         return LinkeditOptimizer<Pointer32<LittleEndian>>::optimizeLinkedit(*this);
+         return LinkeditOptimizer<Pointer32<LittleEndian>>::optimizeLinkedit(*this, buffer,
+                                                                             localSymbolsRegion, images);
     }
 }
 
index 1e81c271f282245aa19e9304d81ee4167fdf589f..58d59b9379a0a92a1ca6f594613aa83644178282 100644 (file)
@@ -37,6 +37,7 @@
 #include "MachOFileAbstraction.hpp"
 #include "MachOLoaded.h"
 #include "MachOAnalyzer.h"
+#include "MachOAnalyzerSet.h"
 
 #ifndef MH_HAS_OBJC
   #define MH_HAS_OBJC            0x40000000
@@ -311,7 +312,7 @@ public:
     {
         if (cls->isMetaClass(cache)) return;
 
-        const char *name = cls->getName(cache);
+        const charname = cls->getName(cache);
         uint64_t name_vmaddr = cache->vmAddrForContent((void*)name);
         uint64_t cls_vmaddr = cache->vmAddrForContent(cls);
         uint64_t hinfo_vmaddr = cache->vmAddrForContent(_hInfos.hinfoForHeader(cache, header));
@@ -331,6 +332,404 @@ public:
     size_t count() const { return _count; }
 };
 
+
+/// Builds a map from (install name, class name, method name) to actual IMPs
+template <typename P>
+class IMPMapBuilder
+{
+private:
+    typedef typename P::uint_t pint_t;
+
+public:
+
+    struct MapKey {
+        std::string_view installName;
+        std::string_view className;
+        std::string_view methodName;
+        bool isInstanceMethod;
+
+        bool operator==(const MapKey& other) const {
+            return isInstanceMethod == other.isInstanceMethod &&
+                    installName == other.installName &&
+                    className == other.className &&
+                    methodName == other.methodName;
+        }
+
+        size_t hash() const {
+            std::size_t seed = 0;
+            seed ^= std::hash<std::string_view>()(installName) + 0x9e3779b9 + (seed<<6) + (seed>>2);
+            seed ^= std::hash<std::string_view>()(className) + 0x9e3779b9 + (seed<<6) + (seed>>2);
+            seed ^= std::hash<std::string_view>()(methodName) + 0x9e3779b9 + (seed<<6) + (seed>>2);
+            seed ^= std::hash<bool>()(isInstanceMethod) + 0x9e3779b9 + (seed<<6) + (seed>>2);
+            return seed;
+        }
+    };
+
+    struct MapKeyHasher {
+        size_t operator()(const MapKey& k) const {
+            return k.hash();
+        }
+    };
+
+    std::unordered_map<MapKey, pint_t, MapKeyHasher> impMap;
+    bool relativeMethodListSelectorsAreDirect;
+
+    IMPMapBuilder(bool relativeMethodListSelectorsAreDirect)
+        : relativeMethodListSelectorsAreDirect(relativeMethodListSelectorsAreDirect) { }
+
+    void visitClass(ContentAccessor* cache,
+                    const macho_header<P>* header,
+                    objc_class_t<P>* cls)
+    {
+        objc_method_list_t<P> *methodList = cls->getMethodList(cache);
+        if (methodList == nullptr) return;
+
+        const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)header;
+        bool isInstanceMethod = !cls->isMetaClass(cache);
+        const char* className = cls->getName(cache);
+        const char* installName = ma->installName();
+
+        for (uint32_t n = 0; n < methodList->getCount(); n++) {
+            // do not clobber an existing entry if any, because categories win
+            impMap.try_emplace(MapKey{
+                .installName = installName,
+                .className = className,
+                .methodName = methodList->getStringName(cache, n, relativeMethodListSelectorsAreDirect),
+                .isInstanceMethod = isInstanceMethod
+            }, methodList->getImp(n, cache));
+        }
+    }
+
+    void visit(ContentAccessor* cache, const macho_header<P>* header) {
+        const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)header;
+
+        // Method lists from categories
+        PointerSection<P, objc_category_t<P> *>
+            cats(cache, header, "__DATA", "__objc_catlist");
+        for (pint_t i = 0; i < cats.count(); i++) {
+            objc_category_t<P> *cat = cats.get(i);
+            objc_class_t<P>* cls = cat->getClass(cache);
+            if (cls == nullptr)
+                continue;
+
+            objc_method_list_t<P> *instanceMethods = cat->getInstanceMethods(cache);
+            if (instanceMethods != nullptr) {
+                for (uint32_t n = 0; n < instanceMethods->getCount(); n++) {
+                    MapKey k {
+                        .installName = ma->installName(),
+                        .className = cls->getName(cache),
+                        .methodName = instanceMethods->getStringName(cache, n, relativeMethodListSelectorsAreDirect),
+                        .isInstanceMethod = true
+                    };
+                    //printf("Adding %s %s %s %d cat %s\n", k.installName.data(), k.className.data(), k.methodName.data(), k.isInstanceMethod, k.catName->data());
+                    impMap[k] = instanceMethods->getImp(n, cache);
+                }
+            }
+            objc_method_list_t<P> *classMethods = cat->getClassMethods(cache);
+            if (classMethods != nullptr) {
+                for (uint32_t n = 0; n < classMethods->getCount(); n++) {
+                    MapKey k {
+                        .installName = ma->installName(),
+                        .className = cls->getName(cache),
+                        .methodName = classMethods->getStringName(cache, n, relativeMethodListSelectorsAreDirect),
+                        .isInstanceMethod = false
+                    };
+                    //printf("Adding %s %s %s %d cat %s\n", k.installName.data(), k.className.data(), k.methodName.data(), k.isInstanceMethod, k.catName->data());
+                    impMap[k] = classMethods->getImp(n, cache);
+                }
+            }
+        }
+    }
+};
+
+// List of offsets in libobjc that the shared cache optimization needs to use.
+template <typename T>
+struct objc_opt_imp_caches_pointerlist_tt {
+    T selectorStringVMAddrStart;
+    T selectorStringVMAddrEnd;
+    T inlinedSelectorsVMAddrStart;
+    T inlinedSelectorsVMAddrEnd;
+};
+
+template <typename P>
+class IMPCachesEmitter
+{
+    typedef typename P::uint_t pint_t;
+
+private:
+    Diagnostics&                 diag;
+    const IMPMapBuilder<P>&      impMapBuilder;
+    uint64_t                     selectorStringVMAddr;
+    uint8_t*&                    readOnlyBuffer;
+    size_t&                      readOnlyBufferSize;
+    uint8_t*&                    readWriteBuffer;
+    size_t&                      readWriteBufferSize;
+    CacheBuilder::ASLR_Tracker& aslrTracker;
+
+    std::map<std::string_view, const CacheBuilder::DylibInfo*> _dylibInfos;
+    std::map<std::string_view, const macho_header<P>*> _dylibs;
+    const std::vector<const IMPCaches::Selector*> inlinedSelectors;
+
+    struct ImpCacheHeader {
+        int32_t  fallback_class_offset;
+        uint32_t cache_shift :  5;
+        uint32_t cache_mask  : 11;
+        uint32_t occupied    : 14;
+        uint32_t has_inlines :  1;
+        uint32_t bit_one     :  1;
+    };
+
+    struct ImpCacheEntry {
+        uint32_t selOffset;
+        uint32_t impOffset;
+    };
+
+public:
+
+    static size_t sizeForImpCacheWithCount(int entries) {
+        return sizeof(ImpCacheHeader) + entries * sizeof(ImpCacheEntry);
+    }
+
+    struct ImpCacheContents {
+        struct bucket_t {
+            uint32_t sel_offset = 0;
+            uint64_t imp = 0;
+        };
+        std::vector<bucket_t>   buckets;
+        uint64_t                occupiedBuckets = 0;
+        bool hasInlines = false;
+
+        uint64_t capacity() const
+        {
+            return buckets.size();
+        }
+
+        uint64_t occupied() const {
+            return occupiedBuckets;
+        }
+
+        void incrementOccupied() {
+            ++occupiedBuckets;
+        }
+
+        void insert(uint64_t slot, uint64_t selOffset, uint64_t imp) {
+            bucket_t& b = buckets[slot];
+            assert(b.imp == 0);
+
+            if (!b.imp) incrementOccupied();
+            assert((uint32_t)selOffset == selOffset);
+            b.sel_offset = (uint32_t)selOffset;
+            b.imp = imp;
+        }
+
+        void fillBuckets(const IMPCaches::ClassData* classData, bool metaclass, const IMPMapBuilder<P> & classRecorder) {
+            const std::vector<IMPCaches::ClassData::Method> & methods = classData->methods;
+            buckets.resize(classData->modulo());
+            for (const IMPCaches::ClassData::Method& method : methods) {
+                typename IMPMapBuilder<P>::MapKey k {
+                    .installName = method.installName,
+                    .className = method.className,
+                    .methodName = method.selector->name,
+                    .isInstanceMethod = !metaclass
+                };
+
+                pint_t imp = classRecorder.impMap.at(k);
+                int slot = (method.selector->inProgressBucketIndex >> classData->shift) & classData->mask();
+                insert(slot, method.selector->offset, imp);
+                hasInlines |= (method.wasInlined && !method.fromFlattening);
+            }
+        }
+
+        std::pair<uint64_t, uint64_t>
+        write(ContentAccessor* cache,
+              uint64_t cacheSelectorStringVMAddr, uint64_t clsVMAddr,
+              uint8_t*& buf, size_t& bufSize, Diagnostics& diags) {
+            constexpr bool log = false;
+            uint64_t spaceRequired = sizeof(ImpCacheEntry) * capacity();
+
+            if (spaceRequired > bufSize) {
+                diags.error("Not enough space for imp cache");
+                return { 0, 0 };
+            }
+
+            // Convert from addresses to offsets and write out
+            ImpCacheEntry* offsetBuckets = (ImpCacheEntry*)buf;
+            // printf("Buckets: 0x%08llx\n", cache->vmAddrForContent(offsetBuckets));
+            for (uint64_t index = 0; index != buckets.size(); ++index) {
+                bucket_t bucket = buckets[index];
+                if (bucket.sel_offset == 0 && bucket.imp == 0) {
+                    // Empty bucket
+                    offsetBuckets[index].selOffset = 0xFFFFFFFF;
+                    offsetBuckets[index].impOffset = 0;
+                } else {
+                    int64_t selOffset = (int64_t)bucket.sel_offset;
+                    int64_t impOffset = clsVMAddr - bucket.imp;
+                    assert((int32_t)impOffset == impOffset);
+                    assert((int32_t)selOffset == selOffset);
+                    offsetBuckets[index].selOffset = (int32_t)selOffset;
+                    offsetBuckets[index].impOffset = (int32_t)impOffset;
+                    if (log) {
+                        diags.verbose("[IMP Caches] Coder[%lld]: %#08llx (sel: %#08x, imp %#08x) %s\n", index,
+                           cache->vmAddrForOnDiskVMAddr(bucket.imp),
+                           (int32_t)selOffset, (int32_t)impOffset,
+                           (const char*)cache->contentForVMAddr(cacheSelectorStringVMAddr + bucket.sel_offset));
+                    }
+                }
+            }
+
+            buf += spaceRequired;
+            bufSize -= spaceRequired;
+
+            return { cache->vmAddrForContent(offsetBuckets), (uint64_t)buckets.size() };
+        }
+    };
+
+    IMPCachesEmitter(Diagnostics& diags, const IMPMapBuilder<P>& builder, uint64_t selectorStringVMAddr, uint8_t*& roBuf, size_t& roBufSize, uint8_t* &rwBuf, size_t& rwBufSize, const std::vector<CacheBuilder::DylibInfo> & dylibInfos, const std::vector<const macho_header<P>*> & dylibs, CacheBuilder::ASLR_Tracker& tracker)
+        : diag(diags), impMapBuilder(builder), selectorStringVMAddr(selectorStringVMAddr), readOnlyBuffer(roBuf), readOnlyBufferSize(roBufSize), readWriteBuffer(rwBuf), readWriteBufferSize(rwBufSize), aslrTracker(tracker) {
+            for (const CacheBuilder::DylibInfo& d : dylibInfos) {
+                _dylibInfos[d.dylibID] = &d;
+            }
+            for (const macho_header<P>* d : dylibs) {
+                const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*) d;
+                _dylibs[ma->installName()] = d;
+            }
+        }
+
+    // Returns true if we should filter this class out from getting an imp cache
+    bool filter(ContentAccessor* cache, const dyld3::MachOAnalyzer* ma, const objc_class_t<P>* cls) {
+        const CacheBuilder::DylibInfo* d = _dylibInfos[ma->installName()];
+        IMPCaches::ClassKey key {
+            .name = cls->getName(cache),
+            .metaclass = cls->isMetaClass(cache)
+        };
+        return (d->impCachesClassData.find(key) == d->impCachesClassData.end());
+    }
+
+    void visitClass(ContentAccessor* cache,
+                    const macho_header<P>* header,
+                    objc_class_t<P>* cls)
+    {
+        // If we ran out of space then don't try to optimize more
+        if (diag.hasError())
+            return;
+
+        const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*) header;
+        if (filter(cache, ma, cls)) {
+            *cls->getVTableAddress() = 0;
+            return;
+        }
+
+        const char* className = cls->getName(cache);
+
+        if (cls->getVTable(cache) != 0) {
+            diag.error("Class '%s' has non-zero vtable\n", className);
+            return;
+        }
+
+        const CacheBuilder::DylibInfo* d = _dylibInfos[ma->installName()];
+        IMPCaches::ClassKey key {
+            .name = cls->getName(cache),
+            .metaclass = cls->isMetaClass(cache)
+        };
+        IMPCaches::ClassData* data = (d->impCachesClassData.at(key)).get();
+#if 0
+        for (const objc_method_t<P>& method : methods) {
+            printf("  0x%llx: 0x%llx (%s)\n", method.getImp(), method.getName(),
+                   (const char*)cache->contentForVMAddr(method.getName()));
+        }
+#endif
+
+        uint64_t clsVMAddr = cache->vmAddrForContent(cls);
+
+        if (data->mask() > 0x7ff) {
+            diag.verbose("Cache for class %s (%#08llx) is too large (mask: %#x)\n",
+                         className, clsVMAddr, data->mask());
+            return;
+        }
+
+        ImpCacheContents impCache;
+        impCache.fillBuckets(data, cls->isMetaClass(cache), impMapBuilder);
+
+        constexpr bool log = false;
+        if (log) {
+            printf("Writing cache for %sclass %s (%#08llx)\n", cls->isMetaClass(cache) ? "meta" : "", className, clsVMAddr);
+        }
+
+        struct ImpCacheHeader {
+            int32_t  fallback_class_offset;
+            uint32_t cache_shift :  5;
+            uint32_t cache_mask  : 11;
+            uint32_t occupied    : 14;
+            uint32_t has_inlines :  1;
+            uint32_t bit_one     :  1;
+        };
+        pint_t* vtableAddr = cls->getVTableAddress();
+
+        // the alignment of ImpCaches to 16 bytes is only needed for arm64_32.
+        ImpCacheHeader* cachePtr = (ImpCacheHeader*)align_buffer(readOnlyBuffer, sizeof(pint_t) == 4 ? 4 : 3);
+
+        assert(readOnlyBufferSize > sizeof(ImpCacheHeader));
+
+        uint64_t occupied = impCache.occupied();
+        int64_t fallback_class_offset = *(cls->getSuperClassAddress()) - clsVMAddr;
+
+        if (data->flatteningRootSuperclass) {
+            // If we are a class being flattened (inheriting all the selectors of
+            // its superclasses up to and including the flattening root), the fallback class
+            // should be the first superclass which is not flattened.
+            
+            // Find the VMAddr of that superclass, given its segment index and offset
+            // in the source dylib.
+            const auto & superclass = *(data->flatteningRootSuperclass);
+            const macho_header<P> * d = _dylibs[superclass.installName];
+            __block uint64_t superclassVMAddr = 0;
+            const dyld3::MachOAnalyzer *ma = (const dyld3::MachOAnalyzer *)d;
+            ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
+                if (info.segIndex == superclass.segmentIndex) {
+                    superclassVMAddr = info.vmAddr + superclass.segmentOffset;
+                    stop = true;
+                }
+            });
+
+            assert(superclassVMAddr > 0);
+            fallback_class_offset = superclassVMAddr - clsVMAddr;
+        }
+
+        assert((int32_t)fallback_class_offset == fallback_class_offset);
+        assert((uint32_t)occupied == occupied);
+
+        *cachePtr = (ImpCacheHeader){
+            .fallback_class_offset = (int32_t)fallback_class_offset,
+            .cache_shift = (uint32_t)(data->shift + 7),
+            .cache_mask = (uint32_t)data->mask(),
+            .occupied = (uint32_t)occupied,
+            .has_inlines = impCache.hasInlines,
+            .bit_one = 1, // obj-c plays HORRENDOUS games here
+        };
+
+        // is this right?
+        int64_t vmaddr = cache->vmAddrForContent(readOnlyBuffer);
+        assert((pint_t)vmaddr == (uint64_t)vmaddr);
+        *vtableAddr =  (pint_t)cache->vmAddrForContent(readOnlyBuffer);
+        aslrTracker.add(vtableAddr);
+        readOnlyBuffer += sizeof(ImpCacheHeader);
+        readOnlyBufferSize -= sizeof(ImpCacheHeader);
+
+        impCache.write(cache, selectorStringVMAddr, clsVMAddr, readOnlyBuffer, readOnlyBufferSize, diag);
+    }
+
+    void emitInlinedSelectors(const std::vector<const IMPCaches::Selector*> selectors) {
+        // FIXME: this should be in constant memory
+        for (const IMPCaches::Selector* s : selectors) {
+            assert(readWriteBufferSize >= sizeof(pint_t));
+            *(pint_t*)readWriteBuffer = (pint_t)(selectorStringVMAddr + s->offset);
+            aslrTracker.add(readWriteBuffer);
+            readWriteBuffer += sizeof(pint_t);
+            readWriteBufferSize -= sizeof(pint_t);
+        }
+    }
+};
+
 template <typename P>
 class ProtocolOptimizer
 {
@@ -370,7 +769,7 @@ public:
         for (pint_t i = 0; i < protocols.count(); i++) {
             objc_protocol_t<P> *proto = protocols.get(i);
 
-            const char *name = proto->getName(cache);
+            const charname = proto->getName(cache);
             if (_protocolNames.count(name) == 0) {
                 if (proto->getSize() > sizeof(objc_protocol_t<P>)) {
                     _diagnostics.error("objc protocol is too big");
@@ -394,7 +793,8 @@ public:
                                uint8_t *& rwdest, size_t& rwremaining,
                                uint8_t *& rodest, size_t& roremaining, 
                                CacheBuilder::ASLR_Tracker& aslrTracker,
-                               pint_t protocolClassVMAddr)
+                               pint_t protocolClassVMAddr,
+                               const dyld3::MachOAnalyzerSet::PointerMetaData& PMD)
     {
         if (_protocolCount == 0) return NULL;
 
@@ -423,6 +823,12 @@ public:
             if (!proto->getIsaVMAddr()) {
                 proto->setIsaVMAddr(protocolClassVMAddr);
             }
+
+            // If the objc runtime signed the Protocol ISA, then we need to too
+            if ( PMD.authenticated ) {
+                aslrTracker.setAuthData(proto->getISALocation(), PMD.diversity, PMD.usesAddrDiversity, PMD.key);
+            }
+
             if (oldSize < sizeof(*proto)) {
                 // Protocol object is old. Populate new fields.
                 proto->setSize(sizeof(objc_protocol_t<P>));
@@ -571,6 +977,43 @@ void addObjcSegments(Diagnostics& diag, DyldSharedCache* cache, const mach_heade
     }
 }
 
+template <typename P> static inline void emitIMPCaches(ContentAccessor& cacheAccessor,
+                                         std::vector<CacheBuilder::DylibInfo> & allDylibs,
+                                         std::vector<const macho_header<P>*> & sizeSortedDylibs,
+                                         bool relativeMethodListSelectorsAreDirect,
+                                         uint64_t selectorStringVMAddr,
+                                         uint8_t* optROData, size_t& optRORemaining,
+                                         uint8_t* optRWData, size_t& optRWRemaining,
+                                         CacheBuilder::ASLR_Tracker& aslrTracker,
+                                         const std::vector<const IMPCaches::Selector*> & inlinedSelectors,
+                                         uint8_t* &inlinedSelectorsStart,
+                                         uint8_t* &inlinedSelectorsEnd,
+                                         Diagnostics& diag,
+                                         TimeRecorder& timeRecorder) {
+    diag.verbose("[IMP caches] computing IMP map\n");
+
+    IMPMapBuilder<P> classRecorder(relativeMethodListSelectorsAreDirect);
+    for (const macho_header<P>* mh : sizeSortedDylibs) {
+        ClassWalker<P, IMPMapBuilder<P>> classWalker(classRecorder, ClassWalkerMode::ClassAndMetaclasses);
+        classWalker.walk(&cacheAccessor, mh);
+        classRecorder.visit(&cacheAccessor, mh);
+    }
+
+    timeRecorder.recordTime("compute IMP map");
+    diag.verbose("[IMP caches] emitting IMP caches\n");
+
+    IMPCachesEmitter<P> impCachesEmitter(diag, classRecorder, selectorStringVMAddr, optROData, optRORemaining, optRWData, optRWRemaining, allDylibs, sizeSortedDylibs, aslrTracker);
+    ClassWalker<P, IMPCachesEmitter<P>> impEmitterClassWalker(impCachesEmitter, ClassWalkerMode::ClassAndMetaclasses);
+    for (const macho_header<P>* mh : sizeSortedDylibs) {
+        impEmitterClassWalker.walk(&cacheAccessor, mh);
+        if (diag.hasError())
+            return;
+    }
+
+    inlinedSelectorsStart = optRWData;
+    impCachesEmitter.emitInlinedSelectors(inlinedSelectors);
+    inlinedSelectorsEnd = optRWData;
+}
 
 template <typename P>
 void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::ASLR_Tracker& aslrTracker,
@@ -578,7 +1021,11 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS
                     const std::map<void*, std::string>& missingWeakImports, Diagnostics& diag,
                     uint8_t* objcReadOnlyBuffer, uint64_t objcReadOnlyBufferSizeUsed, uint64_t objcReadOnlyBufferSizeAllocated,
                     uint8_t* objcReadWriteBuffer, uint64_t objcReadWriteBufferSizeAllocated,
-                    uint32_t objcRwFileOffset)
+                    uint32_t objcRwFileOffset,
+                    std::vector<CacheBuilder::DylibInfo> & allDylibs,
+                    const std::vector<const IMPCaches::Selector*> & inlinedSelectors,
+                    bool impCachesSuccess,
+                    TimeRecorder& timeRecorder)
 {
     typedef typename P::E           E;
     typedef typename P::uint_t      pint_t;
@@ -599,6 +1046,7 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS
     __block const mach_header*      libobjcMH = nullptr;
     __block const macho_section<P> *optROSection = nullptr;
     __block const macho_section<P> *optPointerListSection = nullptr;
+    __block const macho_section<P> *optImpCachesPointerSection = nullptr;
     __block std::vector<const macho_header<P>*> objcDylibs;
     cache->forEachImage(^(const mach_header* machHeader, const char* installName) {
         const macho_header<P>* mh = (const macho_header<P>*)machHeader;
@@ -606,6 +1054,9 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS
             libobjcMH = (mach_header*)mh;
             optROSection = mh->getSection("__TEXT", "__objc_opt_ro");
             optPointerListSection = mh->getSection("__DATA", "__objc_opt_ptrs");
+            if ( optPointerListSection == nullptr )
+                optPointerListSection = mh->getSection("__AUTH", "__objc_opt_ptrs");
+            optImpCachesPointerSection = mh->getSection("__DATA_CONST", "__objc_scoffs");
         }
         if ( mh->getSection("__DATA", "__objc_imageinfo") || mh->getSection("__OBJC", "__image_info") ) {
             objcDylibs.push_back(mh);
@@ -620,6 +1071,9 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS
         diag.warning("libobjc's pointer list section missing (metadata not optimized)");
         return;
     }
+    if ( optImpCachesPointerSection == nullptr ) {
+        diag.warning("libobjc's magical shared cache offsets list section missing (metadata not optimized)");
+    }
     // point optROData into space allocated in dyld cache
     uint8_t* optROData = objcReadOnlyBuffer + objcReadOnlyBufferSizeUsed;
     size_t optRORemaining = objcReadOnlyBufferSizeAllocated - objcReadOnlyBufferSizeUsed;
@@ -750,7 +1204,11 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS
         return (uint8_t*)(((uintptr_t)ptr + 0x7) & ~0x7);
     };
 
-    SelectorOptimizer<P, ObjCSelectorUniquer<P> > selOptimizer(uniq);
+    // Relative method lists names are initially an offset to a selector reference.
+    // Eventually we'll update them to offsets directly to the selector string.
+    bool relativeMethodListSelectorsAreDirect = false;
+
+    SelectorOptimizer<P, ObjCSelectorUniquer<P> > selOptimizer(uniq, relativeMethodListSelectorsAreDirect);
     selOptimizer.visitCoalescedStrings(coalescedText);
     for (const macho_header<P>* mh : sizeSortedDylibs) {
         LegacySelectorUpdater<P, ObjCSelectorUniquer<P>>::update(&cacheAccessor, mh, uniq);
@@ -848,7 +1306,7 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS
     //
     // This is SAFE: modified binaries are still usable as unsorted lists.
     // This must be done AFTER uniquing selectors.
-    MethodListSorter<P> methodSorter;
+    MethodListSorter<P> methodSorter(relativeMethodListSelectorsAreDirect);
     for (const macho_header<P>* mh : sizeSortedDylibs) {
         methodSorter.optimize(&cacheAccessor, mh);
     }
@@ -870,10 +1328,26 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS
                protocolOptimizer.protocolCount());
 
     pint_t protocolClassVMAddr = (pint_t)P::getP(optPointerList->protocolClass);
+
+    // Get the pointer metadata from the magic protocolClassVMAddr symbol
+    // We'll transfer it over to the ISA on all the objc protocols when we set their ISAs
+    dyld3::MachOAnalyzerSet::PointerMetaData protocolClassPMD;
+    uint16_t    protocolClassAuthDiversity  = 0;
+    bool        protocolClassAuthIsAddr     = false;
+    uint8_t     protocolClassAuthKey        = 0;
+    if ( aslrTracker.hasAuthData((void*)&optPointerList->protocolClass, &protocolClassAuthDiversity, &protocolClassAuthIsAddr, &protocolClassAuthKey) ) {
+        protocolClassPMD.diversity           = protocolClassAuthDiversity;
+        protocolClassPMD.high8               = 0;
+        protocolClassPMD.authenticated       = 1;
+        protocolClassPMD.key                 = protocolClassAuthKey;
+        protocolClassPMD.usesAddrDiversity   = protocolClassAuthIsAddr;
+    }
+
     err = protocolOptimizer.writeProtocols(&cacheAccessor,
                                            optRWData, optRWRemaining,
                                            optROData, optRORemaining,
-                                           aslrTracker, protocolClassVMAddr);
+                                           aslrTracker, protocolClassVMAddr,
+                                           protocolClassPMD);
     if (err) {
         diag.warning("%s", err);
         return;
@@ -925,6 +1399,56 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS
     
     diag.verbose("  updated  % 6ld ivar offsets\n", ivarOffsetOptimizer.optimized());
 
+    //
+    // Build imp caches
+    //
+    // Objc has a magic section of imp cache base pointers.  We need these to
+    // offset everything else from
+    const CacheBuilder::CacheCoalescedText::StringSection& methodNames = coalescedText.getSectionData("__objc_methname");
+    uint64_t selectorStringVMAddr = methodNames.bufferVMAddr;
+    uint64_t selectorStringVMSize = methodNames.bufferSize;
+    uint64_t impCachesVMSize = 0; // We'll calculate this later
+
+    uint64_t optRODataRemainingBeforeImpCaches = optRORemaining;
+
+    timeRecorder.pushTimedSection();
+    
+    uint8_t* inlinedSelectorsStart = optRWData;
+    uint8_t* inlinedSelectorsEnd = optRWData;
+    
+    if (impCachesSuccess) {
+        emitIMPCaches<P>(cacheAccessor, allDylibs, sizeSortedDylibs, relativeMethodListSelectorsAreDirect,
+                         selectorStringVMAddr, optROData, optRORemaining, optRWData, optRWRemaining,
+                         aslrTracker, inlinedSelectors, inlinedSelectorsStart, inlinedSelectorsEnd, diag, timeRecorder);
+    }
+
+    uint8_t* alignedROData = alignPointer(optROData);
+    optRORemaining -= (alignedROData - optROData);
+    optROData = alignedROData;
+
+    impCachesVMSize = optRODataRemainingBeforeImpCaches - optRORemaining;
+    timeRecorder.recordTime("emit IMP caches");
+    timeRecorder.popTimedSection();
+
+    diag.verbose("[IMP Caches] Imp caches size: %'lld bytes\n\n", impCachesVMSize);
+
+    // Update the pointers in the pointer list section
+    if (optImpCachesPointerSection) {
+        if (optImpCachesPointerSection->size() < sizeof(objc_opt::objc_opt_pointerlist_tt<pint_t>)) {
+            diag.warning("libobjc's pointer list section is too small (metadata not optimized)");
+            return;
+        }
+        auto *impCachePointers = (objc_opt_imp_caches_pointerlist_tt<pint_t> *)cacheAccessor.contentForVMAddr(optImpCachesPointerSection->addr());
+        impCachePointers->selectorStringVMAddrStart = (pint_t)selectorStringVMAddr;
+        impCachePointers->selectorStringVMAddrEnd   = (pint_t)(selectorStringVMAddr + selectorStringVMSize);
+        impCachePointers->inlinedSelectorsVMAddrStart = (pint_t)cacheAccessor.vmAddrForContent(inlinedSelectorsStart);
+        impCachePointers->inlinedSelectorsVMAddrEnd = (pint_t)cacheAccessor.vmAddrForContent(inlinedSelectorsEnd);
+
+        aslrTracker.add(&impCachePointers->selectorStringVMAddrStart);
+        aslrTracker.add(&impCachePointers->selectorStringVMAddrEnd);
+        aslrTracker.add(&impCachePointers->inlinedSelectorsVMAddrStart);
+        aslrTracker.add(&impCachePointers->inlinedSelectorsVMAddrEnd);        
+    }
 
     // Collect flags.
     uint32_t headerFlags = 0;
@@ -973,7 +1497,7 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS
 
 
     // Now that objc has uniqued the selector references, we can apply the LOHs so that ADRP/LDR -> ADRP/ADD
-    if (forProduction) {
+    {
         const bool logSelectors = false;
         uint64_t lohADRPCount = 0;
         uint64_t lohLDRCount = 0;
@@ -1085,19 +1609,45 @@ void doOptimizeObjC(DyldSharedCache* cache, bool forProduction, CacheBuilder::AS
 
 } // anon namespace
 
-void SharedCacheBuilder::optimizeObjC()
+size_t IMPCaches::sizeForImpCacheWithCount(int count) {
+    // The architecture should not be relevant here as it's all offsets and fixed int sizes.
+    // It was just the most logical place to host this function in.
+
+    size_t size64 = IMPCachesEmitter<Pointer64<LittleEndian>>::sizeForImpCacheWithCount(count);
+    size_t size32 = IMPCachesEmitter<Pointer32<LittleEndian>>::sizeForImpCacheWithCount(count);
+    assert(size64 == size32);
+
+    return size64;
+}
+
+void SharedCacheBuilder::optimizeObjC(bool impCachesSuccess, const std::vector<const IMPCaches::Selector*> & inlinedSelectors)
 {
-    uint32_t objcRwFileOffset = (uint32_t)((_objcReadWriteBuffer - _readWriteRegion.buffer) + _readWriteRegion.cacheFileOffset);
+    // FIXME: Can we move the objc RW content to the __DATA_CONST region?
+    // For now, it is always at the end of the last region
+    const Region* readWriteRegion = lastDataRegion();
+    uint32_t objcRwFileOffset = (uint32_t)((_objcReadWriteBuffer - readWriteRegion->buffer) + readWriteRegion->cacheFileOffset);
     if ( _archLayout->is64 )
-        doOptimizeObjC<Pointer64<LittleEndian>>((DyldSharedCache*)_readExecuteRegion.buffer, _options.optimizeStubs, _aslrTracker, _lohTracker,
-                                                _coalescedText, _missingWeakImports,
-                                                _diagnostics, _objcReadOnlyBuffer, _objcReadOnlyBufferSizeUsed, _objcReadOnlyBufferSizeAllocated,
-                                                _objcReadWriteBuffer, _objcReadWriteBufferSizeAllocated, objcRwFileOffset);
+        doOptimizeObjC<Pointer64<LittleEndian>>((DyldSharedCache*)_readExecuteRegion.buffer,
+            _options.optimizeStubs,
+            _aslrTracker, _lohTracker,
+            _coalescedText,
+            _missingWeakImports, _diagnostics,
+            _objcReadOnlyBuffer,
+            _objcReadOnlyBufferSizeUsed,
+            _objcReadOnlyBufferSizeAllocated,
+            _objcReadWriteBuffer, _objcReadWriteBufferSizeAllocated,
+            objcRwFileOffset, _sortedDylibs, inlinedSelectors, impCachesSuccess, _timeRecorder);
     else
-        doOptimizeObjC<Pointer32<LittleEndian>>((DyldSharedCache*)_readExecuteRegion.buffer, _options.optimizeStubs, _aslrTracker, _lohTracker,
-                                                _coalescedText, _missingWeakImports,
-                                                _diagnostics, _objcReadOnlyBuffer, _objcReadOnlyBufferSizeUsed, _objcReadOnlyBufferSizeAllocated,
-                                                _objcReadWriteBuffer, _objcReadWriteBufferSizeAllocated, objcRwFileOffset);
+        doOptimizeObjC<Pointer32<LittleEndian>>((DyldSharedCache*)_readExecuteRegion.buffer,
+            _options.optimizeStubs,
+            _aslrTracker, _lohTracker,
+            _coalescedText,
+            _missingWeakImports, _diagnostics,
+            _objcReadOnlyBuffer,
+            _objcReadOnlyBufferSizeUsed,
+            _objcReadOnlyBufferSizeAllocated,
+            _objcReadWriteBuffer, _objcReadWriteBufferSizeAllocated,
+            objcRwFileOffset, _sortedDylibs, inlinedSelectors, impCachesSuccess, _timeRecorder);
 }
 
 static uint32_t hashTableSize(uint32_t maxElements, uint32_t perElementData)
@@ -1118,5 +1668,8 @@ uint32_t SharedCacheBuilder::computeReadOnlyObjC(uint32_t selRefCount, uint32_t
 // Space to replace the __objc_opt_rw section.
 uint32_t SharedCacheBuilder::computeReadWriteObjC(uint32_t imageCount, uint32_t protocolDefCount)
 {
-    return 8*imageCount + protocolDefCount*12*(_archLayout->is64 ? 8 : 4);
+    uint8_t pointerSize = _archLayout->is64 ? 8 : 4;
+    return 8*imageCount
+         + protocolDefCount*12*pointerSize
+         + (int)_impCachesBuilder->inlinedSelectors.size() * pointerSize;
 }
index b1523d15aa8f02557d2f312c52e436b3af440d85..572c7b232dc6fddb612f926ed842976c1bcb2984 100644 (file)
 #include <mach/mach_time.h>
 #include <mach/shared_region.h>
 #include <apfs/apfs_fsctl.h>
+#include <iostream>
 
 #include <CommonCrypto/CommonHMAC.h>
 #include <CommonCrypto/CommonDigest.h>
 #include <CommonCrypto/CommonDigestSPI.h>
 
+#include "mach-o/dyld_priv.h"
 #include "ClosureBuilder.h"
 #include "Closure.h"
 #include "ClosureFileSystemNull.h"
 #include "CodeSigningTypes.h"
 #include "MachOFileAbstraction.hpp"
 #include "SharedCacheBuilder.h"
+#include "RootsChecker.h"
+#include "IMPCachesBuilder.hpp"
 
 #include "FileUtils.h"
 #include "StringUtils.h"
     #define ARM64_SHARED_REGION_SIZE     0x100000000ULL
 #endif
 
+#if ARM64_SHARED_REGION_START == 0x7FFF00000000
+    #define ARM64_DELTA_MASK 0x00FF000000000000
+#else
+    #define ARM64_DELTA_MASK 0x00FFFF0000000000
+#endif
+
 #ifndef ARM64_32_SHARED_REGION_START
     #define ARM64_32_SHARED_REGION_START 0x1A000000ULL
     #define ARM64_32_SHARED_REGION_SIZE  0x26000000ULL
 #endif
 
-#if ARM_SHARED_REGION_SIZE > 0x26000000ULL
-  #define  ARMV7K_CHAIN_BITS    0xC0000000
-  #define  ARMV7K_MAX           0x0
+#define  ARMV7K_CHAIN_BITS    0xC0000000
+
+#if BUILDING_UPDATE_DYLD_CACHE_BUILDER
+    #define DISCONTIGUOUS_RX   0x7FFF20000000ULL
 #else
-  #define  ARMV7K_CHAIN_BITS    0xE0000000
-  #define  ARMV7K_MAX           0x20000000
+    #define DISCONTIGUOUS_RX   0x7FFF20000000ULL    // size for MRM builder
 #endif
+#define DISCONTIGUOUS_RW   0x7FFF80000000ULL
+#define DISCONTIGUOUS_RO   0x7FFFC0000000ULL
+#define DISCONTIGUOUS_RX_SIZE (DISCONTIGUOUS_RW - DISCONTIGUOUS_RX)
+#define DISCONTIGUOUS_RW_SIZE 0x40000000
+#define DISCONTIGUOUS_RO_SIZE 0x3FE00000
 
 const SharedCacheBuilder::ArchLayout SharedCacheBuilder::_s_archLayout[] = {
-    { 0x7FFF20000000ULL,            0xEFE00000ULL,              0x0,         0x40000000, 0x00FFFF0000000000, "x86_64",   CS_PAGE_SIZE_4K,  12, 2, true,  true,  true  },
-    { 0x7FFF20000000ULL,            0xEFE00000ULL,              0x0,         0x40000000, 0x00FFFF0000000000, "x86_64h",  CS_PAGE_SIZE_4K,  12, 2, true,  true,  true  },
-    { SHARED_REGION_BASE_I386,      SHARED_REGION_SIZE_I386,    0x0,         0x00200000,                0x0, "i386",     CS_PAGE_SIZE_4K,  12, 0, false, false, true  },
-    { ARM64_SHARED_REGION_START,    ARM64_SHARED_REGION_SIZE,   0x0,         0x02000000, 0x00FFFF0000000000, "arm64",    CS_PAGE_SIZE_4K,  14, 2, false, true,  false },
+    { DISCONTIGUOUS_RX,             0xEFE00000ULL,               0x40000000, 0x00FFFF0000000000, "x86_64",   CS_PAGE_SIZE_4K,  14, 2, true,  true,  true  },
+    { DISCONTIGUOUS_RX,             0xEFE00000ULL,               0x40000000, 0x00FFFF0000000000, "x86_64h",  CS_PAGE_SIZE_4K,  14, 2, true,  true,  true  },
+    { SHARED_REGION_BASE_I386,      SHARED_REGION_SIZE_I386,     0x00200000,                0x0, "i386",     CS_PAGE_SIZE_4K,  12, 0, false, false, true  },
+    { ARM64_SHARED_REGION_START,    ARM64_SHARED_REGION_SIZE,    0x02000000,   ARM64_DELTA_MASK, "arm64",    CS_PAGE_SIZE_4K,  14, 2, false, true,  false },
 #if SUPPORT_ARCH_arm64e
-    { ARM64_SHARED_REGION_START,    ARM64_SHARED_REGION_SIZE,   0x0,         0x02000000, 0x00FFFF0000000000, "arm64e",   CS_PAGE_SIZE_16K, 14, 2, false, true,  false },
+    { ARM64_SHARED_REGION_START,    ARM64_SHARED_REGION_SIZE,    0x02000000,   ARM64_DELTA_MASK, "arm64e",   CS_PAGE_SIZE_16K, 14, 2, false, true,  false },
 #endif
 #if SUPPORT_ARCH_arm64_32
-    { ARM64_32_SHARED_REGION_START, ARM64_32_SHARED_REGION_SIZE,0x0,         0x02000000,         0xC0000000, "arm64_32", CS_PAGE_SIZE_16K, 14, 6, false, false, true  },
+    { ARM64_32_SHARED_REGION_START, ARM64_32_SHARED_REGION_SIZE, 0x02000000,         0xC0000000, "arm64_32", CS_PAGE_SIZE_16K, 14, 6, false, false, true  },
 #endif
-    { ARM_SHARED_REGION_START,      ARM_SHARED_REGION_SIZE,     0x0,         0x02000000,         0xE0000000, "armv7s",   CS_PAGE_SIZE_4K,  14, 4, false, false, true  },
-    { ARM_SHARED_REGION_START,      ARM_SHARED_REGION_SIZE,     ARMV7K_MAX,  0x00400000,  ARMV7K_CHAIN_BITS, "armv7k",   CS_PAGE_SIZE_4K,  14, 4, false, false, true  },
-    { 0x40000000,                   0x40000000,                 0x0,         0x02000000,                0x0, "sim-x86",  CS_PAGE_SIZE_4K,  14, 0, false, false, true  }
-};
-
-
-// These are dylibs that may be interposed, so stubs calling into them should never be bypassed
-const char* const SharedCacheBuilder::_s_neverStubEliminateDylibs[] = {
-    "/usr/lib/system/libdispatch.dylib",
-    nullptr
+    { ARM_SHARED_REGION_START,      ARM_SHARED_REGION_SIZE,      0x02000000,         0xE0000000, "armv7s",   CS_PAGE_SIZE_4K,  14, 4, false, false, true  },
+    { ARM_SHARED_REGION_START,      ARM_SHARED_REGION_SIZE,      0x00400000,  ARMV7K_CHAIN_BITS, "armv7k",   CS_PAGE_SIZE_4K,  14, 4, false, false, true  },
+    { 0x40000000,                   0x40000000,                  0x02000000,                0x0, "sim-x86",  CS_PAGE_SIZE_4K,  14, 0, false, false, true  }
 };
 
 // These are functions that are interposed by Instruments.app or ASan
@@ -207,10 +215,12 @@ const char* const SharedCacheBuilder::_s_neverStubEliminateSymbols[] = {
     "_malloc_create_zone",
     "_malloc_default_purgeable_zone",
     "_malloc_default_zone",
+    "_malloc_destroy_zone",
     "_malloc_good_size",
     "_malloc_make_nonpurgeable",
     "_malloc_make_purgeable",
     "_malloc_set_zone_name",
+    "_malloc_zone_from_ptr",
     "_mbsnrtowcs",
     "_mbsrtowcs",
     "_mbstowcs",
@@ -381,6 +391,11 @@ public:
         for (CacheBuilder::InputFile& inputFile : inputFiles) {
             char realerPath[MAXPATHLEN];
             dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(inputFile.diag, fileSystem, inputFile.path, reqArchs, reqPlatform, realerPath);
+            if ( (reqPlatform == dyld3::Platform::macOS) && inputFile.diag.hasError() ) {
+                // Try again with iOSMac
+                inputFile.diag.clearError();
+                loadedFileInfo = dyld3::MachOAnalyzer::load(inputFile.diag, fileSystem, inputFile.path, reqArchs, dyld3::Platform::iOSMac, realerPath);
+            }
             const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
             if (ma == nullptr) {
                 couldNotLoadFiles.emplace_back((CacheBuilder::LoadedMachO){ DyldSharedCache::MappedMachO(), loadedFileInfo, &inputFile });
@@ -394,18 +409,29 @@ public:
             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;
+                const char* dylibPath = inputFile.path;
+                if ( (installName != inputFile.path) && (reqPlatform == dyld3::Platform::macOS) ) {
+                    // We now typically require that install names and paths match.  However symlinks may allow us to bring in a path which
+                    // doesn't match its install name.
+                    // For example:
+                    //   /usr/lib/libstdc++.6.0.9.dylib is a real file with install name /usr/lib/libstdc++.6.dylib
+                    //   /usr/lib/libstdc++.6.dylib is a symlink to /usr/lib/libstdc++.6.0.9.dylib
+                    // So long as we add both paths (with one as an alias) then this will work, even if dylibs are removed from disk
+                    // but the symlink remains.
+                    char resolvedSymlinkPath[PATH_MAX];
+                    if ( fileSystem.getRealPath(installName.c_str(), resolvedSymlinkPath) ) {
+                        if (!strcmp(resolvedSymlinkPath, inputFile.path)) {
+                            // Symlink is the install name and points to the on-disk dylib
+                            //fprintf(stderr, "Symlink works: %s == %s\n", inputFile.path, installName.c_str());
+                            dylibPath = installName.c_str();
+                        }
+                    }
                 }
 
-                if (!ma->canBePlacedInDyldCache(inputFile.path, ^(const char* msg) {
+                if (!ma->canBePlacedInDyldCache(dylibPath, ^(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.
+
                     if (!ma->canHavePrecomputedDlopenClosure(inputFile.path, ^(const char* msg) {
                         inputFile.diag.verbose("Dylib located at '%s' cannot prebuild dlopen closure in cache because: %s", inputFile.path, msg);
                     }) ) {
@@ -441,7 +467,7 @@ public:
                     }
                 }
             } else if (ma->isBundle()) {
-                // TODO: Add exclusion lists here?
+
                 if (!ma->canHavePrecomputedDlopenClosure(inputFile.path, ^(const char* msg) {
                     inputFile.diag.verbose("Dylib located at '%s' cannot prebuild dlopen closure in cache because: %s", inputFile.path, msg);
                 }) ) {
@@ -450,7 +476,9 @@ public:
                 }
                 otherDylibs.emplace_back((CacheBuilder::LoadedMachO){ mappedFile, loadedFileInfo, &inputFile });
             } else if (ma->isDynamicExecutable()) {
-                if (platformExcludesExecutablePath_macOS(inputFile.path)) {
+
+                // Let the platform exclude the file before we do anything else.
+                if (platformExcludesExecutablePath(inputFile.path)) {
                     inputFile.diag.verbose("Platform excluded file\n");
                     fileSystem.unloadFile(loadedFileInfo);
                     continue;
@@ -465,89 +493,31 @@ public:
 
 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;
-            case dyld3::Platform::driverKit:
-                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") {
+        // We no longer support ROSP, so skip all paths which start with the special prefix
+        if ( startsWith(path, "/System/Library/Templates/Data/") )
             return true;
-        }
-        return false;
-    }
 
-    static bool platformExcludesExecutablePath_tvOS(const std::string& path) {
-        return platformExcludesExecutablePath_iOS(path);
-    }
+        static const char* sAllowedPrefixes[] = {
+            "/bin/",
+            "/sbin/",
+            "/usr/",
+            "/System/",
+            "/Library/Apple/System/",
+            "/Library/Apple/usr/",
+            "/System/Applications/Safari.app/",
+            "/Library/CoreMediaIO/Plug-Ins/DAL/"                // temp until plugins moved or closured working
+        };
 
-    static bool platformExcludesExecutablePath_watchOS(const std::string& path) {
-        return platformExcludesExecutablePath_iOS(path);
-    }
+        bool inSearchDir = false;
+        for (const char* searchDir : sAllowedPrefixes ) {
+            if ( strncmp(searchDir, path.c_str(), strlen(searchDir)) == 0 )  {
+                inSearchDir = true;
+                break;
+            }
+        }
 
-    static bool platformExcludesExecutablePath_bridgeOS(const std::string& path) {
-        return platformExcludesExecutablePath_iOS(path);
+        return !inSearchDir;
     }
 
     // Returns true if the current platform requires that this path be excluded from the shared cache
@@ -559,15 +529,15 @@ private:
             case dyld3::Platform::macOS:
                 return platformExcludesExecutablePath_macOS(path);
             case dyld3::Platform::iOS:
-                return platformExcludesExecutablePath_iOS(path);
+                return false;
             case dyld3::Platform::tvOS:
-                return platformExcludesExecutablePath_tvOS(path);
+                return false;
             case dyld3::Platform::watchOS:
-                return platformExcludesExecutablePath_watchOS(path);
+                return false;
             case dyld3::Platform::bridgeOS:
-                return platformExcludesExecutablePath_bridgeOS(path);
-            case dyld3::Platform::iOSMac:
                 return false;
+            case dyld3::Platform::iOSMac:
+                return platformExcludesExecutablePath_macOS(path);
             case dyld3::Platform::iOS_simulator:
                 return false;
             case dyld3::Platform::tvOS_simulator:
@@ -605,7 +575,8 @@ SharedCacheBuilder::SharedCacheBuilder(const DyldSharedCache::CreateOptions& opt
     }
 }
 
-static void verifySelfContained(std::vector<CacheBuilder::LoadedMachO>& dylibsToCache,
+static void verifySelfContained(const dyld3::closure::FileSystem& fileSystem,
+                                std::vector<CacheBuilder::LoadedMachO>& dylibsToCache,
                                 std::vector<CacheBuilder::LoadedMachO>& otherDylibs,
                                 std::vector<CacheBuilder::LoadedMachO>& couldNotLoadFiles)
 {
@@ -631,8 +602,44 @@ static void verifySelfContained(std::vector<CacheBuilder::LoadedMachO>& dylibsTo
         allDylibs.insert({ dylib.inputFile->path, &dylib });
     }
 
-    // check all dependencies to assure every dylib in cache only depends on other dylibs in cache
+    // Exclude bad unzippered twins.  These are where a zippered binary links
+    // an unzippered twin
+    std::unordered_map<std::string, std::string> macOSPathToTwinPath;
+    for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+        macOSPathToTwinPath[dylib.mappedFile.runtimePath] = "";
+    }
+    for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+        if ( startsWith(dylib.mappedFile.runtimePath, "/System/iOSSupport/") ) {
+            std::string tail = dylib.mappedFile.runtimePath.substr(18);
+            if ( macOSPathToTwinPath.find(tail) != macOSPathToTwinPath.end() )
+                macOSPathToTwinPath[tail] = dylib.mappedFile.runtimePath;
+        }
+    }
+
     __block std::map<std::string, std::set<std::string>> badDylibs;
+    for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
+        if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 )
+            continue;
+        if ( dylib.mappedFile.mh->isZippered() ) {
+            dylib.mappedFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
+                auto macOSAndTwinPath = macOSPathToTwinPath.find(loadPath);
+                if ( macOSAndTwinPath != macOSPathToTwinPath.end() ) {
+                    const std::string& twinPath = macOSAndTwinPath->second;
+                    if ( badDylibs.count(twinPath) != 0 )
+                        return;
+                    knownDylibs.erase(twinPath);
+                    badDylibs[twinPath].insert(std::string("evicting UIKitForMac binary as it is linked by zippered binary '") + dylib.mappedFile.runtimePath + "'");
+                }
+            });
+        }
+    }
+
+    // HACK: Exclude some dylibs and transitive deps for now until we have project fixes
+    __block std::set<std::string> badProjects;
+    badProjects.insert("/System/Library/PrivateFrameworks/TuriCore.framework/Versions/A/TuriCore");
+    badProjects.insert("/System/Library/PrivateFrameworks/UHASHelloExtensionPoint-macOS.framework/Versions/A/UHASHelloExtensionPoint-macOS");
+
+    // check all dependencies to assure every dylib in cache only depends on other dylibs in cache
     __block bool doAgain = true;
     while ( doAgain ) {
         doAgain = false;
@@ -640,9 +647,32 @@ static void verifySelfContained(std::vector<CacheBuilder::LoadedMachO>& dylibsTo
         for (const CacheBuilder::LoadedMachO& dylib : dylibsToCache) {
             if ( badDylibs.count(dylib.mappedFile.runtimePath) != 0 )
                 continue;
+            if ( badProjects.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 ( badProjects.count(loadPath) != 0 ) {
+                    // We depend on a bad dylib, so add this one to the list too
+                    badProjects.insert(dylib.mappedFile.runtimePath);
+                    badProjects.insert(dylib.mappedFile.mh->installName());
+                    knownDylibs.erase(dylib.mappedFile.runtimePath);
+                    knownDylibs.erase(dylib.mappedFile.mh->installName());
+                    badDylibs[dylib.mappedFile.runtimePath].insert(std::string("Depends on bad project '") + loadPath + "'");
+                    doAgain = true;
+                    return;
+                }
+                char resolvedSymlinkPath[PATH_MAX];
+                if ( knownDylibs.count(loadPath) == 0 ) {
+                    // The loadPath was embedded when the dylib was built, but we may be in the process of moving
+                    // a dylib with symlinks from old to new paths
+                    // In this case, the realpath will tell us the new location
+                    if ( fileSystem.getRealPath(loadPath, resolvedSymlinkPath) ) {
+                        if ( strcmp(resolvedSymlinkPath, loadPath) != 0 ) {
+                            loadPath = resolvedSymlinkPath;
+                        }
+                    }
+                }
                 if ( knownDylibs.count(loadPath) == 0 ) {
                     badDylibs[dylib.mappedFile.runtimePath].insert(std::string("Could not find dependency '") + loadPath + "'");
                     knownDylibs.erase(dylib.mappedFile.runtimePath);
@@ -744,7 +774,7 @@ void SharedCacheBuilder::build(std::vector<CacheBuilder::InputFile>& inputFiles,
     std::vector<LoadedMachO> couldNotLoadFiles;
     cacheInputBuilder.loadMachOs(inputFiles, dylibsToCache, otherDylibs, executables, couldNotLoadFiles);
 
-    verifySelfContained(dylibsToCache, otherDylibs, couldNotLoadFiles);
+    verifySelfContained(_fileSystem, dylibsToCache, otherDylibs, couldNotLoadFiles);
 
     // Check for required binaries before we try to build the cache
     if (!_diagnostics.hasError()) {
@@ -883,7 +913,8 @@ void SharedCacheBuilder::build(const std::vector<LoadedMachO>& dylibs,
         _diagnostics.error("missing required minimum set of dylibs");
         return;
     }
-    uint64_t t1 = mach_absolute_time();
+
+    _timeRecorder.pushTimedSection();
 
     // make copy of dylib list and sort
     makeSortedDylibs(dylibs, _options.dylibOrdering);
@@ -895,12 +926,48 @@ void SharedCacheBuilder::build(const std::vector<LoadedMachO>& dylibs,
         return;
     }
 
+    _timeRecorder.recordTime("sort dylibs");
+
+    bool impCachesSuccess = false;
+    IMPCaches::HoleMap selectorAddressIntervals;
+    _impCachesBuilder = new IMPCaches::IMPCachesBuilder(_sortedDylibs, _options.objcOptimizations, _diagnostics, _timeRecorder, _fileSystem);
+
+    // Note, macOS allows install names and paths to mismatch.  This is currently not supported by
+    // IMP caches as we use install names to look up the set of dylibs.
+    if (    _archLayout->is64
+        && (_options.platform !=  dyld3::Platform::macOS)
+        && ((_impCachesBuilder->neededClasses.size() > 0) || (_impCachesBuilder->neededMetaclasses.size() > 0))) {
+        // Build the class map across all dylibs (including cross-image superclass references)
+        _impCachesBuilder->buildClassesMap(_diagnostics);
+
+        // Determine which methods will end up in each class's IMP cache
+        impCachesSuccess = _impCachesBuilder->parseDylibs(_diagnostics);
+
+        // Compute perfect hash functions for IMP caches
+        if (impCachesSuccess) _impCachesBuilder->buildPerfectHashes(selectorAddressIntervals, _diagnostics);
+    }
+
+    constexpr bool log = false;
+    if (log) {
+        for (const auto& p : _impCachesBuilder->selectors.map) {
+            printf("0x%06x %s\n", p.second->offset, p.second->name);
+        }
+    }
+
+    _timeRecorder.recordTime("compute IMP caches");
+
+    IMPCaches::SelectorMap emptyMap;
+    IMPCaches::SelectorMap& selectorMap = impCachesSuccess ? _impCachesBuilder->selectors : emptyMap;
     // assign addresses for each segment of each dylib in new cache
-    parseCoalescableSegments();
-    processSelectorStrings(osExecutables);
+    parseCoalescableSegments(selectorMap, selectorAddressIntervals);
+    processSelectorStrings(osExecutables, selectorAddressIntervals);
+
     assignSegmentAddresses();
     std::vector<const LoadedMachO*> overflowDylibs;
     while ( cacheOverflowAmount() != 0 ) {
+        // IMP caches: we may need to recompute the selector addresses here to be slightly more compact
+        // if we remove dylibs? This is probably overkill.
+
         if ( !_options.evictLeafDylibsOnOverflow ) {
             _diagnostics.error("cache overflow by %lluMB", cacheOverflowAmount() / 1024 / 1024);
             return;
@@ -909,9 +976,16 @@ void SharedCacheBuilder::build(const std::vector<LoadedMachO>& dylibs,
         // re-layout cache
         for (DylibInfo& dylib : _sortedDylibs)
             dylib.cacheLocation.clear();
+        _dataRegions.clear();
         _coalescedText.clear();
-        parseCoalescableSegments();
-        processSelectorStrings(osExecutables);
+        
+        // Re-generate the hole map to remove any cruft that was added when parsing the coalescable text the first time.
+        // Always clear the hole map, even if IMP caches are off, as it is used by the text coalescer
+        selectorAddressIntervals.clear();
+        if (impCachesSuccess) _impCachesBuilder->computeLowBits(selectorAddressIntervals);
+        
+        parseCoalescableSegments(selectorMap, selectorAddressIntervals);
+        processSelectorStrings(osExecutables, selectorAddressIntervals);
         assignSegmentAddresses();
 
         _diagnostics.verbose("cache overflow, evicted %lu leaf dylibs\n", evictionCount);
@@ -919,60 +993,99 @@ void SharedCacheBuilder::build(const std::vector<LoadedMachO>& dylibs,
     markPaddingInaccessible();
 
      // copy all segments into cache
-    uint64_t t2 = mach_absolute_time();
+
+    unsigned long wastedSelectorsSpace = selectorAddressIntervals.totalHoleSize();
+    if (wastedSelectorsSpace > 0) {
+        _diagnostics.verbose("Selector placement for IMP caches wasted %lu bytes\n", wastedSelectorsSpace);
+        if (log) {
+            std::cerr << selectorAddressIntervals << std::endl;
+        }
+    }
+
+    _timeRecorder.recordTime("layout cache");
+
     writeCacheHeader();
     copyRawSegments();
+    _timeRecorder.recordTime("copy cached dylibs into buffer");
 
     // rebase all dylibs for new location in cache
-    uint64_t t3 = mach_absolute_time();
-    _aslrTracker.setDataRegion(_readWriteRegion.buffer, _readWriteRegion.sizeInUse);
+
+    _aslrTracker.setDataRegion(firstDataRegion()->buffer, dataRegionsTotalSize());
     if ( !_options.cacheSupportsASLR )
         _aslrTracker.disable();
-    adjustAllImagesForNewSegmentLocations();
+    adjustAllImagesForNewSegmentLocations(_archLayout->sharedMemoryStart, _aslrTracker,
+                                          &_lohTracker, &_coalescedText);
     if ( _diagnostics.hasError() )
         return;
 
+    _timeRecorder.recordTime("adjust segments for new split locations");
+
     // 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;
 
+    _timeRecorder.recordTime("bind all images");
+
     // optimize ObjC
-    uint64_t t5 = mach_absolute_time();
     DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
-    if ( _options.optimizeObjC )
-        optimizeObjC();
+    optimizeObjC(impCachesSuccess, _impCachesBuilder->inlinedSelectors);
+
+    delete _impCachesBuilder;
+    _impCachesBuilder = nullptr;
+
     if ( _diagnostics.hasError() )
         return;
 
+    _timeRecorder.recordTime("optimize Objective-C");
+
+    if ( _options.optimizeStubs ) {
+        __block std::vector<std::pair<const mach_header*, const char*>> images;
+        dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
+            images.push_back({ mh, installName });
+        });
 
-    // optimize away stubs
-    uint64_t t6 = mach_absolute_time();
-    if ( _options.optimizeStubs )
-         optimizeAwayStubs();
+        int64_t cacheSlide = (long)dyldCache - dyldCache->unslidLoadAddress();
+        uint64_t cacheUnslideAddr = dyldCache->unslidLoadAddress();
+        optimizeAwayStubs(images, cacheSlide, cacheUnslideAddr,
+                          dyldCache, _s_neverStubEliminateSymbols);
+    }
 
 
     // FIPS seal corecrypto, This must be done after stub elimination (so that __TEXT,__text is not changed after sealing)
     fipsSign();
 
+    _timeRecorder.recordTime("do stub elimination");
+
     // merge and compact LINKEDIT segments
-    uint64_t t7 = mach_absolute_time();
-    optimizeLinkedit();
+    {
+        // If we want to remove, not just unmap locals, then set the dylibs themselves to be stripped
+        DylibStripMode dylibStripMode = DylibStripMode::stripNone;
+        if ( _options.localSymbolMode == DyldSharedCache::LocalSymbolsMode::strip )
+            dylibStripMode = CacheBuilder::DylibStripMode::stripLocals;
+
+        __block std::vector<std::tuple<const mach_header*, const char*, DylibStripMode>> images;
+        dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
+            images.push_back({ mh, installName, dylibStripMode });
+        });
+        optimizeLinkedit(&_localSymbolsRegion, images);
+    }
 
     // copy ImageArray to end of read-only region
     addImageArray();
     if ( _diagnostics.hasError() )
         return;
-    uint64_t t8 = mach_absolute_time();
 
-    // don't add dyld3 closures to simulator cache
-    if ( !dyld3::MachOFile::isSimulatorPlatform(_options.platform) ) {
+    _timeRecorder.recordTime("optimize LINKEDITs");
+
+    // don't add dyld3 closures to simulator cache or the base system where size is more of an issue
+    if ( _options.optimizeDyldDlopens ) {
         // compute and add dlopen closures for all other dylibs
         addOtherImageArray(otherOsDylibsInput, overflowDylibs);
         if ( _diagnostics.hasError() )
             return;
-
+    }
+    if ( _options.optimizeDyldLaunches ) {
         // compute and add launch closures to end of read-only region
         addClosures(osExecutables);
         if ( _diagnostics.hasError() )
@@ -981,16 +1094,20 @@ void SharedCacheBuilder::build(const std::vector<LoadedMachO>& dylibs,
 
     // 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 )
+    mappings[dyldCache->header.mappingCount - 1].size = _readOnlyRegion.sizeInUse;
+    dyld_cache_mapping_and_slide_info* slidableMappings = (dyld_cache_mapping_and_slide_info*)(_readExecuteRegion.buffer + dyldCache->header.mappingWithSlideOffset);
+    slidableMappings[dyldCache->header.mappingCount - 1].size = _readOnlyRegion.sizeInUse;
+    if ( _localSymbolsRegion.sizeInUse != 0 ) {
         dyldCache->header.localSymbolsOffset = _readOnlyRegion.cacheFileOffset + _readOnlyRegion.sizeInUse;
+        dyldCache->header.localSymbolsSize   = _localSymbolsRegion.sizeInUse;
+    }
 
     // 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 - _readExecuteRegion.sizeInUse; // TEXT region has 1.5GB region
-        uint64_t maxSlide1 = 0x40000000 - _readWriteRegion.sizeInUse;
-        uint64_t maxSlide2 = 0x3FE00000 - _readOnlyRegion.sizeInUse;
+        uint64_t maxSlide0 = DISCONTIGUOUS_RX_SIZE - _readExecuteRegion.sizeInUse; // TEXT region has 1.5GB region
+        uint64_t maxSlide1 = DISCONTIGUOUS_RW_SIZE - dataRegionsTotalSize();
+        uint64_t maxSlide2 = DISCONTIGUOUS_RO_SIZE - _readOnlyRegion.sizeInUse;
         dyldCache->header.maxSlide = std::min(std::min(maxSlide0, maxSlide1), maxSlide2);
     }
     else {
@@ -1004,7 +1121,13 @@ void SharedCacheBuilder::build(const std::vector<LoadedMachO>& dylibs,
     // mark if any input dylibs were built with chained fixups
     dyldCache->header.builtFromChainedFixups = _someDylibsUsedChainedFixups;
 
-    uint64_t t9 = mach_absolute_time();
+    _timeRecorder.recordTime("build %lu closures", osExecutables.size());
+    // Emit the CF strings without their ISAs being signed
+    // This must be after addImageArray() as it depends on hasImageIndex().
+    // It also has to be before emitting slide info as it adds ASLR entries.
+    emitContantObjects();
+
+    _timeRecorder.recordTime("emit constant objects");
 
     // fill in slide info at start of region[2]
     // do this last because it modifies pointers in DATA segments
@@ -1024,7 +1147,7 @@ void SharedCacheBuilder::build(const std::vector<LoadedMachO>& dylibs,
             writeSlideInfoV2<Pointer32<LittleEndian>>(_aslrTracker.bitmap(), _aslrTracker.dataPageCount());
     }
 
-    uint64_t t10 = mach_absolute_time();
+    _timeRecorder.recordTime("compute slide info");
 
     // last sanity check on size
     if ( cacheOverflowAmount() != 0 ) {
@@ -1037,19 +1160,10 @@ void SharedCacheBuilder::build(const std::vector<LoadedMachO>& dylibs,
     if ( _diagnostics.hasError() )
         return;
 
-    uint64_t t11 = mach_absolute_time();
+    _timeRecorder.recordTime("compute UUID and codesign cache file");
 
-    if ( _options.verbose ) {
-        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));
+    if (_options.verbose) {
+        _timeRecorder.logTimings();
     }
 
     return;
@@ -1068,19 +1182,23 @@ const std::set<const dyld3::MachOAnalyzer*> SharedCacheBuilder::evictions()
 void SharedCacheBuilder::deleteBuffer()
 {
     // Cache buffer
-    vm_deallocate(mach_task_self(), _fullAllocatedBuffer, _archLayout->sharedMemorySize);
-    _fullAllocatedBuffer = 0;
-    _allocatedBufferSize = 0;
+    if ( _allocatedBufferSize != 0 ) {
+        vm_deallocate(mach_task_self(), _fullAllocatedBuffer, _allocatedBufferSize);
+        _fullAllocatedBuffer = 0;
+        _allocatedBufferSize = 0;
+    }
     // Local symbols buffer
     if ( _localSymbolsRegion.bufferSize != 0 ) {
         vm_deallocate(mach_task_self(), (vm_address_t)_localSymbolsRegion.buffer, _localSymbolsRegion.bufferSize);
         _localSymbolsRegion.buffer = 0;
         _localSymbolsRegion.bufferSize = 0;
     }
-    // Code singatures
-    vm_deallocate(mach_task_self(), (vm_address_t)_codeSignatureRegion.buffer, _codeSignatureRegion.bufferSize);
-    _codeSignatureRegion.buffer = 0;
-    _codeSignatureRegion.bufferSize = 0;
+    // Code signatures
+    if ( _codeSignatureRegion.bufferSize != 0 ) {
+        vm_deallocate(mach_task_self(), (vm_address_t)_codeSignatureRegion.buffer, _codeSignatureRegion.bufferSize);
+        _codeSignatureRegion.buffer = 0;
+        _codeSignatureRegion.bufferSize = 0;
+    }
 }
 
 
@@ -1128,29 +1246,22 @@ uint64_t SharedCacheBuilder::cacheOverflowAmount()
 {
     if ( _archLayout->sharedRegionsAreDiscontiguous ) {
         // for macOS x86_64 cache, need to check each region for overflow
-        if ( _readExecuteRegion.sizeInUse > 0x60000000 )
-            return (_readExecuteRegion.sizeInUse - 0x60000000);
+        if ( _readExecuteRegion.sizeInUse > DISCONTIGUOUS_RX_SIZE )
+            return (_readExecuteRegion.sizeInUse - DISCONTIGUOUS_RX_SIZE);
 
-        if ( _readWriteRegion.sizeInUse > 0x40000000 )
-            return (_readWriteRegion.sizeInUse - 0x40000000);
+        uint64_t dataSize = dataRegionsTotalSize();
+        if ( dataSize > DISCONTIGUOUS_RW_SIZE )
+            return (dataSize - DISCONTIGUOUS_RW_SIZE);
 
-        if ( _readOnlyRegion.sizeInUse > 0x3FE00000 )
-            return (_readOnlyRegion.sizeInUse - 0x3FE00000);
-    }
-    else if ( _archLayout->textAndDataMaxSize != 0 ) {
-        // for armv7k, limit is 512MB of TEX+DATA
-        uint64_t totalTextAndData = _readWriteRegion.unslidLoadAddress + _readWriteRegion.sizeInUse - _readExecuteRegion.unslidLoadAddress;
-        if ( totalTextAndData < _archLayout->textAndDataMaxSize )
-            return 0;
-        else
-            return totalTextAndData - _archLayout->textAndDataMaxSize;
+        if ( _readOnlyRegion.sizeInUse > DISCONTIGUOUS_RO_SIZE )
+            return (_readOnlyRegion.sizeInUse - DISCONTIGUOUS_RO_SIZE);
     }
     else {
         bool alreadyOptimized = (_readOnlyRegion.sizeInUse != _readOnlyRegion.bufferSize);
         uint64_t vmSize = _readOnlyRegion.unslidLoadAddress - _readExecuteRegion.unslidLoadAddress;
         if ( alreadyOptimized )
             vmSize += _readOnlyRegion.sizeInUse;
-        else if ( _options.excludeLocalSymbols )
+        else if ( _options.localSymbolMode == DyldSharedCache::LocalSymbolsMode::unmap )
             vmSize += (_readOnlyRegion.sizeInUse * 37/100); // assume locals removal and LINKEDIT optimzation reduces LINKEDITs %37 of original size
         else
             vmSize += (_readOnlyRegion.sizeInUse * 80/100); // assume LINKEDIT optimzation reduces LINKEDITs to %80 of original size
@@ -1204,7 +1315,7 @@ size_t SharedCacheBuilder::evictLeafDylibs(uint64_t reductionTarget, std::vector
         DylibAndSize candidate;
         uint64_t candidateOrder = 0;
         for(const auto& dylib : dylibsToSort) {
-            const auto &i = referencesPtr->find(dylib.installName);
+            const autoi = referencesPtr->find(dylib.installName);
             assert(i != referencesPtr->end());
             if (!i->second.empty()) {
                 continue;
@@ -1270,18 +1381,24 @@ void SharedCacheBuilder::writeCacheHeader()
     magic.append(_options.archs->name());
     assert(magic.length() == 15);
 
+    // 1 __TEXT segment, n __DATA segments, and 1 __LINKEDIT segment
+    const uint32_t mappingCount = 2 + (uint32_t)_dataRegions.size();
+    assert(mappingCount <= DyldSharedCache::MaxMappings);
+
     // fill in header
     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));
+    dyldCacheHeader->mappingCount         = mappingCount;
+    dyldCacheHeader->mappingWithSlideOffset = (uint32_t)(dyldCacheHeader->mappingOffset + mappingCount*sizeof(dyld_cache_mapping_and_slide_info));
+    dyldCacheHeader->mappingWithSlideCount  = mappingCount;
+    dyldCacheHeader->imagesOffset         = (uint32_t)(dyldCacheHeader->mappingWithSlideOffset + mappingCount*sizeof(dyld_cache_mapping_and_slide_info));
     dyldCacheHeader->imagesCount          = (uint32_t)_sortedDylibs.size() + _aliasCount;
     dyldCacheHeader->dyldBaseAddress      = 0;
     dyldCacheHeader->codeSignatureOffset  = 0;
     dyldCacheHeader->codeSignatureSize    = 0;
-    dyldCacheHeader->slideInfoOffset      = _slideInfoFileOffset;
-    dyldCacheHeader->slideInfoSize        = _slideInfoBufferSizeAllocated;
+    dyldCacheHeader->slideInfoOffsetUnused     = 0;
+    dyldCacheHeader->slideInfoSizeUnused       = 0;
     dyldCacheHeader->localSymbolsOffset   = 0;
     dyldCacheHeader->localSymbolsSize     = 0;
     dyldCacheHeader->cacheType            = _options.optimizeStubs ? kDyldSharedCacheTypeProduction : kDyldSharedCacheTypeDevelopment;
@@ -1312,21 +1429,67 @@ void SharedCacheBuilder::writeCacheHeader()
 
    // fill in mappings
     dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(_readExecuteRegion.buffer + dyldCacheHeader->mappingOffset);
+    assert(_readExecuteRegion.cacheFileOffset == 0);
     mappings[0].address    = _readExecuteRegion.unslidLoadAddress;
-    mappings[0].fileOffset = 0;
+    mappings[0].fileOffset = _readExecuteRegion.cacheFileOffset;
     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;
+    for (uint32_t i = 0; i != _dataRegions.size(); ++i) {
+        if ( i == 0 ) {
+            assert(_dataRegions[i].cacheFileOffset == _readExecuteRegion.sizeInUse);
+        }
+        mappings[i + 1].address    = _dataRegions[i].unslidLoadAddress;
+        mappings[i + 1].fileOffset = _dataRegions[i].cacheFileOffset;
+        mappings[i + 1].size       = _dataRegions[i].sizeInUse;
+        mappings[i + 1].maxProt    = VM_PROT_READ | VM_PROT_WRITE;
+        mappings[i + 1].initProt   = VM_PROT_READ | VM_PROT_WRITE;
+    }
+    assert(_readOnlyRegion.cacheFileOffset == (_dataRegions.back().cacheFileOffset + _dataRegions.back().sizeInUse));
+    mappings[mappingCount - 1].address    = _readOnlyRegion.unslidLoadAddress;
+    mappings[mappingCount - 1].fileOffset = _readOnlyRegion.cacheFileOffset;
+    mappings[mappingCount - 1].size       = _readOnlyRegion.sizeInUse;
+    mappings[mappingCount - 1].maxProt    = VM_PROT_READ;
+    mappings[mappingCount - 1].initProt   = VM_PROT_READ;
+
+    // Add in the new mappings with also have slide info
+    dyld_cache_mapping_and_slide_info* slidableMappings = (dyld_cache_mapping_and_slide_info*)(_readExecuteRegion.buffer + dyldCacheHeader->mappingWithSlideOffset);
+    slidableMappings[0].address             = _readExecuteRegion.unslidLoadAddress;
+    slidableMappings[0].fileOffset          = _readExecuteRegion.cacheFileOffset;
+    slidableMappings[0].size                = _readExecuteRegion.sizeInUse;
+    slidableMappings[0].maxProt             = VM_PROT_READ | VM_PROT_EXECUTE;
+    slidableMappings[0].initProt            = VM_PROT_READ | VM_PROT_EXECUTE;
+    slidableMappings[0].slideInfoFileOffset = 0;
+    slidableMappings[0].slideInfoFileSize   = 0;
+    slidableMappings[0].flags               = 0;
+    for (uint32_t i = 0; i != _dataRegions.size(); ++i) {
+        // Work out which flags this mapping has
+        uint64_t flags = 0;
+        if ( startsWith(_dataRegions[i].name, "__AUTH") )
+            flags |= DYLD_CACHE_MAPPING_AUTH_DATA;
+        if ( (_dataRegions[i].name == "__AUTH_DIRTY") || (_dataRegions[i].name == "__DATA_DIRTY") ) {
+            flags |= DYLD_CACHE_MAPPING_DIRTY_DATA;
+        } else if ( (_dataRegions[i].name == "__AUTH_CONST") || (_dataRegions[i].name == "__DATA_CONST") ) {
+            flags |= DYLD_CACHE_MAPPING_CONST_DATA;
+        }
+
+        slidableMappings[i + 1].address             = _dataRegions[i].unslidLoadAddress;
+        slidableMappings[i + 1].fileOffset          = _dataRegions[i].cacheFileOffset;
+        slidableMappings[i + 1].size                = _dataRegions[i].sizeInUse;
+        slidableMappings[i + 1].maxProt             = VM_PROT_READ | VM_PROT_WRITE;
+        slidableMappings[i + 1].initProt            = VM_PROT_READ | VM_PROT_WRITE;
+        slidableMappings[i + 1].slideInfoFileOffset = _dataRegions[i].slideInfoFileOffset;
+        slidableMappings[i + 1].slideInfoFileSize   = _dataRegions[i].slideInfoFileSize;
+        slidableMappings[i + 1].flags               = flags;
+    }
+    slidableMappings[mappingCount - 1].address             = _readOnlyRegion.unslidLoadAddress;
+    slidableMappings[mappingCount - 1].fileOffset          = _readOnlyRegion.cacheFileOffset;
+    slidableMappings[mappingCount - 1].size                = _readOnlyRegion.sizeInUse;
+    slidableMappings[mappingCount - 1].maxProt             = VM_PROT_READ;
+    slidableMappings[mappingCount - 1].initProt            = VM_PROT_READ;
+    slidableMappings[mappingCount - 1].slideInfoFileOffset = 0;
+    slidableMappings[mappingCount - 1].slideInfoFileSize   = 0;
+    slidableMappings[mappingCount - 1].flags               = 0;
 
     // fill in image table
     dyld_cache_image_info* images = (dyld_cache_image_info*)(_readExecuteRegion.buffer + dyldCacheHeader->imagesOffset);
@@ -1389,10 +1552,16 @@ void SharedCacheBuilder::writeCacheHeader()
     assert(stringOffset <= (firstImage->address - mappings[0].address));
 }
 
-void SharedCacheBuilder::processSelectorStrings(const std::vector<LoadedMachO>& executables) {
+void SharedCacheBuilder::processSelectorStrings(const std::vector<LoadedMachO>& executables, IMPCaches::HoleMap& selectorsHoleMap) {
     const bool log = false;
 
+    // We only do this optimisation to reduce the size of the shared cache executable closures
+    // Skip this is those closures are not being built
+    if ( !_options.optimizeDyldDlopens || !_options.optimizeDyldLaunches )
+        return;
+
     _selectorStringsFromExecutables = 0;
+    uint64_t totalBytesPulledIn = 0;
 
     // Don't do this optimisation on watchOS where the shared cache is too small
     if (_options.platform == dyld3::Platform::watchOS)
@@ -1405,32 +1574,39 @@ void SharedCacheBuilder::processSelectorStrings(const std::vector<LoadedMachO>&
 
         uint64_t sizeBeforeProcessing = cacheStringSection.bufferSize;
 
-        ma->forEachObjCMethodName(^(const char *methodName) {
+        ma->forEachObjCMethodName(^(const charmethodName) {
             std::string_view str = methodName;
-            auto itAndInserted = cacheStringSection.stringsToOffsets.insert({ str, cacheStringSection.bufferSize });
-            if (itAndInserted.second) {
-                // If we inserted the string then we need to include it in the total
-                cacheStringSection.bufferSize += str.size() + 1;
+            if (cacheStringSection.stringsToOffsets.find(str) == cacheStringSection.stringsToOffsets.end()) {
+                int offset = selectorsHoleMap.addStringOfSize((unsigned)str.size() + 1);
+                cacheStringSection.stringsToOffsets[str] = offset;
+
+                // If we inserted the string past the end then we need to include it in the total
+                int possibleNewEnd = offset + (int)str.size() + 1;
+                if (cacheStringSection.bufferSize < (uint32_t)possibleNewEnd) {
+                    cacheStringSection.bufferSize = (uint32_t)possibleNewEnd;
+                }
                 // if (log) printf("Selector: %s -> %s\n", ma->installName(), methodName);
                 ++_selectorStringsFromExecutables;
             }
         });
 
         uint64_t sizeAfterProcessing = cacheStringSection.bufferSize;
+        totalBytesPulledIn += (sizeAfterProcessing - sizeBeforeProcessing);
         if ( log && (sizeBeforeProcessing != sizeAfterProcessing) ) {
             printf("Pulled in % 6lld bytes of selectors from %s\n",
                    sizeAfterProcessing - sizeBeforeProcessing, executable.loadedFileInfo.path);
         }
     }
 
-    _diagnostics.verbose("Pulled in %lld selector strings from executables\n", _selectorStringsFromExecutables);
+    _diagnostics.verbose("Pulled in %lld selector strings (%lld bytes) from executables\n",
+                         _selectorStringsFromExecutables, totalBytesPulledIn);
 }
 
-void SharedCacheBuilder::parseCoalescableSegments() {
+void SharedCacheBuilder::parseCoalescableSegments(IMPCaches::SelectorMap& selectors, IMPCaches::HoleMap& selectorsHoleMap) {
     const bool log = false;
 
     for (DylibInfo& dylib : _sortedDylibs)
-        _coalescedText.parseCoalescableText(dylib.input->mappedFile.mh, dylib.textCoalescer);
+        _coalescedText.parseCoalescableText(dylib.input->mappedFile.mh, dylib.textCoalescer, selectors, selectorsHoleMap);
 
     if (log) {
         for (const char* section : CacheCoalescedText::SupportedSections) {
@@ -1439,309 +1615,651 @@ void SharedCacheBuilder::parseCoalescableSegments() {
                    sectionData.bufferSize + sectionData.savedSpace, sectionData.bufferSize, sectionData.savedSpace);
         }
     }
-}
 
-void SharedCacheBuilder::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);
-    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);
+    // arm64e needs to convert CF constants to tagged pointers
+    if ( !strcmp(_archLayout->archName, "arm64e") ) {
+        // Find the dylib which exports the CFString ISA.  It's likely CoreFoundation but it could move
+        CacheCoalescedText::CFSection& cfStrings = _coalescedText.cfStrings;
+        for (DylibInfo& dylib : _sortedDylibs) {
+            const dyld3::MachOAnalyzer* ma = dylib.input->mappedFile.mh;
+            dyld3::MachOAnalyzer::FoundSymbol foundInfo;
+            bool foundISASymbol = ma->findExportedSymbol(_diagnostics, cfStrings.isaClassName, false, foundInfo, nullptr);
+            if ( foundISASymbol ) {
+                // This dylib exports the ISA, so everyone else should look here for the ISA too.
+                if ( cfStrings.isaInstallName != nullptr ) {
+                    // Found a duplicate.  We can't do anything here
+                    _diagnostics.verbose("Could not optimize CFString's due to duplicate ISA symbols");
+                    cfStrings.isaInstallName = nullptr;
+                    break;
+                } else {
+                    cfStrings.isaInstallName = ma->installName();
+                    cfStrings.isaVMOffset    = foundInfo.value;
+                }
+            }
+        }
+        if ( cfStrings.isaInstallName != nullptr ) {
+            for (DylibInfo& dylib : _sortedDylibs) {
+                _coalescedText.parseCFConstants(dylib.input->mappedFile.mh, dylib.textCoalescer);
+            }
+        }
     }
-    //fprintf(stderr, "%s total header size = 0x%08lX\n", _options.archName.c_str(), startOffset);
-    startOffset = align(startOffset, 12);
+}
 
-    // HACK!: Rebase v4 assumes that values below 0x8000 are not pointers (encoding as offsets from the cache header).
-    // If using a minimal cache, we need to pad out the cache header to make sure a pointer doesn't fall within that range
-#if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k
-    if ( _options.cacheSupportsASLR && !_archLayout->is64 ) {
-        if ( _archLayout->pointerDeltaMask == 0xC0000000 )
-            startOffset = std::max(startOffset, (size_t)0x8000);
+// This is the new method which will put all __DATA* mappings in to a their own mappings
+void SharedCacheBuilder::assignMultipleDataSegmentAddresses(uint64_t& addr, uint32_t totalProtocolDefCount) {
+    uint64_t nextRegionFileOffset = _readExecuteRegion.sizeInUse;
+
+    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;
+    });
+
+    // Work out if we'll have __AUTH regions, as the objc RW has to go at the end of __AUTH if it exists, or
+    // the end of __DATA if we have no __AUTH
+    __block bool foundAuthenticatedFixups = false;
+
+    // This tracks which segments contain authenticated data, even if their name isn't __AUTH*
+    std::map<const DylibInfo*, std::set<uint32_t>> authenticatedSegments;
+
+    if ( strcmp(_archLayout->archName, "arm64e") == 0 ) {
+        for (DylibInfo& dylib : _sortedDylibs) {
+            __block std::set<uint32_t>& authSegmentIndices = authenticatedSegments[&dylib];
+
+            // Put all __DATA_DIRTY segments in the __AUTH region first, then we don't need to walk their chains
+            dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+                if ( strcmp(segInfo.segName, "__DATA_DIRTY") == 0 ) {
+                    authSegmentIndices.insert(segInfo.segIndex);
+                    foundAuthenticatedFixups = true;
+                    stop = true;
+                }
+            });
+            dylib.input->mappedFile.mh->withChainStarts(_diagnostics, 0,
+                                                        ^(const dyld_chained_starts_in_image *starts) {
+                dylib.input->mappedFile.mh->forEachFixupChainSegment(_diagnostics, starts,
+                                                                     ^(const dyld_chained_starts_in_segment* segmentInfo, uint32_t segIndex, bool& stopSegment) {
+                    // Skip walking segments we already know are __AUTH, ie, __DATA_DIRTY
+                    if ( authSegmentIndices.count(segIndex) )
+                        return;
+
+                    dylib.input->mappedFile.mh->forEachFixupInSegmentChains(_diagnostics, segmentInfo, false,
+                                                                            ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stopChain) {
+                        uint16_t chainedFixupsFormat = segInfo->pointer_format;
+                        assert( (chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E) || (chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E_USERLAND) || (chainedFixupsFormat == DYLD_CHAINED_PTR_ARM64E_USERLAND24) );
+
+                        if ( fixupLoc->arm64e.authRebase.auth ) {
+                            foundAuthenticatedFixups = true;
+                            authSegmentIndices.insert(segIndex);
+                            stopChain = true;
+                            return;
+                        }
+                    });
+                });
+            });
+        }
     }
-#endif
 
-    // assign TEXT segment addresses
-    _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
-    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;
-            // We may have coalesced the sections at the end of this segment.  In that case, shrink the segment to remove them.
-            __block size_t sizeOfSections = 0;
-            __block bool foundCoalescedSection = false;
-            dylib.input->mappedFile.mh->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo &sectInfo, bool malformedSectionRange, bool &stopSection) {
-                if (strcmp(sectInfo.segInfo.segName, segInfo.segName) != 0)
+    // __DATA
+    {
+        Region region;
+        region.buffer               = (uint8_t*)_fullAllocatedBuffer + addr - _archLayout->sharedMemoryStart;
+        region.bufferSize           = 0;
+        region.sizeInUse            = 0;
+        region.unslidLoadAddress    = addr;
+        region.cacheFileOffset      = nextRegionFileOffset;
+        region.name                 = "__DATA";
+
+        // layout all __DATA_CONST/__OBJC_CONST segments
+        __block int dataConstSegmentCount = 0;
+        for (DylibInfo& dylib : _sortedDylibs) {
+            __block uint64_t textSegVmAddr = 0;
+            __block std::set<uint32_t>& authSegmentIndices = authenticatedSegments[&dylib];
+           dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+               if ( _options.platform == dyld3::Platform::watchOS_simulator && !_is64)
+                   return;
+                if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+                    textSegVmAddr = segInfo.vmAddr;
+                if ( segInfo.protections != (VM_PROT_READ | VM_PROT_WRITE) )
                     return;
-                if ( dylib.textCoalescer.sectionWasCoalesced(sectInfo.sectName)) {
-                    foundCoalescedSection = true;
-                } else {
-                    sizeOfSections = sectInfo.sectAddr + sectInfo.sectSize - segInfo.vmAddr;
+                if ( (strcmp(segInfo.segName, "__DATA_CONST") != 0) && (strcmp(segInfo.segName, "__OBJC_CONST") != 0) )
+                    return;
+
+                // We may have coalesced the sections at the end of this segment.  In that case, shrink the segment to remove them.
+                __block size_t sizeOfSections = 0;
+                __block bool foundCoalescedSection = false;
+                dylib.input->mappedFile.mh->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo &sectInfo, bool malformedSectionRange, bool &stopSection) {
+                    if (strcmp(sectInfo.segInfo.segName, segInfo.segName) != 0)
+                        return;
+                    if ( dylib.textCoalescer.sectionWasCoalesced(segInfo.segName, sectInfo.sectName)) {
+                        foundCoalescedSection = true;
+                    } else {
+                        sizeOfSections = sectInfo.sectAddr + sectInfo.sectSize - segInfo.vmAddr;
+                    }
+                });
+                if (!foundCoalescedSection)
+                    sizeOfSections = segInfo.sizeOfSections;
+
+                if ( authSegmentIndices.count(segInfo.segIndex) ) {
+                    // Only move this segment to __AUTH if it had content we didn't coalesce away
+                    if ( !foundCoalescedSection || (sizeOfSections != 0) ) {
+                        // Don't put authenticated __DATA_CONST/__OBJC_CONST in the non-AUTH __DATA mapping
+                        _diagnostics.verbose("%s: treating authenticated %s as __AUTH_CONST\n", dylib.dylibID.c_str(), segInfo.segName);
+                        return;
+                    }
                 }
+
+                ++dataConstSegmentCount;
+                // Pack __DATA_CONST segments
+                addr = align(addr, segInfo.p2align);
+                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)sizeOfSections);
+                uint64_t offsetInRegion = addr - region.unslidLoadAddress;
+                SegmentMappingInfo loc;
+                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+                loc.segName                = segInfo.segName;
+                loc.dstSegment             = region.buffer + offsetInRegion;
+                loc.dstCacheUnslidAddress  = addr;
+                loc.dstCacheFileOffset     = (uint32_t)(region.cacheFileOffset + offsetInRegion);
+                loc.dstCacheSegmentSize    = (uint32_t)sizeOfSections;
+                loc.dstCacheFileSize       = (uint32_t)copySize;
+                loc.copySegmentSize        = (uint32_t)copySize;
+                loc.srcSegmentIndex        = segInfo.segIndex;
+                dylib.cacheLocation.push_back(loc);
+                addr += loc.dstCacheSegmentSize;
             });
-            if (!foundCoalescedSection)
-                sizeOfSections = segInfo.sizeOfSections;
+        }
 
-            // Keep __TEXT segments 4K or more aligned
-            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(sizeOfSections, 12);
-            loc.dstCacheFileSize       = (uint32_t)align(sizeOfSections, 12);
-            loc.copySegmentSize        = (uint32_t)sizeOfSections;
-            loc.srcSegmentIndex        = segInfo.segIndex;
-            dylib.cacheLocation.push_back(loc);
-            addr += loc.dstCacheSegmentSize;
-        });
-    }
-    // move read-only segments to end of TEXT
-    if ( _archLayout->textAndDataMaxSize != 0 ) {
-       for (DylibInfo& dylib : _sortedDylibs) {
+        // align __DATA_CONST region end
+        addr = align(addr, _archLayout->sharedRegionAlignP2);
+
+        // Make space for the cfstrings
+        if ( _coalescedText.cfStrings.bufferSize != 0 ) {
+            // Keep __DATA segments 4K or more aligned
+            addr = align(addr, 12);
+            uint64_t offsetInRegion = addr - region.unslidLoadAddress;
+
+            CacheCoalescedText::CFSection& cacheSection = _coalescedText.cfStrings;
+            cacheSection.bufferAddr         = region.buffer + offsetInRegion;
+            cacheSection.bufferVMAddr       = addr;
+            cacheSection.cacheFileOffset    = region.cacheFileOffset + offsetInRegion;
+            addr += cacheSection.bufferSize;
+        }
+
+        // layout all __DATA_DIRTY segments, sorted (FIXME)
+        for (size_t i=0; i < dylibCount; ++i) {
+            DylibInfo& dylib  = _sortedDylibs[dirtyDataSortIndexes[i]];
             __block uint64_t textSegVmAddr = 0;
+            __block std::set<uint32_t>& authSegmentIndices = authenticatedSegments[&dylib];
             dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+                if ( _options.platform == dyld3::Platform::watchOS_simulator && !_is64)
+                    return;
                 if ( strcmp(segInfo.segName, "__TEXT") == 0 )
                     textSegVmAddr = segInfo.vmAddr;
-                if ( segInfo.protections != VM_PROT_READ )
+                if ( segInfo.protections != (VM_PROT_READ | VM_PROT_WRITE) )
+                    return;
+                if ( strcmp(segInfo.segName, "__DATA_DIRTY") != 0 )
                     return;
-                if ( strcmp(segInfo.segName, "__LINKEDIT") == 0 )
+                if ( authSegmentIndices.count(segInfo.segIndex) ) {
+                    // Don't put authenticated __DATA_DIRTY in the non-AUTH __DATA mapping
+                    // This is going to be true for all arm64e __DATA_DIRTY as we move it all, regardless of auth fixups.
+                    // Given that, don't issue a diagnostic as its really not helpful
                     return;
+                }
+                // Pack __DATA_DIRTY segments
+                addr = align(addr, segInfo.p2align);
+                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+                uint64_t offsetInRegion = addr - region.unslidLoadAddress;
+                SegmentMappingInfo loc;
+                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+                loc.segName                = segInfo.segName;
+                loc.dstSegment             = region.buffer + offsetInRegion;
+                loc.dstCacheUnslidAddress  = addr;
+                loc.dstCacheFileOffset     = (uint32_t)(region.cacheFileOffset + offsetInRegion);
+                loc.dstCacheSegmentSize    = (uint32_t)segInfo.sizeOfSections;
+                loc.dstCacheFileSize       = (uint32_t)copySize;
+                loc.copySegmentSize        = (uint32_t)copySize;
+                loc.srcSegmentIndex        = segInfo.segIndex;
+                dylib.cacheLocation.push_back(loc);
+                addr += loc.dstCacheSegmentSize;
+            });
+        }
+
+        // align __DATA_DIRTY region end
+        addr = align(addr, _archLayout->sharedRegionAlignP2);
 
-                // Keep segments segments 4K or more aligned
-                addr = align(addr, std::max((int)segInfo.p2align, (int)12));
-                uint64_t offsetInRegion = addr - _readExecuteRegion.unslidLoadAddress;
+        // layout all __DATA segments (and other r/w non-dirty, non-const, non-auth) segments
+        for (DylibInfo& dylib : _sortedDylibs) {
+            __block uint64_t textSegVmAddr = 0;
+            __block std::set<uint32_t>& authSegmentIndices = authenticatedSegments[&dylib];
+           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 ( _options.platform != dyld3::Platform::watchOS_simulator || _is64) {
+                    if ( strcmp(segInfo.segName, "__DATA_CONST") == 0 )
+                        return;
+                    if ( strcmp(segInfo.segName, "__DATA_DIRTY") == 0 )
+                        return;
+                    if ( strcmp(segInfo.segName, "__OBJC_CONST") == 0 )
+                        return;
+                }
+                // Skip __AUTH* segments as they'll be handled elsewhere
+                if ( strncmp(segInfo.segName, "__AUTH", 6) == 0 )
+                    return;
+               if ( authSegmentIndices.count(segInfo.segIndex) ) {
+                   // Don't put authenticated __DATA in the non-AUTH __DATA mapping
+                   _diagnostics.verbose("%s: treating authenticated __DATA as __AUTH\n", dylib.dylibID.c_str());
+                   return;
+               }
+                bool forcePageAlignedData = false;
+                if (_options.platform == dyld3::Platform::macOS) {
+                    forcePageAlignedData = dylib.input->mappedFile.mh->hasUnalignedPointerFixups();
+                    //if ( forcePageAlignedData )
+                    //    warning("unaligned pointer in %s\n", dylib.input->mappedFile.runtimePath.c_str());
+                }
+                if ( (dataConstSegmentCount > 10) && !forcePageAlignedData ) {
+                    // Pack __DATA segments only if we also have __DATA_CONST segments
+                    addr = align(addr, segInfo.p2align);
+                }
+                else {
+                    // Keep __DATA segments 4K or more aligned
+                    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 - region.unslidLoadAddress;
                 SegmentMappingInfo loc;
                 loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
                 loc.segName                = segInfo.segName;
-                loc.dstSegment             = _readExecuteRegion.buffer + offsetInRegion;
+                loc.dstSegment             = region.buffer + offsetInRegion;
                 loc.dstCacheUnslidAddress  = addr;
-                loc.dstCacheFileOffset     = (uint32_t)(_readExecuteRegion.cacheFileOffset + offsetInRegion);
-                loc.dstCacheSegmentSize    = (uint32_t)align(segInfo.sizeOfSections, 12);
-                loc.dstCacheFileSize       = (uint32_t)segInfo.sizeOfSections;
-                loc.copySegmentSize        = (uint32_t)segInfo.sizeOfSections;
+                loc.dstCacheFileOffset     = (uint32_t)(region.cacheFileOffset + offsetInRegion);
+                loc.dstCacheSegmentSize    = (uint32_t)segInfo.sizeOfSections;
+                loc.dstCacheFileSize       = (uint32_t)copySize;
+                loc.copySegmentSize        = (uint32_t)copySize;
                 loc.srcSegmentIndex        = segInfo.segIndex;
                 dylib.cacheLocation.push_back(loc);
                 addr += loc.dstCacheSegmentSize;
             });
         }
-    }
 
-    // reserve space for objc optimization tables and deduped strings
-    uint64_t objcReadOnlyBufferVMAddr = addr;
-    _objcReadOnlyBuffer = _readExecuteRegion.buffer + (addr - _readExecuteRegion.unslidLoadAddress);
+        if ( !foundAuthenticatedFixups ) {
+            // reserve space for objc r/w optimization tables
+            _objcReadWriteBufferSizeAllocated = align(computeReadWriteObjC((uint32_t)_sortedDylibs.size(), totalProtocolDefCount), 14);
+            addr = align(addr, 4); // objc r/w section contains pointer and must be at least pointer align
+            _objcReadWriteBuffer = region.buffer + (addr - region.unslidLoadAddress);
+            addr += _objcReadWriteBufferSizeAllocated;
+        }
 
-    // First the strings as we'll fill in the objc tables later in the optimizer
-    for (const char* section: CacheCoalescedText::SupportedSections) {
-        CacheCoalescedText::StringSection& cacheStringSection = _coalescedText.getSectionData(section);
-        cacheStringSection.bufferAddr = _readExecuteRegion.buffer + (addr - _readExecuteRegion.unslidLoadAddress);
-        cacheStringSection.bufferVMAddr = addr;
-        addr += cacheStringSection.bufferSize;
+        // align DATA region end
+        addr = align(addr, _archLayout->sharedRegionAlignP2);
+        uint64_t endDataAddress = addr;
+        region.bufferSize   = endDataAddress - region.unslidLoadAddress;
+        region.sizeInUse    = region.bufferSize;
+
+        _dataRegions.push_back(region);
+        nextRegionFileOffset = region.cacheFileOffset + region.sizeInUse;
     }
 
-    addr = align(addr, 14);
-    _objcReadOnlyBufferSizeUsed = addr - objcReadOnlyBufferVMAddr;
+    // __AUTH
+    if ( foundAuthenticatedFixups ) {
 
-    uint32_t totalSelectorRefCount = (uint32_t)_selectorStringsFromExecutables;
-    uint32_t totalClassDefCount    = 0;
-    uint32_t totalProtocolDefCount = 0;
-    for (DylibInfo& dylib : _sortedDylibs) {
-        dyld3::MachOAnalyzer::ObjCInfo info = dylib.input->mappedFile.mh->getObjCInfo();
-        totalSelectorRefCount   += info.selRefCount;
-        totalClassDefCount      += info.classDefCount;
-        totalProtocolDefCount   += info.protocolDefCount;
-    }
+        // align __AUTH region
+        addr = align((addr + _archLayout->sharedRegionPadding), _archLayout->sharedRegionAlignP2);
 
-    // now that shared cache coalesces all selector strings, use that better count
-    uint32_t coalescedSelectorCount = (uint32_t)_coalescedText.objcMethNames.stringsToOffsets.size();
-    if ( coalescedSelectorCount > totalSelectorRefCount )
-        totalSelectorRefCount = coalescedSelectorCount;
-    addr += align(computeReadOnlyObjC(totalSelectorRefCount, totalClassDefCount, totalProtocolDefCount), 14);
-    _objcReadOnlyBufferSizeAllocated = addr - objcReadOnlyBufferVMAddr;
+        Region region;
+        region.buffer               = (uint8_t*)_fullAllocatedBuffer + addr - _archLayout->sharedMemoryStart;
+        region.bufferSize           = 0;
+        region.sizeInUse            = 0;
+        region.unslidLoadAddress    = addr;
+        region.cacheFileOffset      = nextRegionFileOffset;
+        region.name                 = "__AUTH";
 
+        // layout all __AUTH_CONST segments
+        __block int authConstSegmentCount = 0;
+        for (DylibInfo& dylib : _sortedDylibs) {
+            __block uint64_t textSegVmAddr = 0;
+            __block std::set<uint32_t>& authSegmentIndices = authenticatedSegments[&dylib];
+            dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
+                if ( _options.platform == dyld3::Platform::watchOS_simulator && !_is64)
+                    return;
+                if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+                    textSegVmAddr = segInfo.vmAddr;
+                if ( segInfo.protections != (VM_PROT_READ | VM_PROT_WRITE) )
+                    return;
 
-    // align TEXT region end
-    uint64_t endTextAddress = align(addr, _archLayout->sharedRegionAlignP2);
-    _readExecuteRegion.bufferSize = endTextAddress - _readExecuteRegion.unslidLoadAddress;
-    _readExecuteRegion.sizeInUse  = _readExecuteRegion.bufferSize;
+                // We may have coalesced the sections at the end of this segment.  In that case, shrink the segment to remove them.
+                __block size_t sizeOfSections = 0;
+                __block bool foundCoalescedSection = false;
+                dylib.input->mappedFile.mh->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo &sectInfo, bool malformedSectionRange, bool &stopSection) {
+                    if (strcmp(sectInfo.segInfo.segName, segInfo.segName) != 0)
+                        return;
+                    if ( dylib.textCoalescer.sectionWasCoalesced(segInfo.segName, sectInfo.sectName)) {
+                        foundCoalescedSection = true;
+                    } else {
+                        sizeOfSections = sectInfo.sectAddr + sectInfo.sectSize - segInfo.vmAddr;
+                    }
+                });
+                if (!foundCoalescedSection)
+                    sizeOfSections = segInfo.sizeOfSections;
+
+                if ( strcmp(segInfo.segName, "__AUTH_CONST") == 0 ) {
+                    // We'll handle __AUTH_CONST here
+                } else if ( (strcmp(segInfo.segName, "__DATA_CONST") == 0) || (strcmp(segInfo.segName, "__OBJC_CONST") == 0) ) {
+                    // And we'll also handle __DATA_CONST/__OBJC_CONST which may contain authenticated pointers
+                    if ( authSegmentIndices.count(segInfo.segIndex) == 0 ) {
+                        // This __DATA_CONST doesn't contain authenticated pointers so was handled earlier
+                        return;
+                    } else {
+                        // We only moved this segment to __AUTH if it had content we didn't coalesce away
+                        if ( foundCoalescedSection && (sizeOfSections == 0) ) {
+                            // This __DATA_CONST doesn't contain authenticated pointers so was handled earlier
+                            return;
+                        }
+                    }
+                } else {
+                   // Not __AUTH_CONST or __DATA_CONST/__OBJC_CONST so skip this
+                   return;
+                }
+                ++authConstSegmentCount;
+                // Pack __AUTH_CONST segments
+                addr = align(addr, segInfo.p2align);
+                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)sizeOfSections);
+                uint64_t offsetInRegion = addr - region.unslidLoadAddress;
+                SegmentMappingInfo loc;
+                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+                loc.segName                = segInfo.segName;
+                loc.dstSegment             = region.buffer + offsetInRegion;
+                loc.dstCacheUnslidAddress  = addr;
+                loc.dstCacheFileOffset     = (uint32_t)(region.cacheFileOffset + offsetInRegion);
+                loc.dstCacheSegmentSize    = (uint32_t)sizeOfSections;
+                loc.dstCacheFileSize       = (uint32_t)copySize;
+                loc.copySegmentSize        = (uint32_t)copySize;
+                loc.srcSegmentIndex        = segInfo.segIndex;
+                dylib.cacheLocation.push_back(loc);
+                addr += loc.dstCacheSegmentSize;
+            });
+        }
 
+        // align __AUTH_CONST region end
+        addr = align(addr, _archLayout->sharedRegionAlignP2);
 
-    // assign __DATA* addresses
-    if ( _archLayout->sharedRegionsAreDiscontiguous )
-        addr = _archLayout->sharedMemoryStart + 0x60000000;
-    else
-        addr = align((addr + _archLayout->sharedRegionPadding), _archLayout->sharedRegionAlignP2);
-    _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 (DylibInfo& dylib : _sortedDylibs) {
-        __block uint64_t textSegVmAddr = 0;
-       dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
-           if ( _options.platform == dyld3::Platform::watchOS_simulator)
-               return;
-            if ( strcmp(segInfo.segName, "__TEXT") == 0 )
-                textSegVmAddr = segInfo.vmAddr;
-            if ( segInfo.protections != (VM_PROT_READ | VM_PROT_WRITE) )
-                return;
-            if ( strcmp(segInfo.segName, "__DATA_CONST") != 0 )
-                return;
-            ++dataConstSegmentCount;
-            // Pack __DATA_CONST segments
-            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.dstCacheFileSize       = (uint32_t)copySize;
-            loc.copySegmentSize        = (uint32_t)copySize;
-            loc.srcSegmentIndex        = segInfo.segIndex;
-            dylib.cacheLocation.push_back(loc);
-            addr += loc.dstCacheSegmentSize;
-        });
+        // __AUTH_DIRTY.  Note this is really __DATA_DIRTY as we don't generate an __AUTH_DIRTY in ld64
+        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 ( _options.platform == dyld3::Platform::watchOS_simulator && !_is64)
+                    return;
+                if ( strcmp(segInfo.segName, "__TEXT") == 0 )
+                    textSegVmAddr = segInfo.vmAddr;
+                if ( segInfo.protections != (VM_PROT_READ | VM_PROT_WRITE) )
+                    return;
+                if ( strcmp(segInfo.segName, "__DATA_DIRTY") != 0 )
+                    return;
+                // Pack __AUTH_DIRTY segments
+                addr = align(addr, segInfo.p2align);
+                size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
+                uint64_t offsetInRegion = addr - region.unslidLoadAddress;
+                SegmentMappingInfo loc;
+                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+                loc.segName                = segInfo.segName;
+                loc.dstSegment             = region.buffer + offsetInRegion;
+                loc.dstCacheUnslidAddress  = addr;
+                loc.dstCacheFileOffset     = (uint32_t)(region.cacheFileOffset + offsetInRegion);
+                loc.dstCacheSegmentSize    = (uint32_t)segInfo.sizeOfSections;
+                loc.dstCacheFileSize       = (uint32_t)copySize;
+                loc.copySegmentSize        = (uint32_t)copySize;
+                loc.srcSegmentIndex        = segInfo.segIndex;
+                dylib.cacheLocation.push_back(loc);
+                addr += loc.dstCacheSegmentSize;
+            });
+        }
+
+        // align __AUTH_DIRTY region end
+        addr = align(addr, _archLayout->sharedRegionAlignP2);
+
+        // layout all __AUTH segments (and other r/w non-dirty, non-const, non-auth) segments
+        for (DylibInfo& dylib : _sortedDylibs) {
+            __block uint64_t textSegVmAddr = 0;
+            __block std::set<uint32_t>& authSegmentIndices = authenticatedSegments[&dylib];
+           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 ( _options.platform != dyld3::Platform::watchOS_simulator || _is64) {
+                    if ( strcmp(segInfo.segName, "__AUTH_CONST") == 0 )
+                        return;
+                }
+               if ( strncmp(segInfo.segName, "__AUTH", 6) == 0 ) {
+                   // We'll handle __AUTH* here
+               } else {
+                   // And we'll also handle __DATA* which contains authenticated pointers
+                   if ( authSegmentIndices.count(segInfo.segIndex) == 0 ) {
+                       // This __DATA doesn't contain authenticated pointers so was handled earlier
+                       return;
+                   }
+                   if ( _options.platform != dyld3::Platform::watchOS_simulator || _is64) {
+                       if ( strcmp(segInfo.segName, "__DATA_CONST") == 0 )
+                           return;
+                       if ( strcmp(segInfo.segName, "__DATA_DIRTY") == 0 )
+                           return;
+                       if ( strcmp(segInfo.segName, "__OBJC_CONST") == 0 )
+                           return;
+                   }
+               }
+                bool forcePageAlignedData = false;
+                if (_options.platform == dyld3::Platform::macOS) {
+                    forcePageAlignedData = dylib.input->mappedFile.mh->hasUnalignedPointerFixups();
+                    //if ( forcePageAlignedData )
+                    //    warning("unaligned pointer in %s\n", dylib.input->mappedFile.runtimePath.c_str());
+                }
+                if ( (authConstSegmentCount > 10) && !forcePageAlignedData ) {
+                    // Pack __AUTH segments only if we also have __AUTH_CONST segments
+                    addr = align(addr, segInfo.p2align);
+                }
+                else {
+                    // Keep __AUTH segments 4K or more aligned
+                    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 - region.unslidLoadAddress;
+                SegmentMappingInfo loc;
+                loc.srcSegment             = (uint8_t*)dylib.input->mappedFile.mh + segInfo.vmAddr - textSegVmAddr;
+                loc.segName                = segInfo.segName;
+                loc.dstSegment             = region.buffer + offsetInRegion;
+                loc.dstCacheUnslidAddress  = addr;
+                loc.dstCacheFileOffset     = (uint32_t)(region.cacheFileOffset + offsetInRegion);
+                loc.dstCacheSegmentSize    = (uint32_t)segInfo.sizeOfSections;
+                loc.dstCacheFileSize       = (uint32_t)copySize;
+                loc.copySegmentSize        = (uint32_t)copySize;
+                loc.srcSegmentIndex        = segInfo.segIndex;
+                dylib.cacheLocation.push_back(loc);
+                addr += loc.dstCacheSegmentSize;
+            });
+        }
+
+        // reserve space for objc r/w optimization tables
+        _objcReadWriteBufferSizeAllocated = align(computeReadWriteObjC((uint32_t)_sortedDylibs.size(), totalProtocolDefCount), 14);
+        addr = align(addr, 4); // objc r/w section contains pointer and must be at least pointer align
+        _objcReadWriteBuffer = region.buffer + (addr - region.unslidLoadAddress);
+        addr += _objcReadWriteBufferSizeAllocated;
+
+        // align DATA region end
+        addr = align(addr, _archLayout->sharedRegionAlignP2);
+        uint64_t endDataAddress = addr;
+        region.bufferSize   = endDataAddress - region.unslidLoadAddress;
+        region.sizeInUse    = region.bufferSize;
+
+        _dataRegions.push_back(region);
+        nextRegionFileOffset = region.cacheFileOffset + region.sizeInUse;
     }
 
-    // layout all __DATA segments (and other r/w non-dirty, non-const) segments
+#if 0
+    // Sanity check that we didn't put the same segment in 2 different ranges
     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) )
+        __block std::unordered_set<uint64_t> seenSegmentIndices;
+        for (SegmentMappingInfo& segmentInfo : dylib.cacheLocation) {
+            if ( seenSegmentIndices.count(segmentInfo.srcSegmentIndex) != 0 ) {
+                _diagnostics.error("%s segment %s was duplicated in layout",
+                                   dylib.input->mappedFile.mh->installName(), segmentInfo.segName);
                 return;
-            if ( _options.platform != dyld3::Platform::watchOS_simulator) {
-                if ( strcmp(segInfo.segName, "__DATA_CONST") == 0 )
-                    return;
-                if ( strcmp(segInfo.segName, "__DATA_DIRTY") == 0 )
-                    return;
-            }
-            bool forcePageAlignedData = false;
-            if (_options.platform == dyld3::Platform::macOS) {
-                forcePageAlignedData = dylib.input->mappedFile.mh->hasUnalignedPointerFixups();
-                //if ( forcePageAlignedData )
-                //    warning("unaligned pointer in %s\n", dylib.input->mappedFile.runtimePath.c_str());
-            }
-            if ( (dataConstSegmentCount > 10) && !forcePageAlignedData ) {
-                // Pack __DATA segments only if we also have __DATA_CONST segments
-                addr = align(addr, segInfo.p2align);
             }
-            else {
-                // Keep __DATA segments 4K or more aligned
-                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 - _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.dstCacheFileSize       = (uint32_t)copySize;
-            loc.copySegmentSize        = (uint32_t)copySize;
-            loc.srcSegmentIndex        = segInfo.segIndex;
-            dylib.cacheLocation.push_back(loc);
-            addr += loc.dstCacheSegmentSize;
-        });
+            seenSegmentIndices.insert(segmentInfo.srcSegmentIndex);
+        }
     }
+#endif
+}
 
-    // 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());
+void SharedCacheBuilder::assignSegmentAddresses()
+{
+    // calculate size of header info and where first dylib's mach_header should start
+    size_t startOffset = sizeof(dyld_cache_header) + DyldSharedCache::MaxMappings * sizeof(dyld_cache_mapping_info);
+    startOffset += DyldSharedCache::MaxMappings * sizeof(dyld_cache_mapping_and_slide_info);
+    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);
 
-        // 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);
-    for (size_t i=0; i < dylibCount; ++i) {
-        DylibInfo& dylib  = _sortedDylibs[dirtyDataSortIndexes[i]];
+    // HACK!: Rebase v4 assumes that values below 0x8000 are not pointers (encoding as offsets from the cache header).
+    // If using a minimal cache, we need to pad out the cache header to make sure a pointer doesn't fall within that range
+#if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k
+    if ( _options.cacheSupportsASLR && !_archLayout->is64 ) {
+        if ( _archLayout->pointerDeltaMask == 0xC0000000 )
+            startOffset = std::max(startOffset, (size_t)0x8000);
+    }
+#endif
+
+    // assign TEXT segment addresses
+    _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
+    for (DylibInfo& dylib : _sortedDylibs) {
         __block uint64_t textSegVmAddr = 0;
         dylib.input->mappedFile.mh->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& segInfo, bool& stop) {
-            if ( _options.platform == dyld3::Platform::watchOS_simulator)
-                return;
             if ( strcmp(segInfo.segName, "__TEXT") == 0 )
                 textSegVmAddr = segInfo.vmAddr;
-            if ( segInfo.protections != (VM_PROT_READ | VM_PROT_WRITE) )
-                return;
-            if ( strcmp(segInfo.segName, "__DATA_DIRTY") != 0 )
+            if ( segInfo.protections != (VM_PROT_READ | VM_PROT_EXECUTE) )
                 return;
-            // Pack __DATA_DIRTY segments
-            addr = align(addr, segInfo.p2align);
-            size_t copySize = std::min((size_t)segInfo.fileSize, (size_t)segInfo.sizeOfSections);
-            uint64_t offsetInRegion = addr - _readWriteRegion.unslidLoadAddress;
+            // We may have coalesced the sections at the end of this segment.  In that case, shrink the segment to remove them.
+            __block size_t sizeOfSections = 0;
+            __block bool foundCoalescedSection = false;
+            dylib.input->mappedFile.mh->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo &sectInfo, bool malformedSectionRange, bool &stopSection) {
+                if (strcmp(sectInfo.segInfo.segName, segInfo.segName) != 0)
+                    return;
+                if ( dylib.textCoalescer.sectionWasCoalesced(segInfo.segName, sectInfo.sectName)) {
+                    foundCoalescedSection = true;
+                } else {
+                    sizeOfSections = sectInfo.sectAddr + sectInfo.sectSize - segInfo.vmAddr;
+                }
+            });
+            if (!foundCoalescedSection)
+                sizeOfSections = segInfo.sizeOfSections;
+
+            // Keep __TEXT segments 4K or more aligned
+            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             = _readWriteRegion.buffer + offsetInRegion;
+            loc.dstSegment             = _readExecuteRegion.buffer + offsetInRegion;
             loc.dstCacheUnslidAddress  = addr;
-            loc.dstCacheFileOffset     = (uint32_t)(_readWriteRegion.cacheFileOffset + offsetInRegion);
-            loc.dstCacheSegmentSize    = (uint32_t)segInfo.sizeOfSections;
-            loc.dstCacheFileSize       = (uint32_t)copySize;
-            loc.copySegmentSize        = (uint32_t)copySize;
+            loc.dstCacheFileOffset     = (uint32_t)offsetInRegion;
+            loc.dstCacheSegmentSize    = (uint32_t)align(sizeOfSections, 12);
+            loc.dstCacheFileSize       = (uint32_t)align(sizeOfSections, 12);
+            loc.copySegmentSize        = (uint32_t)sizeOfSections;
             loc.srcSegmentIndex        = segInfo.segIndex;
             dylib.cacheLocation.push_back(loc);
             addr += loc.dstCacheSegmentSize;
         });
     }
 
-    // reserve space for objc r/w optimization tables
-    _objcReadWriteBufferSizeAllocated = align(computeReadWriteObjC((uint32_t)_sortedDylibs.size(), totalProtocolDefCount), 14);
-    addr = align(addr, 4); // objc r/w section contains pointer and must be at least pointer align
-    _objcReadWriteBuffer = _readWriteRegion.buffer + (addr - _readWriteRegion.unslidLoadAddress);
-    addr += _objcReadWriteBufferSizeAllocated;
+    // reserve space for objc optimization tables and deduped strings
+    uint64_t objcReadOnlyBufferVMAddr = addr;
+    _objcReadOnlyBuffer = _readExecuteRegion.buffer + (addr - _readExecuteRegion.unslidLoadAddress);
+
+    // First the strings as we'll fill in the objc tables later in the optimizer
+    for (const char* section: CacheCoalescedText::SupportedSections) {
+        CacheCoalescedText::StringSection& cacheStringSection = _coalescedText.getSectionData(section);
+        cacheStringSection.bufferAddr = _readExecuteRegion.buffer + (addr - _readExecuteRegion.unslidLoadAddress);
+        cacheStringSection.bufferVMAddr = addr;
+        addr += cacheStringSection.bufferSize;
+    }
+
+    addr = align(addr, 14);
+    _objcReadOnlyBufferSizeUsed = addr - objcReadOnlyBufferVMAddr;
+
+    uint32_t totalSelectorRefCount = (uint32_t)_selectorStringsFromExecutables;
+    uint32_t totalClassDefCount    = 0;
+    uint32_t totalProtocolDefCount = 0;
+    for (DylibInfo& dylib : _sortedDylibs) {
+        dyld3::MachOAnalyzer::ObjCInfo info = dylib.input->mappedFile.mh->getObjCInfo();
+        totalSelectorRefCount   += info.selRefCount;
+        totalClassDefCount      += info.classDefCount;
+        totalProtocolDefCount   += info.protocolDefCount;
+    }
+
+    // now that shared cache coalesces all selector strings, use that better count
+    uint32_t coalescedSelectorCount = (uint32_t)_coalescedText.objcMethNames.stringsToOffsets.size();
+    if ( coalescedSelectorCount > totalSelectorRefCount )
+        totalSelectorRefCount = coalescedSelectorCount;
+    addr += align(computeReadOnlyObjC(totalSelectorRefCount, totalClassDefCount, totalProtocolDefCount), 14);
+
+    size_t impCachesSize = _impCachesBuilder->totalIMPCachesSize();
+    size_t alignedImpCachesSize = align(impCachesSize, 14);
+    _diagnostics.verbose("Reserving %zd bytes for IMP caches (aligned to %zd)\n", impCachesSize, alignedImpCachesSize);
+    addr += alignedImpCachesSize;
+
+    _objcReadOnlyBufferSizeAllocated = addr - objcReadOnlyBufferVMAddr;
+
+    // align TEXT region end
+    uint64_t endTextAddress = align(addr, _archLayout->sharedRegionAlignP2);
+    _readExecuteRegion.bufferSize = endTextAddress - _readExecuteRegion.unslidLoadAddress;
+    _readExecuteRegion.sizeInUse  = _readExecuteRegion.bufferSize;
+
+
+    // assign __DATA* addresses
+    if ( _archLayout->sharedRegionsAreDiscontiguous )
+        addr = DISCONTIGUOUS_RW;
+    else
+        addr = align((addr + _archLayout->sharedRegionPadding), _archLayout->sharedRegionAlignP2);
 
-    // align DATA region end
-    uint64_t endDataAddress = align(addr, _archLayout->sharedRegionAlignP2);
-    _readWriteRegion.bufferSize   = endDataAddress - _readWriteRegion.unslidLoadAddress;
-    _readWriteRegion.sizeInUse    = _readWriteRegion.bufferSize;
+    // __DATA*
+    assignMultipleDataSegmentAddresses(addr, totalProtocolDefCount);
 
     // start read-only region
     if ( _archLayout->sharedRegionsAreDiscontiguous )
-        addr = _archLayout->sharedMemoryStart + 0xA0000000;
+        addr = DISCONTIGUOUS_RO;
     else
         addr = align((addr + _archLayout->sharedRegionPadding), _archLayout->sharedRegionAlignP2);
     _readOnlyRegion.buffer               = (uint8_t*)_fullAllocatedBuffer + addr - _archLayout->sharedMemoryStart;
     _readOnlyRegion.bufferSize           = 0;
     _readOnlyRegion.sizeInUse            = 0;
     _readOnlyRegion.unslidLoadAddress    = addr;
-    _readOnlyRegion.cacheFileOffset      = _readWriteRegion.cacheFileOffset + _readWriteRegion.sizeInUse;
+    _readOnlyRegion.cacheFileOffset      = lastDataRegion()->cacheFileOffset + lastDataRegion()->sizeInUse;
+
 
     // reserve space for kernel ASLR slide info at start of r/o region
     if ( _options.cacheSupportsASLR ) {
@@ -1749,41 +2267,46 @@ void SharedCacheBuilder::assignSegmentAddresses()
         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 + 0x4000, _archLayout->sharedRegionAlignP2);
-        _slideInfoFileOffset = _readOnlyRegion.cacheFileOffset;
-        addr += _slideInfoBufferSizeAllocated;
+        // We need one slide info header per data region, plus enough space for that regions pages
+        // Each region will also be padded to a page-size so that the kernel can wire it.
+        for (Region& region : _dataRegions) {
+            uint64_t offsetInRegion = addr - _readOnlyRegion.unslidLoadAddress;
+            region.slideInfoBuffer = _readOnlyRegion.buffer + offsetInRegion;
+            region.slideInfoBufferSizeAllocated = align(slideInfoSize + (region.sizeInUse/4096) * _archLayout->slideInfoBytesPerPage + 0x4000, _archLayout->sharedRegionAlignP2);
+            region.slideInfoFileOffset = _readOnlyRegion.cacheFileOffset + offsetInRegion;
+            addr += region.slideInfoBufferSizeAllocated;
+        }
     }
 
     // layout all read-only (but not LINKEDIT) segments
-    if ( _archLayout->textAndDataMaxSize == 0 ) {
-        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(segInfo.segName, "__LINKEDIT") == 0 )
-                    return;
+    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(segInfo.segName, "__LINKEDIT") == 0 )
+                return;
 
-                // Keep segments segments 4K or more aligned
-                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.dstCacheFileSize       = (uint32_t)segInfo.sizeOfSections;
-                loc.copySegmentSize        = (uint32_t)segInfo.sizeOfSections;
-                loc.srcSegmentIndex        = segInfo.segIndex;
-                dylib.cacheLocation.push_back(loc);
-                addr += loc.dstCacheSegmentSize;
-            });
-        }
+            // Keep segments segments 4K or more aligned
+            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.dstCacheFileSize       = (uint32_t)segInfo.sizeOfSections;
+            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), aligned to 16KB
     addr = align(addr, 14);
     _nonLinkEditReadOnlySize =  addr - _readOnlyRegion.unslidLoadAddress;
@@ -1816,12 +2339,13 @@ void SharedCacheBuilder::assignSegmentAddresses()
     }
 
     // align r/o region end
-    uint64_t endReadOnlyAddress = align(addr, _archLayout->sharedRegionAlignP2);
-    _readOnlyRegion.bufferSize  = endReadOnlyAddress - _readOnlyRegion.unslidLoadAddress;
+    addr = align(addr, _archLayout->sharedRegionAlignP2);
+    uint64_t endReadOnlyAddress = addr;
+    _readOnlyRegion.bufferSize  = endReadOnlyAddress - _readOnlyRegion.unslidLoadAddress + 0x100000;
     _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, "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
@@ -1832,30 +2356,62 @@ void SharedCacheBuilder::assignSegmentAddresses()
     }
 }
 
-static dyld_cache_patchable_location makePatchLocation(size_t cacheOff, uint64_t ad) {
-    int64_t signedAddend = (int64_t)ad;
-    assert(((signedAddend << 52) >> 52) == signedAddend);
-    dyld_cache_patchable_location patch;
-    patch.cacheOffset           = cacheOff;
-    patch.addend                = ad;
-    patch.authenticated         = 0;
-    patch.usesAddressDiversity  = 0;
-    patch.key                   = 0;
-    patch.discriminator         = 0;
-    return patch;
+// Return the total size of the data regions, including padding between them.
+// Note this assumes they are contiguous, or that we don't care about including
+// additional space between them.
+uint64_t SharedCacheBuilder::dataRegionsTotalSize() const {
+    const Region* firstRegion = nullptr;
+    const Region* lastRegion = nullptr;
+    for (const Region& region : _dataRegions) {
+        if ( (firstRegion == nullptr) || (region.buffer < firstRegion->buffer) )
+            firstRegion = &region;
+        if ( (lastRegion == nullptr) || (region.buffer > lastRegion->buffer) )
+            lastRegion = &region;
+    }
+    return (lastRegion->buffer - firstRegion->buffer) + lastRegion->sizeInUse;
 }
 
-static dyld_cache_patchable_location makePatchLocation(size_t cacheOff, uint64_t ad,
-                                                       dyld3::MachOLoaded::ChainedFixupPointerOnDisk authInfo) {
-    int64_t signedAddend = (int64_t)ad;
-    assert(((signedAddend << 52) >> 52) == signedAddend);
-    dyld_cache_patchable_location patch;
+
+// Return the total size of the data regions, excluding padding between them
+uint64_t SharedCacheBuilder::dataRegionsSizeInUse() const {
+    size_t size = 0;
+    for (const Region& dataRegion : _dataRegions)
+        size += dataRegion.sizeInUse;
+    return size;
+}
+
+// Return the earliest data region by address
+const CacheBuilder::Region* SharedCacheBuilder::firstDataRegion() const {
+    const Region* firstRegion = nullptr;
+    for (const Region& region : _dataRegions) {
+        if ( (firstRegion == nullptr) || (region.buffer < firstRegion->buffer) )
+            firstRegion = &region;
+    }
+    return firstRegion;
+}
+
+// Return the lateset data region by address
+const CacheBuilder::Region* SharedCacheBuilder::lastDataRegion() const {
+    const Region* lastRegion = nullptr;
+    for (const Region& region : _dataRegions) {
+        if ( (lastRegion == nullptr) || (region.buffer > lastRegion->buffer) )
+            lastRegion = &region;
+    }
+    return lastRegion;
+}
+static dyld_cache_patchable_location makePatchLocation(size_t cacheOff, dyld3::MachOAnalyzerSet::PointerMetaData pmd, uint64_t addend) {
+     dyld_cache_patchable_location patch;
     patch.cacheOffset           = cacheOff;
-    patch.addend                = ad;
-    patch.authenticated         = authInfo.arm64e.authBind.auth;
-    patch.usesAddressDiversity  = authInfo.arm64e.authBind.addrDiv;
-    patch.key                   = authInfo.arm64e.authBind.key;
-    patch.discriminator         = authInfo.arm64e.authBind.diversity;
+    patch.high7                 = pmd.high8 >> 1;
+    patch.addend                = addend;
+    patch.authenticated         = pmd.authenticated;
+    patch.usesAddressDiversity  = pmd.usesAddrDiversity;
+    patch.key                   = pmd.key;
+    patch.discriminator         = pmd.diversity;
+    // check for truncations
+    assert(patch.cacheOffset == cacheOff);
+    assert(patch.addend == addend);
+    assert((patch.high7 << 1) == pmd.high8);
     return patch;
 }
 
@@ -1868,6 +2424,9 @@ void SharedCacheBuilder::buildImageArray(std::vector<DyldSharedCache::FileAlias>
     __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) {
+        const dyld3::MachOLoaded* ml = (dyld3::MachOLoaded*)mh;
+        if ( !_someDylibsUsedChainedFixups && ml->hasChainedFixups() )
+            _someDylibsUsedChainedFixups = true;
         uint64_t mtime;
         uint64_t inode;
         cache->getIndexedImageEntry((uint32_t)dylibInfos.size(), mtime, inode);
@@ -1878,7 +2437,7 @@ void SharedCacheBuilder::buildImageArray(std::vector<DyldSharedCache::FileAlias>
         entry.fileInfo.inode        = inode;
         entry.fileInfo.mtime        = mtime;
         dylibInfos.push_back(entry);
-        imageNumToML[(dyld3::closure::ImageNum)(dylibInfos.size())] = (dyld3::MachOLoaded*)mh;
+        imageNumToML[(dyld3::closure::ImageNum)(dylibInfos.size())] = ml;
     });
 
     // Convert symlinks from STL to simple char pointers.
@@ -1887,271 +2446,89 @@ void SharedCacheBuilder::buildImageArray(std::vector<DyldSharedCache::FileAlias>
     for (const auto& alias : aliases)
         dylibAliases.push_back({ alias.realPath.c_str(), alias.aliasPath.c_str() });
 
-    dyld3::closure::ClosureBuilder::CacheDylibsBindingHandlers handlers;
-
-    handlers.chainedBind = ^(dyld3::closure::ImageNum, const dyld3::MachOLoaded* imageLoadAddress,
-                             const  dyld_chained_starts_in_image* starts,
-                             const dyld3::Array<dyld3::closure::Image::ResolvedSymbolTarget>& targets,
-                             const dyld3::Array<dyld3::closure::ClosureBuilder::ResolvedTargetInfo>& targetInfos) {
-        _someDylibsUsedChainedFixups = true;
-        imageLoadAddress->forEachFixupInAllChains(_diagnostics, starts, true, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
-            uint64_t                                                  offsetInCache;
-            dyld3::closure::Image::ResolvedSymbolTarget               target;
-            const dyld3::closure::ClosureBuilder::ResolvedTargetInfo* targetInfo;
-            dyld3::MachOLoaded::ChainedFixupPointerOnDisk             orgValue;
-            uint64_t                                                  targetVMAddr;
-            uint64_t                                                  addend;
-            bool                                                      pointerIntoCache = true;
-            switch (segInfo->pointer_format) {
-                case DYLD_CHAINED_PTR_ARM64E:
-                    orgValue = *fixupLoc;
-                    if ( fixupLoc->arm64e.bind.bind ) {
-                        target     = targets[fixupLoc->arm64e.bind.ordinal];
-                        targetInfo = &targetInfos[fixupLoc->arm64e.bind.ordinal];
-                        switch ( target.sharedCache.kind ) {
-                            case dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache:
-                                offsetInCache = target.sharedCache.offset - targetInfo->addend;
-                                _dylibToItsExports[targetInfo->foundInDylib].insert(offsetInCache);
-                                _exportsToName[offsetInCache] = targetInfo->foundSymbolName;
-                                addend = targetInfo->addend;
-                                {
-                                    uint16_t top16 = addend >> 48;
-                                    uint8_t  top8  = addend >> 56;
-                                    // if top byte is non-zero and this is not a negative addend, pull off high8
-                                    if ( (top16 != 0xFFFF) && (top8 != 0) ) {
-                                        _aslrTracker.setHigh8(fixupLoc, top8);
-                                        addend &= 0x00FFFFFFFFFFFFFFULL;
-                                    }
-                                }
-                                targetVMAddr = _archLayout->sharedMemoryStart + target.sharedCache.offset;
-                                if ( fixupLoc->arm64e.authBind.auth ) {
-                                    // store auth data in side table
-                                    _aslrTracker.setAuthData(fixupLoc, fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv, fixupLoc->arm64e.authBind.key);
-                                    _exportsToUses[offsetInCache].push_back(makePatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, addend, *fixupLoc));
-                                }
-                                else {
-                                    _exportsToUses[offsetInCache].push_back(makePatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, addend));
-                                    // plain binds can have addend in chain
-                                    targetVMAddr += fixupLoc->arm64e.bind.addend;
-                                }
-                                // change location from a chain ptr into a raw pointer to the target vmaddr
-                                fixupLoc->raw64 = targetVMAddr;
-                                _aslrTracker.add(fixupLoc);
-                                break;
-                            case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute:
-                                fixupLoc->raw64 = target.absolute.value;
-                                pointerIntoCache = false;
-                                // don't record absolute targets for ASLR
-                                _aslrTracker.remove(fixupLoc);
-                                if ( (targetInfo->libOrdinal > 0) && (targetInfo->libOrdinal <= (int)(imageLoadAddress->dependentDylibCount())) ) {
-                                    _missingWeakImports[fixupLoc] = imageLoadAddress->dependentDylibLoadPath(targetInfo->libOrdinal - 1);
-                                }
-                               break;
-                            default:
-                                assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache");
-                        }
-                    }
-                    else {
-                        // convert rebase chain entry to raw pointer to target vmaddr
-                        if ( fixupLoc->arm64e.rebase.auth ) {
-                            // store auth data in side table
-                            _aslrTracker.setAuthData(fixupLoc, fixupLoc->arm64e.authRebase.diversity, fixupLoc->arm64e.authRebase.addrDiv, fixupLoc->arm64e.authRebase.key);
-                            targetVMAddr = fixupLoc->arm64e.authRebase.target;
-                            fixupLoc->raw64 = targetVMAddr;
-                        }
-                        else {
-                            targetVMAddr = fixupLoc->arm64e.rebase.target;
-                            if ( targetVMAddr == CacheBuilder::kRebaseTargetInSideTableArm64e ) {
-                                // target was stored in side table
-                                _aslrTracker.hasRebaseTarget64(fixupLoc, &targetVMAddr);
-                            }
-                            // store high8 in side table
-                            if ( fixupLoc->arm64e.rebase.high8 )
-                                _aslrTracker.setHigh8(fixupLoc, fixupLoc->arm64e.rebase.high8);
-                            fixupLoc->raw64 = targetVMAddr;
-                        }
-                    }
-                    if ( pointerIntoCache ) {
-                        assert(fixupLoc->raw64 > _readExecuteRegion.unslidLoadAddress);
-                        assert(fixupLoc->raw64 < _readOnlyRegion.unslidLoadAddress+_readOnlyRegion.sizeInUse);
-                    }
-                    break;
-                case DYLD_CHAINED_PTR_64:
-                    if ( fixupLoc->generic64.bind.bind ) {
-                        target     = targets[fixupLoc->generic64.bind.ordinal];
-                        targetInfo = &targetInfos[fixupLoc->generic64.bind.ordinal];
-                        switch ( target.sharedCache.kind ) {
-                            case dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache:
-                                offsetInCache = target.sharedCache.offset - targetInfo->addend;
-                                _dylibToItsExports[targetInfo->foundInDylib].insert(offsetInCache);
-                                _exportsToName[offsetInCache] = targetInfo->foundSymbolName;
-                                addend = targetInfo->addend + fixupLoc->generic64.bind.addend;
-                                {
-                                    uint16_t top16 = addend >> 48;
-                                    uint8_t  top8  = addend >> 56;
-                                    // if top byte is non-zero and this is not a negative addend, pull off high8
-                                    if ( (top16 != 0xFFFF) && (top8 != 0) ) {
-                                        _aslrTracker.setHigh8(fixupLoc, top8);
-                                        addend &= 0x00FFFFFFFFFFFFFFULL;
-                                    }
-                                }
-                                // turn this bind into a flat vmaddr
-                                fixupLoc->raw64 = _archLayout->sharedMemoryStart + target.sharedCache.offset + fixupLoc->generic64.bind.addend;
-                                _exportsToUses[offsetInCache].push_back(makePatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, addend));
-                                _aslrTracker.add(fixupLoc);
-                                break;
-                            case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute:
-                                fixupLoc->raw64 = target.absolute.value;
-                                pointerIntoCache = false;
-                                // don't record absolute targets for ASLR
-                                _aslrTracker.remove(fixupLoc);
-                                if ( (targetInfo->libOrdinal > 0) && (targetInfo->libOrdinal <= (int)(imageLoadAddress->dependentDylibCount())) ) {
-                                    _missingWeakImports[fixupLoc] = imageLoadAddress->dependentDylibLoadPath(targetInfo->libOrdinal - 1);
-                                }
-                                break;
-                            default:
-                                assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache");
-                        }
+    typedef dyld3::MachOAnalyzerSet::FixupTarget        FixupTarget;
+    typedef dyld3::MachOAnalyzerSet::PointerMetaData    PointerMetaData;
+
+    dyld3::closure::ClosureBuilder::DylibFixupHandler handler = ^(const dyld3::MachOLoaded* fixupIn, uint64_t fixupLocRuntimeOffset,
+                                                                  PointerMetaData pmd, const FixupTarget& target) {
+        uint8_t*  fixupLoc = (uint8_t*)fixupIn + fixupLocRuntimeOffset;
+        uint32_t* fixupLoc32 = (uint32_t*)fixupLoc;
+        uint64_t* fixupLoc64 = (uint64_t*)fixupLoc;
+        uint64_t  targetSymbolOffsetInCache;
+        switch ( target.kind ) {
+            case FixupTarget::Kind::rebase:
+                // rebasing already done in AdjustDylibSegments, but if input dylib uses chained fixups, target might not fit
+                if ( _archLayout->is64 ) {
+                    if ( pmd.authenticated )
+                        _aslrTracker.setAuthData(fixupLoc, pmd.diversity, pmd.usesAddrDiversity, pmd.key);
+                    if ( pmd.high8 )
+                        _aslrTracker.setHigh8(fixupLoc, pmd.high8);
+                    uint64_t targetVmAddr;
+                    if ( _aslrTracker.hasRebaseTarget64(fixupLoc, &targetVmAddr) )
+                        *fixupLoc64 = targetVmAddr;
+                    else
+                        *fixupLoc64 = (uint8_t*)target.foundInImage._mh - _readExecuteRegion.buffer + target.offsetInImage + _readExecuteRegion.unslidLoadAddress;
+                }
+                else {
+                    uint32_t targetVmAddr;
+                    assert(_aslrTracker.hasRebaseTarget32(fixupLoc, &targetVmAddr) && "32-bit archs always store target in side table");
+                    *fixupLoc32 = targetVmAddr;
+                }
+                break;
+            case FixupTarget::Kind::bindAbsolute:
+                if ( _archLayout->is64 )
+                    *fixupLoc64 = target.offsetInImage;
+                else
+                    *fixupLoc32 = (uint32_t)(target.offsetInImage);
+                // don't record absolute targets for ASLR
+                _aslrTracker.remove(fixupLoc);
+                break;
+            case FixupTarget::Kind::bindToImage:
+                targetSymbolOffsetInCache = (uint8_t*)target.foundInImage._mh - _readExecuteRegion.buffer + target.offsetInImage - target.addend;
+                if ( !target.weakCoalesced || !_aslrTracker.has(fixupLoc) ) {
+                    // this handler is called a second time for weak_bind info, which we ignore when building cache
+                    _aslrTracker.add(fixupLoc);
+                    if ( _archLayout->is64 ) {
+                        if ( pmd.high8 )
+                            _aslrTracker.setHigh8(fixupLoc, pmd.high8);
+                        if ( pmd.authenticated )
+                            _aslrTracker.setAuthData(fixupLoc, pmd.diversity, pmd.usesAddrDiversity, pmd.key);
+                        *fixupLoc64 = _archLayout->sharedMemoryStart + targetSymbolOffsetInCache + target.addend;
                     }
                     else {
-                        // convert rebase chain entry to raw pointer to target vmaddr
-                        targetVMAddr = fixupLoc->generic64.rebase.target;
-                        if ( targetVMAddr == CacheBuilder::kRebaseTargetInSideTableArm64 ) {
-                            // target was stored in side table
-                            _aslrTracker.hasRebaseTarget64(fixupLoc, &targetVMAddr);
-                        }
-                        // store high8 in side table
-                        if ( fixupLoc->generic64.rebase.high8 )
-                            _aslrTracker.setHigh8(fixupLoc, fixupLoc->generic64.rebase.high8);
-                        fixupLoc->raw64 = targetVMAddr;
-                    }
-                    if ( pointerIntoCache ) {
-                        assert(fixupLoc->raw64 > _readExecuteRegion.unslidLoadAddress);
-                        assert(fixupLoc->raw64 < _readOnlyRegion.unslidLoadAddress+_readOnlyRegion.sizeInUse);
-                    }
-                    break;
-                case DYLD_CHAINED_PTR_32:
-                    if ( fixupLoc->generic32.bind.bind ) {
-                        // turn this bind into a flat vmaddr pointer
-                        target     = targets[fixupLoc->generic32.bind.ordinal];
-                        targetInfo = &targetInfos[fixupLoc->generic32.bind.ordinal];
-                        switch ( target.sharedCache.kind ) {
-                            case dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache:
-                                offsetInCache = target.sharedCache.offset - targetInfo->addend;
-                                _dylibToItsExports[targetInfo->foundInDylib].insert(offsetInCache);
-                                _exportsToName[offsetInCache] = targetInfo->foundSymbolName;
-                                fixupLoc->raw32 = (uint32_t)(_archLayout->sharedMemoryStart + target.sharedCache.offset + fixupLoc->generic32.bind.addend);
-                                _exportsToUses[offsetInCache].push_back(makePatchLocation((uint8_t*)fixupLoc - _readExecuteRegion.buffer, targetInfo->addend+fixupLoc->generic32.bind.addend));
-                                _aslrTracker.add(fixupLoc);
-                                break;
-                            case dyld3::closure::Image::ResolvedSymbolTarget::kindAbsolute:
-                                fixupLoc->raw32 = (uint32_t)target.absolute.value;
-                                pointerIntoCache = false;
-                                // don't record absolute targets for ASLR
-                                _aslrTracker.remove(fixupLoc);
-                                if ( (targetInfo->libOrdinal > 0) && (targetInfo->libOrdinal <= (int)(imageLoadAddress->dependentDylibCount())) ) {
-                                    _missingWeakImports[fixupLoc] = imageLoadAddress->dependentDylibLoadPath(targetInfo->libOrdinal - 1);
-                                }
-                                break;
-                            default:
-                                assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache");
-                        }
-                    }
-                    else if ( fixupLoc->generic32.rebase.target == CacheBuilder::kRebaseTargetInSideTableGeneric32 ) {
+                        assert(targetSymbolOffsetInCache < (_readOnlyRegion.buffer - _readExecuteRegion.buffer) && "offset not into TEXT or DATA of cache file");
                         uint32_t targetVmAddr;
                         if ( _aslrTracker.hasRebaseTarget32(fixupLoc, &targetVmAddr) )
-                            fixupLoc->raw32 = targetVmAddr;
+                            *fixupLoc32 = targetVmAddr;
                         else
-                            assert(0 && "missing target for rebase");
-                    }
-                    else if ( fixupLoc->generic32.rebase.target > segInfo->max_valid_pointer ) {
-                        __block const char* badImagePath = nullptr;
-                        cache->forEachImage(^(const mach_header* mh, const char* installName) {
-                            if ( mh == imageLoadAddress )
-                                badImagePath = installName;
-                        });
-                        _diagnostics.error("unexpected non-pointer in chain for image at %s", badImagePath);
-                        stop = true;
-                        pointerIntoCache = false;
+                            *fixupLoc32 = (uint32_t)(_archLayout->sharedMemoryStart + targetSymbolOffsetInCache + target.addend);
                     }
-                    if ( pointerIntoCache ) {
-                        assert(fixupLoc->raw32 > _readExecuteRegion.unslidLoadAddress);
-                        assert(fixupLoc->raw32 < _readOnlyRegion.unslidLoadAddress+_readOnlyRegion.sizeInUse);
-                    }
-                    break;
-                case DYLD_CHAINED_PTR_64_OFFSET:
-                case DYLD_CHAINED_PTR_ARM64E_OFFSET:
-                    assert(0 && "unsupported chained bind type");
-                    break;
-                default:
-                    assert(0 && "unsupported chained bind type");
-            }
-
-        });
-    };
-
-    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);
-                if (targetInfo.isWeakDef)
-                    _dylibWeakExports.insert({ targetInfo.foundInDylib, offsetInCache });
-                _exportsToUses[offsetInCache].push_back(makePatchLocation(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);
                 }
+                _dylibToItsExports[target.foundInImage._mh].insert(targetSymbolOffsetInCache);
+                if ( target.isWeakDef )
+                    _dylibWeakExports.insert({ target.foundInImage._mh, targetSymbolOffsetInCache });
+                _exportsToUses[targetSymbolOffsetInCache].push_back(makePatchLocation(fixupLoc - _readExecuteRegion.buffer, pmd, target.addend));
+                _exportsToName[targetSymbolOffsetInCache] = target.foundSymbolName;
                 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 <= (int)(mh->dependentDylibCount())) ) {
-                    _missingWeakImports[fixupLoc] = mh->dependentDylibLoadPath(targetInfo.libOrdinal - 1);
-                }
+            case FixupTarget::Kind::bindMissingSymbol:
+                // if there are missing symbols, makeDyldCacheImageArray() will error 
                 break;
-            default:
-                assert(0 && "unsupported ResolvedSymbolTarget kind in dyld cache");
         }
     };
 
+
     // build ImageArray for all dylibs in dyld cache
     dyld3::closure::PathOverrides pathOverrides;
-    dyld3::closure::ClosureBuilder cb(dyld3::closure::kFirstDyldCacheImageNum, _fileSystem, cache, false, *_options.archs, pathOverrides,
-                                      dyld3::closure::ClosureBuilder::AtPath::none, false, nullptr, _options.platform, &handlers);
+    dyld3::RootsChecker rootsChecker;
+    dyld3::closure::ClosureBuilder cb(dyld3::closure::kFirstDyldCacheImageNum, _fileSystem, rootsChecker, cache, false, *_options.archs, pathOverrides,
+                                      dyld3::closure::ClosureBuilder::AtPath::none, false, nullptr, _options.platform, handler);
     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);
+    _imageArray = cb.makeDyldCacheImageArray(dylibs, aliasesArray);
     if ( cb.diagnostics().hasError() ) {
         _diagnostics.error("%s", cb.diagnostics().errorMessage().c_str());
         return;
     }
-
 }
 
 static bool operator==(const dyld_cache_patchable_location& a, const dyld_cache_patchable_location& b) {
@@ -2195,10 +2572,6 @@ void SharedCacheBuilder::addImageArray()
         return alwaysGeneratePatch.find(exportName) != alwaysGeneratePatch.end();
     };
 
-    std::set<std::string> alwaysPatchDylibs;
-    for (const char* const* d= _s_neverStubEliminateDylibs; *d != nullptr; ++d)
-        alwaysPatchDylibs.insert(*d);
-
     // First calculate how much space we need
     const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
     cache->forEachImage(^(const mach_header* mh, const char* installName) {
@@ -2206,7 +2579,7 @@ void SharedCacheBuilder::addImageArray()
         const std::set<CacheOffset>& dylibExports = _dylibToItsExports[ml];
 
         // On a customer cache, only store patch locations for interposable dylibs and weak binding
-        bool dylibNeedsPatching = !_options.optimizeStubs || alwaysPatchDylibs.count(installName);
+        bool dylibNeedsPatching = cache->isOverridablePath(installName);
 
         uint64_t numDylibExports = 0;
         for (CacheOffset exportCacheOffset : dylibExports) {
@@ -2239,7 +2612,7 @@ void SharedCacheBuilder::addImageArray()
         const std::set<CacheOffset>& dylibExports = _dylibToItsExports[ml];
 
         // On a customer cache, only store patch locations for interposable dylibs and weak binding
-        bool dylibNeedsPatching = !_options.optimizeStubs || alwaysPatchDylibs.count(installName);
+        bool dylibNeedsPatching = cache->isOverridablePath(installName);
 
         // Add the patch image which points in to the exports
         dyld_cache_image_patches patchImage;
@@ -2324,6 +2697,7 @@ void SharedCacheBuilder::addImageArray()
 
     // Free the underlying image array buffer
     _imageArray->deallocate();
+    _imageArray = nullptr;
 }
 
 void SharedCacheBuilder::addOtherImageArray(const std::vector<LoadedMachO>& otherDylibsAndBundles, std::vector<const LoadedMachO*>& overflowDylibs)
@@ -2331,7 +2705,8 @@ void SharedCacheBuilder::addOtherImageArray(const std::vector<LoadedMachO>& othe
     DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
     dyld3::closure::PathOverrides pathOverrides;
     dyld3::closure::FileSystemNull nullFileSystem;
-    dyld3::closure::ClosureBuilder cb(dyld3::closure::kFirstOtherOSImageNum, nullFileSystem, cache, false, *_options.archs, pathOverrides,
+    dyld3::RootsChecker rootsChecker;
+    dyld3::closure::ClosureBuilder cb(dyld3::closure::kFirstOtherOSImageNum, nullFileSystem, rootsChecker, cache, false, *_options.archs, pathOverrides,
                                       dyld3::closure::ClosureBuilder::AtPath::none, false, nullptr, _options.platform);
 
     // make ImageArray for other dylibs and bundles
@@ -2410,8 +2785,18 @@ void SharedCacheBuilder::addClosures(const std::vector<LoadedMachO>& osExecutabl
         if ( startsWith(loadedMachO.mappedFile.runtimePath, "/private/var/staged_system_apps/") ) {
             return;
         }
+
+        // prebuilt closures use the cdhash of the dylib to verify that the dylib is still the same
+        // at runtime as when the shared cache processed it.  We must have a code signature to record this information
+        uint32_t codeSigFileOffset;
+        uint32_t codeSigSize;
+        if ( !loadedMachO.mappedFile.mh->hasCodeSignature(codeSigFileOffset, codeSigSize) ) {
+            return;
+        }
+
         dyld3::closure::PathOverrides pathOverrides;
-        dyld3::closure::ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, _fileSystem, dyldCache, false, *_options.archs, pathOverrides,
+        dyld3::RootsChecker rootsChecker;
+        dyld3::closure::ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, _fileSystem, rootsChecker, dyldCache, false, *_options.archs, pathOverrides,
                                                dyld3::closure::ClosureBuilder::AtPath::all, false, nullptr, _options.platform, nullptr);
         bool issetuid = false;
         if ( this->_options.platform == dyld3::Platform::macOS || dyld3::MachOFile::isSimulatorPlatform(this->_options.platform) )
@@ -2496,25 +2881,149 @@ void SharedCacheBuilder::addClosures(const std::vector<LoadedMachO>& osExecutabl
     _readOnlyRegion.sizeInUse = align(_readOnlyRegion.sizeInUse, 14);
 }
 
+void SharedCacheBuilder::emitContantObjects() {
+    if ( _coalescedText.cfStrings.bufferSize == 0 )
+        return;
+
+    assert(_coalescedText.cfStrings.isaInstallName != nullptr);
+    DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    __block uint64_t targetSymbolOffsetInCache = 0;
+    __block const dyld3::MachOAnalyzer* targetSymbolMA = nullptr;
+    __block const dyld3::MachOAnalyzer* libdyldMA = nullptr;
+    cache->forEachImage(^(const mach_header* mh, const char* installName) {
+        const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
+
+        if ( strcmp(installName, "/usr/lib/system/libdyld.dylib") == 0 ) {
+            libdyldMA = ma;
+        }
+
+        if ( targetSymbolOffsetInCache != 0 )
+            return;
+        if ( strcmp(installName, _coalescedText.cfStrings.isaInstallName) != 0 )
+            return;
+        dyld3::MachOAnalyzer::FoundSymbol foundInfo;
+        bool foundSymbol = ma->findExportedSymbol(_diagnostics, _coalescedText.cfStrings.isaClassName,
+                                                  false, foundInfo, nullptr);
+        if ( foundSymbol ) {
+            targetSymbolOffsetInCache = (uint8_t*)ma - _readExecuteRegion.buffer + foundInfo.value;
+            targetSymbolMA = ma;
+        }
+    });
+    if ( targetSymbolOffsetInCache == 0 ) {
+        _diagnostics.error("Could not find export of '%s' in '%s'", _coalescedText.cfStrings.isaClassName,
+                           _coalescedText.cfStrings.isaInstallName);
+        return;
+    }
+    if ( libdyldMA == nullptr ) {
+        _diagnostics.error("Could not libdyld.dylib in shared cache");
+        return;
+    }
+
+    // If all binds to this symbol were via CF constants, then we'll never have seen the ISA patch export
+    // os add it now just in case
+    _dylibToItsExports[targetSymbolMA].insert(targetSymbolOffsetInCache);
+    _exportsToName[targetSymbolOffsetInCache] = _coalescedText.cfStrings.isaClassName;
+
+    // CFString's have so far just been memcpy'ed from the source dylib to the shared cache.
+    // We now need to rewrite their ISAs to be rebases to the ___CFConstantStringClassReference class
+    const uint64_t cfStringAtomSize = (uint64_t)DyldSharedCache::ConstantClasses::cfStringAtomSize;
+    assert( (_coalescedText.cfStrings.bufferSize % cfStringAtomSize) == 0);
+    for (uint64_t bufferOffset = 0; bufferOffset != _coalescedText.cfStrings.bufferSize; bufferOffset += cfStringAtomSize) {
+        uint8_t* atomBuffer = _coalescedText.cfStrings.bufferAddr + bufferOffset;
+        // The ISA fixup is at an offset of 0 in to the atom
+        uint8_t* fixupLoc = atomBuffer;
+        // We purposefully want to remove the pointer authentication from the ISA so
+        // just use an empty pointer metadata
+        dyld3::Loader::PointerMetaData pmd;
+        uint64_t addend = 0;
+        _exportsToUses[targetSymbolOffsetInCache].push_back(makePatchLocation(fixupLoc - _readExecuteRegion.buffer, pmd, addend));
+        *(uint64_t*)fixupLoc = _archLayout->sharedMemoryStart + targetSymbolOffsetInCache;
+        _aslrTracker.add(fixupLoc);
+    }
+
+    // Set the ranges in the libdyld in the shared cache.  At runtime we can use these to quickly check if a given address
+    // is a valid constant
+    typedef std::pair<const uint8_t*, const uint8_t*> ObjCConstantRange;
+    std::pair<const void*, uint64_t> sharedCacheRanges = cache->getObjCConstantRange();
+    uint64_t numRanges = sharedCacheRanges.second / sizeof(ObjCConstantRange);
+    dyld3::Array<ObjCConstantRange> rangeArray((ObjCConstantRange*)sharedCacheRanges.first, numRanges, numRanges);
+
+    if ( numRanges > dyld_objc_string_kind ) {
+        rangeArray[dyld_objc_string_kind].first = (const uint8_t*)_coalescedText.cfStrings.bufferVMAddr;
+        rangeArray[dyld_objc_string_kind].second = rangeArray[dyld_objc_string_kind].first + _coalescedText.cfStrings.bufferSize;
+        _aslrTracker.add(&rangeArray[dyld_objc_string_kind].first);
+        _aslrTracker.add(&rangeArray[dyld_objc_string_kind].second);
+    }
+
+    // Update the __SHARED_CACHE range in libdyld to contain the cf/objc constants
+    libdyldMA->forEachLoadCommand(_diagnostics, ^(const load_command* cmd, bool& stop) {
+        // We don't handle 32-bit as this is only needed for pointer authentication
+        assert(cmd->cmd != LC_SEGMENT);
+        if ( cmd->cmd == LC_SEGMENT_64 ) {
+            segment_command_64* seg = (segment_command_64*)cmd;
+            if ( strcmp(seg->segname, "__SHARED_CACHE") == 0 ) {
+                // Update the range of this segment, and any sections inside
+                seg->vmaddr     = _coalescedText.cfStrings.bufferVMAddr;
+                seg->vmsize     = _coalescedText.cfStrings.bufferSize;
+                seg->fileoff    = _coalescedText.cfStrings.cacheFileOffset;
+                seg->fileoff    = _coalescedText.cfStrings.bufferSize;
+                section_64* const sectionsStart = (section_64*)((char*)seg + sizeof(struct segment_command_64));
+                section_64* const sectionsEnd   = &sectionsStart[seg->nsects];
+                for (section_64* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+                    if ( !strcmp(sect->sectname, "__cfstring") ) {
+                        sect->addr      = _coalescedText.cfStrings.bufferVMAddr;
+                        sect->size      = _coalescedText.cfStrings.bufferSize;
+                        sect->offset    = (uint32_t)_coalescedText.cfStrings.cacheFileOffset;
+                    }
+                }
+                stop = true;
+            }
+        }
+    });
+}
+
 
 bool SharedCacheBuilder::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);
+    const uint32_t mappingsCount = cacheHeader->mappingCount;
+    // Check the sizes of all the regions are correct
     assert(_readExecuteRegion.sizeInUse       == mappings[0].size);
-    assert(_readWriteRegion.sizeInUse         == mappings[1].size);
-    assert(_readOnlyRegion.sizeInUse          == mappings[2].size);
+    for (uint32_t i = 0; i != _dataRegions.size(); ++i) {
+        assert(_dataRegions[i].sizeInUse == mappings[i + 1].size);
+    }
+    assert(_readOnlyRegion.sizeInUse          == mappings[mappingsCount - 1].size);
+
+    // Check the file offsets of all the regions are correct
     assert(_readExecuteRegion.cacheFileOffset == mappings[0].fileOffset);
-    assert(_readWriteRegion.cacheFileOffset   == mappings[1].fileOffset);
-    assert(_readOnlyRegion.cacheFileOffset    == mappings[2].fileOffset);
+    for (uint32_t i = 0; i != _dataRegions.size(); ++i) {
+        assert(_dataRegions[i].cacheFileOffset   == mappings[i + 1].fileOffset);
+    }
+    assert(_readOnlyRegion.cacheFileOffset    == mappings[mappingsCount - 1].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);
+    assert(cacheHeader->codeSignatureOffset   == _readOnlyRegion.cacheFileOffset+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse);
+
+    // Make sure the slidable mappings have the same ranges as the original mappings
+    const dyld_cache_mapping_and_slide_info* slidableMappings = (dyld_cache_mapping_and_slide_info*)(_readExecuteRegion.buffer + cacheHeader->mappingWithSlideOffset);
+    assert(cacheHeader->mappingCount == cacheHeader->mappingWithSlideCount);
+    for (uint32_t i = 0; i != cacheHeader->mappingCount; ++i) {
+        assert(mappings[i].address      == slidableMappings[i].address);
+        assert(mappings[i].size         == slidableMappings[i].size);
+        assert(mappings[i].fileOffset   == slidableMappings[i].fileOffset);
+        assert(mappings[i].maxProt      == slidableMappings[i].maxProt);
+        assert(mappings[i].initProt     == slidableMappings[i].initProt);
+    }
+
+    // Now that we know everything is correct, actually copy the data
+    cacheSizeCallback(_readExecuteRegion.sizeInUse+dataRegionsSizeInUse()+_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);
+    for (uint32_t i = 0; i != _dataRegions.size(); ++i) {
+        fullyWritten &= copyCallback(_dataRegions[i].buffer, _dataRegions[i].sizeInUse, mappings[i + 1].fileOffset);
+    }
+    fullyWritten &= copyCallback(_readOnlyRegion.buffer, _readOnlyRegion.sizeInUse, mappings[cacheHeader->mappingCount - 1].fileOffset);
     if ( _localSymbolsRegion.sizeInUse != 0 ) {
-        assert(cacheHeader->localSymbolsOffset == mappings[2].fileOffset+_readOnlyRegion.sizeInUse);
+        assert(cacheHeader->localSymbolsOffset == mappings[cacheHeader->mappingCount - 1].fileOffset+_readOnlyRegion.sizeInUse);
         fullyWritten &= copyCallback(_localSymbolsRegion.buffer, _localSymbolsRegion.sizeInUse, cacheHeader->localSymbolsOffset);
     }
     fullyWritten &= copyCallback(_codeSignatureRegion.buffer, _codeSignatureRegion.sizeInUse, cacheHeader->codeSignatureOffset);
@@ -2531,12 +3040,6 @@ void SharedCacheBuilder::writeFile(const std::string& path)
     int fd = mkstemp(pathTemplateSpace);
     if ( fd != -1 ) {
         auto cacheSizeCallback = ^(uint64_t size) {
-            // if making macOS dyld cache for current OS into standard location
-            if ( (_options.platform == dyld3::Platform::macOS) && startsWith(path, MACOSX_DYLD_SHARED_CACHE_DIR) ) {
-                // <rdar://48687550> pin cache file to SSD on fusion drives
-                apfs_data_pin_location_t where = APFS_PIN_DATA_TO_MAIN;
-                ::fsctl(pathTemplateSpace, APFSIOC_PIN_DATA, &where, 0);
-            }
             // set final cache file size (may help defragment file)
             ::ftruncate(fd, size);
         };
@@ -2565,16 +3068,27 @@ void SharedCacheBuilder::writeFile(const std::string& path)
         if ( fullyWritten ) {
             ::fchmod(fd, S_IRUSR|S_IRGRP|S_IROTH); // mkstemp() makes file "rw-------", switch it to "r--r--r--"
             // <rdar://problem/55370916> TOCTOU: verify path is still a realpath (not changed)
-            char resolvedPath[PATH_MAX];
-            ::realpath(path.c_str(), resolvedPath);
-            // Note: if the target cache file does not already exist, realpath() will return NULL, but still fill in the path buffer
-            if ( path != resolvedPath ) {
-                _diagnostics.error("output file path changed from: '%s' to: '%s'", path.c_str(), resolvedPath);
-                return;
+            // For MRM bringup, dyld installs symlinks from:
+            //   dyld_shared_cache_x86_64 -> ../../../../System/Library/dyld/dyld_shared_cache_x86_64
+            //   dyld_shared_cache_x86_64h -> ../../../../System/Library/dyld/dyld_shared_cache_x86_64h
+            // We don't want to follow that symlink when we install the cache, but instead write over it
+            auto lastSlash = path.find_last_of("/");
+            if ( lastSlash != std::string::npos ) {
+                std::string directoryPath = path.substr(0, lastSlash);
+
+                char resolvedPath[PATH_MAX];
+                ::realpath(directoryPath.c_str(), resolvedPath);
+                // Note: if the target cache file does not already exist, realpath() will return NULL, but still fill in the path buffer
+                if ( directoryPath != resolvedPath ) {
+                    _diagnostics.error("output directory file path changed from: '%s' to: '%s'", directoryPath.c_str(), resolvedPath);
+                    return;
+                }
             }
             if ( ::rename(pathTemplateSpace, path.c_str()) == 0) {
                 ::close(fd);
                 return; // success
+            } else {
+                _diagnostics.error("could not rename file '%s' to: '%s'", pathTemplateSpace, path.c_str());
             }
         }
         else {
@@ -2603,12 +3117,17 @@ void SharedCacheBuilder::writeBuffer(uint8_t*& buffer, uint64_t& bufferSize) {
 
 void SharedCacheBuilder::writeMapFile(const std::string& path)
 {
-    const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
-    std::string mapContent = cache->mapFile();
+    std::string mapContent = getMapFileBuffer();
     safeSave(mapContent.c_str(), mapContent.size(), path);
 }
 
-std::string SharedCacheBuilder::getMapFileBuffer(const std::string& cacheDisposition) const
+std::string SharedCacheBuilder::getMapFileBuffer() const
+{
+    const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    return cache->mapFile();
+}
+
+std::string SharedCacheBuilder::getMapFileJSONBuffer(const std::string& cacheDisposition) const
 {
     const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
     return cache->generateJSONMap(cacheDisposition.c_str());
@@ -2618,11 +3137,12 @@ void SharedCacheBuilder::markPaddingInaccessible()
 {
     // region between RX and RW
     uint8_t* startPad1 = _readExecuteRegion.buffer+_readExecuteRegion.sizeInUse;
-    uint8_t* endPad1   = _readWriteRegion.buffer;
+    uint8_t* endPad1   = firstDataRegion()->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;
+    const Region* lastRegion = lastDataRegion();
+    uint8_t* startPad2 = lastRegion->buffer+lastRegion->sizeInUse;
     uint8_t* endPad2   = _readOnlyRegion.buffer;
     ::vm_protect(mach_task_self(), (vm_address_t)startPad2, endPad2-startPad2, false, 0);
 }
@@ -2630,7 +3150,23 @@ void SharedCacheBuilder::markPaddingInaccessible()
 
 void SharedCacheBuilder::forEachCacheDylib(void (^callback)(const std::string& path)) {
     for (const DylibInfo& dylibInfo : _sortedDylibs)
-        callback(dylibInfo.runtimePath);
+        callback(dylibInfo.dylibID);
+}
+
+
+void SharedCacheBuilder::forEachCacheSymlink(void (^callback)(const std::string& path))
+{
+    const DyldSharedCache* cache = (DyldSharedCache*)_readExecuteRegion.buffer;
+    const dyld3::closure::ImageArray* images = cache->cachedDylibsImageArray();
+    if ( images == nullptr )
+        return;
+
+    // Aliases we folded in to the cache are in the cache dylib closures
+    images->forEachImage(^(const dyld3::closure::Image *image, bool &stop) {
+        image->forEachAlias(^(const char *aliasPath, bool &stop) {
+            callback(aliasPath);
+        });
+    });
 }
 
 
@@ -2739,7 +3275,7 @@ void SharedCacheBuilder::codeSign()
             cacheIdentifier += ".development";
     }
     // get pointers into shared cache buffer
-    size_t          inBbufferSize = _readExecuteRegion.sizeInUse+_readWriteRegion.sizeInUse+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse;
+    size_t          inBbufferSize = _readExecuteRegion.sizeInUse+dataRegionsSizeInUse()+_readOnlyRegion.sizeInUse+_localSymbolsRegion.sizeInUse;
 
     const uint16_t pageSize = _archLayout->csPageSize;
 
@@ -2886,26 +3422,50 @@ void SharedCacheBuilder::codeSign()
     cache->codeSignatureOffset  = inBbufferSize;
     cache->codeSignatureSize    = sigSize;
 
-    const uint32_t rwSlotStart = (uint32_t)(_readExecuteRegion.sizeInUse / pageSize);
-    const uint32_t roSlotStart = (uint32_t)(rwSlotStart + _readWriteRegion.sizeInUse / pageSize);
-    const uint32_t localsSlotStart = (uint32_t)(roSlotStart + _readOnlyRegion.sizeInUse / pageSize);
+    struct SlotRange {
+        uint64_t        start   = 0;
+        uint64_t        end     = 0;
+        const uint8_t*  buffer  = nullptr;
+    };
+    std::vector<SlotRange> regionSlots;
+    // __TEXT
+    regionSlots.push_back({ 0, (_readExecuteRegion.sizeInUse / pageSize), _readExecuteRegion.buffer });
+    // __DATA
+    for (const Region& dataRegion : _dataRegions) {
+        // The first data region starts at the end of __TEXT, and subsequent regions are
+        // after the previous __DATA region.
+        uint64_t previousEnd = regionSlots.back().end;
+        uint64_t numSlots = dataRegion.sizeInUse / pageSize;
+        regionSlots.push_back({ previousEnd, previousEnd + numSlots, dataRegion.buffer });
+    }
+    // __LINKEDIT
+    {
+        uint64_t previousEnd = regionSlots.back().end;
+        uint64_t numSlots = _readOnlyRegion.sizeInUse / pageSize;
+        regionSlots.push_back({ previousEnd, previousEnd + numSlots, _readOnlyRegion.buffer });
+    }
+    // local symbols
+    if ( _localSymbolsRegion.sizeInUse != 0 ) {
+        uint64_t previousEnd = regionSlots.back().end;
+        uint64_t numSlots = _localSymbolsRegion.sizeInUse / pageSize;
+        regionSlots.push_back({ previousEnd, previousEnd + numSlots, _localSymbolsRegion.buffer });
+    }
+
     auto codeSignPage = ^(size_t i) {
-        const uint8_t* code = nullptr;
         // move to correct region
-        if ( i < rwSlotStart )
-            code = _readExecuteRegion.buffer + (i * pageSize);
-        else if ( i >= rwSlotStart && i < roSlotStart )
-            code = _readWriteRegion.buffer + ((i - rwSlotStart) * pageSize);
-        else if ( i >= roSlotStart && i < localsSlotStart )
-            code = _readOnlyRegion.buffer + ((i - roSlotStart) * pageSize);
-        else
-            code = _localSymbolsRegion.buffer + ((i - localsSlotStart) * pageSize);
+        for (const SlotRange& slotRange : regionSlots) {
+            if ( (i >= slotRange.start) && (i < slotRange.end) ) {
+                const uint8_t* code = slotRange.buffer + ((i - slotRange.start) * pageSize);
 
-        CCDigest(dscDigestFormat, code, pageSize, hashSlot + (i * dscHashSize));
+                CCDigest(dscDigestFormat, code, pageSize, hashSlot + (i * dscHashSize));
 
-        if ( agile ) {
-            CCDigest(kCCDigestSHA256, code, pageSize, hash256Slot + (i * CS_HASH_SIZE_SHA256));
+                if ( agile ) {
+                    CCDigest(kCCDigestSHA256, code, pageSize, hash256Slot + (i * CS_HASH_SIZE_SHA256));
+                }
+                return;
+            }
         }
+        assert(0 && "Out of range slot");
     };
 
     // compute hashes
@@ -2976,6 +3536,14 @@ const std::string SharedCacheBuilder::uuid() const
     return uuidStr;
 }
 
+void SharedCacheBuilder::forEachDylibInfo(void (^callback)(const DylibInfo& dylib, Diagnostics& dylibDiag)) {
+    for (const DylibInfo& dylibInfo : _sortedDylibs) {
+        // The shared cache builder doesn't use per-dylib errors right now
+        // so just share the global diagnostics
+        callback(dylibInfo, _diagnostics);
+    }
+}
+
 
 
 template <typename P>
@@ -3048,7 +3616,7 @@ bool SharedCacheBuilder::makeRebaseChainV2(uint8_t* pageContent, uint16_t lastLo
         uint16_t nOffset = nonRebaseLocationOffsets[n];
         assert(nOffset != 0);
         pint_t* nLoc = (pint_t*)&pageContent[nOffset];
-        uint32_t delta2 = nOffset - prevOffset;
+        pint_t delta2 = nOffset - prevOffset;
         pint_t value = (pint_t)P::getP(*prevLoc);
         pint_t newValue;
         if ( value == 0 )
@@ -3060,7 +3628,7 @@ bool SharedCacheBuilder::makeRebaseChainV2(uint8_t* pageContent, uint16_t lastLo
         prevOffset = nOffset;
         prevLoc = nLoc;
     }
-    uint32_t delta3 = offset - prevOffset;
+    pint_t delta3 = offset - prevOffset;
     pint_t value = (pint_t)P::getP(*prevLoc);
     pint_t newValue;
     if ( value == 0 )
@@ -3126,54 +3694,70 @@ void SharedCacheBuilder::addPageStartsV2(uint8_t* pageContent, const bool bitmap
 }
 
 template <typename P>
-void SharedCacheBuilder::writeSlideInfoV2(const bool bitmap[], unsigned dataPageCount)
+void SharedCacheBuilder::writeSlideInfoV2(const bool bitmapForAllDataRegions[], unsigned dataPageCountForAllDataRegions)
 {
     typedef typename P::uint_t    pint_t;
     typedef typename P::E         E;
-    const uint32_t pageSize = 4096;
-
-    // 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  = _archLayout->useValueAdd ? _archLayout->sharedMemoryStart : 0;
-
-    // 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;
+
+    const uint32_t  pageSize = _aslrTracker.pageSize();
+    const uint8_t*  firstDataRegionBuffer = firstDataRegion()->buffer;
+    for (uint32_t dataRegionIndex = 0; dataRegionIndex != _dataRegions.size(); ++dataRegionIndex) {
+        Region& dataRegion = _dataRegions[dataRegionIndex];
+
+        // fill in fixed info
+        assert(dataRegion.slideInfoFileOffset != 0);
+        assert((dataRegion.sizeInUse % pageSize) == 0);
+        unsigned dataPageCount = (uint32_t)dataRegion.sizeInUse / pageSize;
+        dyld_cache_slide_info2* info = (dyld_cache_slide_info2*)dataRegion.slideInfoBuffer;
+        info->version    = 2;
+        info->page_size  = pageSize;
+        info->delta_mask = _archLayout->pointerDeltaMask;
+        info->value_add  = _archLayout->useValueAdd ? _archLayout->sharedMemoryStart : 0;
+
+        // set page starts and extras for each page
+        std::vector<uint16_t> pageStarts;
+        std::vector<uint16_t> pageExtras;
+        pageStarts.reserve(dataPageCount);
+
+        const size_t bitmapEntriesPerPage = (sizeof(bool)*(pageSize/4));
+        uint8_t* pageContent = dataRegion.buffer;
+        unsigned numPagesFromFirstDataRegion = (uint32_t)(dataRegion.buffer - firstDataRegionBuffer) / pageSize;
+        assert((numPagesFromFirstDataRegion + dataPageCount) <= dataPageCountForAllDataRegions);
+        const bool* bitmapForRegion = (const bool*)bitmapForAllDataRegions + (bitmapEntriesPerPage * numPagesFromFirstDataRegion);
+        const bool* bitmapForPage = bitmapForRegion;
+        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));
+        }
+
+        // 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);
+        dataRegion.slideInfoFileSize = slideInfoSize;
+        if ( dataRegion.slideInfoFileSize > dataRegion.slideInfoBufferSizeAllocated ) {
+            _diagnostics.error("kernel slide info overflow buffer");
         }
-        pageContent += pageSize;
-        bitmapForPage += (sizeof(bool)*(pageSize/4));
-    }
-
-    // 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());
+        // Update the mapping entry on the cache header
+        const dyld_cache_header*       cacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer;
+        dyld_cache_mapping_and_slide_info* slidableMappings = (dyld_cache_mapping_and_slide_info*)(_readExecuteRegion.buffer + cacheHeader->mappingWithSlideOffset);
+        slidableMappings[1 + dataRegionIndex].slideInfoFileSize = dataRegion.slideInfoFileSize;
+        //fprintf(stderr, "pageCount=%u, page_starts_count=%lu, page_extras_count=%lu\n", dataPageCount, pageStarts.size(), pageExtras.size());
+    }
 }
 
 #if SUPPORT_ARCH_arm64_32 || SUPPORT_ARCH_armv7k
@@ -3330,52 +3914,67 @@ void SharedCacheBuilder::addPageStartsV4(uint8_t* pageContent, const bool bitmap
 
 
 template <typename P>
-void SharedCacheBuilder::writeSlideInfoV4(const bool bitmap[], unsigned dataPageCount)
+void SharedCacheBuilder::writeSlideInfoV4(const bool bitmapForAllDataRegions[], unsigned dataPageCountForAllDataRegions)
 {
     typedef typename P::uint_t    pint_t;
     typedef typename P::E         E;
-    const uint32_t pageSize = 4096;
-
-    // fill in fixed info
-    assert(_slideInfoFileOffset != 0);
-    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  = info->value_add  = _archLayout->useValueAdd ? _archLayout->sharedMemoryStart : 0;
-
-    // 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) {
-        addPageStartsV4<P>(pageContent, bitmapForPage, info, pageStarts, pageExtras);
-        if ( _diagnostics.hasError() ) {
-            return;
+
+    const uint32_t  pageSize = _aslrTracker.pageSize();
+    const uint8_t*  firstDataRegionBuffer = firstDataRegion()->buffer;
+    for (uint32_t dataRegionIndex = 0; dataRegionIndex != _dataRegions.size(); ++dataRegionIndex) {
+        Region& dataRegion = _dataRegions[dataRegionIndex];
+
+        // fill in fixed info
+        assert(dataRegion.slideInfoFileOffset != 0);
+        assert((dataRegion.sizeInUse % pageSize) == 0);
+        unsigned dataPageCount = (uint32_t)dataRegion.sizeInUse / pageSize;
+        dyld_cache_slide_info4* info = (dyld_cache_slide_info4*)dataRegion.slideInfoBuffer;
+        info->version    = 4;
+        info->page_size  = pageSize;
+        info->delta_mask = _archLayout->pointerDeltaMask;
+        info->value_add  = info->value_add  = _archLayout->useValueAdd ? _archLayout->sharedMemoryStart : 0;
+
+        // set page starts and extras for each page
+        std::vector<uint16_t> pageStarts;
+        std::vector<uint16_t> pageExtras;
+        pageStarts.reserve(dataPageCount);
+        const size_t bitmapEntriesPerPage = (sizeof(bool)*(pageSize/4));
+        uint8_t* pageContent = dataRegion.buffer;
+        unsigned numPagesFromFirstDataRegion = (uint32_t)(dataRegion.buffer - firstDataRegionBuffer) / pageSize;
+        assert((numPagesFromFirstDataRegion + dataPageCount) <= dataPageCountForAllDataRegions);
+        const bool* bitmapForRegion = (const bool*)bitmapForAllDataRegions + (bitmapEntriesPerPage * numPagesFromFirstDataRegion);
+        const bool* bitmapForPage = bitmapForRegion;
+        for (unsigned i=0; i < dataPageCount; ++i) {
+            addPageStartsV4<P>(pageContent, bitmapForPage, info, pageStarts, pageExtras);
+            if ( _diagnostics.hasError() ) {
+                return;
+            }
+            pageContent += pageSize;
+            bitmapForPage += (sizeof(bool)*(pageSize/4));
         }
-        pageContent += pageSize;
-        bitmapForPage += (sizeof(bool)*(pageSize/4));
-    }
-    // fill in computed info
-    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_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);
-    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 v4 overflow buffer, need %lldKB, have room for %lldKB", slideInfoSize, _slideInfoBufferSizeAllocated);
-    }
-    ((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());
+        // fill in computed info
+        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_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);
+        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);
+        dataRegion.slideInfoFileSize = slideInfoSize;
+        if ( dataRegion.slideInfoFileSize > dataRegion.slideInfoBufferSizeAllocated ) {
+            _diagnostics.error("kernel slide info overflow buffer");
+        }
+        // Update the mapping entry on the cache header
+        const dyld_cache_header*       cacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer;
+        dyld_cache_mapping_and_slide_info* slidableMappings = (dyld_cache_mapping_and_slide_info*)(_readExecuteRegion.buffer + cacheHeader->mappingWithSlideOffset);
+        slidableMappings[1 + dataRegionIndex].slideInfoFileSize = dataRegion.slideInfoFileSize;
+        //fprintf(stderr, "pageCount=%u, page_starts_count=%lu, page_extras_count=%lu\n", dataPageCount, pageStarts.size(), pageExtras.size());
+    }
 }
 #endif
 
@@ -3505,31 +4104,43 @@ uint16_t SharedCacheBuilder::pageStartV3(uint8_t* pageContent, uint32_t pageSize
 }
 
 
-void SharedCacheBuilder::writeSlideInfoV3(const bool bitmap[], unsigned dataPageCount)
+void SharedCacheBuilder::writeSlideInfoV3(const bool bitmapForAllDataRegions[], unsigned dataPageCountForAllDataRegions)
 {
-    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");
+    const uint32_t  pageSize = _aslrTracker.pageSize();
+    const uint8_t*  firstDataRegionBuffer = firstDataRegion()->buffer;
+    for (uint32_t dataRegionIndex = 0; dataRegionIndex != _dataRegions.size(); ++dataRegionIndex) {
+        Region& dataRegion = _dataRegions[dataRegionIndex];
+        // fprintf(stderr, "writeSlideInfoV3: %s 0x%llx->0x%llx\n", dataRegion.name.c_str(), dataRegion.cacheFileOffset, dataRegion.cacheFileOffset + dataRegion.sizeInUse);
+        // fill in fixed info
+        assert(dataRegion.slideInfoFileOffset != 0);
+        assert((dataRegion.sizeInUse % pageSize) == 0);
+        unsigned dataPageCount = (uint32_t)dataRegion.sizeInUse / pageSize;
+        dyld_cache_slide_info3* info = (dyld_cache_slide_info3*)dataRegion.slideInfoBuffer;
+        info->version           = 3;
+        info->page_size         = pageSize;
+        info->page_starts_count = dataPageCount;
+        info->auth_value_add    = _archLayout->sharedMemoryStart;
+
+        // fill in per-page starts
+        const size_t bitmapEntriesPerPage = (sizeof(bool)*(pageSize/4));
+        uint8_t* pageContent = dataRegion.buffer;
+        unsigned numPagesFromFirstDataRegion = (uint32_t)(dataRegion.buffer - firstDataRegionBuffer) / pageSize;
+        assert((numPagesFromFirstDataRegion + dataPageCount) <= dataPageCountForAllDataRegions);
+        const bool* bitmapForRegion = (const bool*)bitmapForAllDataRegions + (bitmapEntriesPerPage * numPagesFromFirstDataRegion);
+        const bool* bitmapForPage = bitmapForRegion;
+        //for (unsigned i=0; i < dataPageCount; ++i) {
+        dispatch_apply(dataPageCount, DISPATCH_APPLY_AUTO, ^(size_t i) {
+            info->page_starts[i] = pageStartV3(pageContent + (i * pageSize), pageSize, bitmapForPage + (i * bitmapEntriesPerPage));
+        });
+
+        // update region with final size
+        dataRegion.slideInfoFileSize = align(__offsetof(dyld_cache_slide_info3, page_starts[dataPageCount]), _archLayout->sharedRegionAlignP2);
+        if ( dataRegion.slideInfoFileSize > dataRegion.slideInfoBufferSizeAllocated ) {
+            _diagnostics.error("kernel slide info overflow buffer");
+        }
+        // Update the mapping entry on the cache header
+        const dyld_cache_header*       cacheHeader = (dyld_cache_header*)_readExecuteRegion.buffer;
+        dyld_cache_mapping_and_slide_info* slidableMappings = (dyld_cache_mapping_and_slide_info*)(_readExecuteRegion.buffer + cacheHeader->mappingWithSlideOffset);
+        slidableMappings[1 + dataRegionIndex].slideInfoFileSize = dataRegion.slideInfoFileSize;
     }
 }
index 97520b13721c5c377e311ca00f59bf59d4a6aa8b..082f035410162d4ad72b5f44c7cd4a8397a167e5 100644 (file)
@@ -28,6 +28,7 @@
 #include "CacheBuilder.h"
 #include "DyldSharedCache.h"
 #include "ClosureFileSystem.h"
+#include "IMPCachesBuilder.hpp"
 
 class SharedCacheBuilder : public CacheBuilder {
 public:
@@ -47,7 +48,8 @@ public:
     void                                        writeFile(const std::string& path);
     void                                        writeBuffer(uint8_t*& buffer, uint64_t& size);
     void                                        writeMapFile(const std::string& path);
-    std::string                                 getMapFileBuffer(const std::string& cacheDisposition) const;
+    std::string                                 getMapFileBuffer() const;
+    std::string                                 getMapFileJSONBuffer(const std::string& cacheDisposition) const;
     void                                        deleteBuffer();
     const std::set<std::string>                 warnings();
     const std::set<const dyld3::MachOAnalyzer*> evictions();
@@ -57,6 +59,9 @@ public:
     const std::string                           uuid() const;
 
     void                                        forEachCacheDylib(void (^callback)(const std::string& path));
+    void                                        forEachCacheSymlink(void (^callback)(const std::string& path));
+
+    void                                        forEachDylibInfo(void (^callback)(const DylibInfo& dylib, Diagnostics& dylibDiag)) override final;
 
 private:
 
@@ -80,7 +85,6 @@ private:
     {
         uint64_t    sharedMemoryStart;
         uint64_t    sharedMemorySize;
-        uint64_t    textAndDataMaxSize;
         uint64_t    sharedRegionPadding;
         uint64_t    pointerDeltaMask;
         const char* archName;
@@ -93,13 +97,22 @@ private:
     };
 
     static const ArchLayout  _s_archLayout[];
-    static const char* const _s_neverStubEliminateDylibs[];
     static const char* const _s_neverStubEliminateSymbols[];
 
     void        makeSortedDylibs(const std::vector<LoadedMachO>& dylibs, const std::unordered_map<std::string, unsigned> sortOrder);
-    void        processSelectorStrings(const std::vector<LoadedMachO>& executables);
-    void        parseCoalescableSegments();
+    void        processSelectorStrings(const std::vector<LoadedMachO>& executables, IMPCaches::HoleMap& selectorsHoleMap);
+    void        parseCoalescableSegments(IMPCaches::SelectorMap& selectorMap, IMPCaches::HoleMap& selectorsHoleMap);
     void        assignSegmentAddresses();
+    void        assignMultipleDataSegmentAddresses(uint64_t& addr, uint32_t totalProtocolDefCount);
+
+    uint64_t    dataRegionsTotalSize() const;
+    uint64_t    dataRegionsSizeInUse() const;
+
+    // Return the earliest data region by address
+    const Region* firstDataRegion() const;
+
+    // Return the lateset data region by address
+    const Region* lastDataRegion() const;
 
     uint64_t    cacheOverflowAmount();
     size_t      evictLeafDylibs(uint64_t reductionTarget, std::vector<const LoadedMachO*>& overflowDylibs);
@@ -118,23 +131,22 @@ private:
     bool        writeCache(void (^cacheSizeCallback)(uint64_t size), bool (^copyCallback)(const uint8_t* src, uint64_t size, uint64_t dstOffset));
 
     // implemented in OptimizerObjC.cpp
-    void        optimizeObjC();
+    void        optimizeObjC(bool impCachesSuccess, const std::vector<const IMPCaches::Selector*> & inlinedSelectors);
     uint32_t    computeReadOnlyObjC(uint32_t selRefCount, uint32_t classDefCount, uint32_t protocolDefCount);
     uint32_t    computeReadWriteObjC(uint32_t imageCount, uint32_t protocolDefCount);
 
-    // implemented in OptimizerBranches.cpp
-    void        optimizeAwayStubs();
+    void        emitContantObjects();
 
     typedef std::unordered_map<std::string, const dyld3::MachOAnalyzer*> InstallNameToMA;
 
     typedef uint64_t                                                CacheOffset;
 
+    std::vector<DylibInfo>                      _sortedDylibs;
+    std::vector<Region>                         _dataRegions; // 1 or more __DATA regions.
     UnmappedRegion                              _codeSignatureRegion;
     std::set<const dyld3::MachOAnalyzer*>       _evictions;
     const ArchLayout*                           _archLayout                             = nullptr;
     uint32_t                                    _aliasCount                             = 0;
-    uint64_t                                    _slideInfoFileOffset                    = 0;
-    uint64_t                                    _slideInfoBufferSizeAllocated           = 0;
     uint8_t*                                    _objcReadOnlyBuffer                     = nullptr;
     uint64_t                                    _objcReadOnlyBufferSizeUsed             = 0;
     uint64_t                                    _objcReadOnlyBufferSizeAllocated        = 0;
@@ -152,6 +164,7 @@ private:
     std::set<std::pair<const dyld3::MachOLoaded*, CacheOffset>>                 _dylibWeakExports;
     std::unordered_map<CacheOffset, std::vector<dyld_cache_patchable_location>> _exportsToUses;
     std::unordered_map<CacheOffset, std::string>                                _exportsToName;
+    IMPCaches::IMPCachesBuilder* _impCachesBuilder;
 };
 
 
index 4bab986ef0877e9d0cfa3bcbb9dff01335f068d0..b88f8fe17678706ca063b6bed594d6ca7f1158c9 100644 (file)
@@ -82,7 +82,7 @@ inline void putHexByte(uint8_t value, char*& p)
     putHexNibble(value & 0x0F, p);
 }
 
-inline uint8_t hexCharToUInt(const char hexByte, uint8_t& value) {
+inline bool hexCharToUInt(const char hexByte, uint8_t& value) {
     if (hexByte >= '0' && hexByte <= '9') {
         value = hexByte - '0';
         return true;
@@ -122,20 +122,21 @@ inline uint64_t hexToUInt64(const char* startHexByte, const char** endHexByte) {
     return retval;
 }
 
-inline bool hexToBytes(const char* startHexByte, uint32_t length, uint8_t buffer[]) {
-    if (startHexByte == nullptr)
-        return false;
-    const char *currentHexByte = startHexByte;
-    for (uint32_t i = 0; i < length; ++i) {
+inline bool hexStringToBytes(const char* hexString, uint8_t buffer[], unsigned bufferMaxSize, unsigned& bufferLenUsed)
+{
+    bufferLenUsed = 0;
+    bool high = true;
+    for (const char* s=hexString; *s != '\0'; ++s) {
+        if ( bufferLenUsed > bufferMaxSize )
+            return false;
         uint8_t value;
-        if (!hexCharToUInt(currentHexByte[i], value)) {
+        if ( !hexCharToUInt(*s, value) )
             return false;
-        }
-        if (i%2 == 0) {
-            buffer[i/2] = value << 4;
-        } else {
-            buffer[(i-1)/2] |= value;
-        }
+        if ( high )
+            buffer[bufferLenUsed] = value << 4;
+        else
+            buffer[bufferLenUsed++] |= value;
+        high = !high;
     }
     return true;
 }
diff --git a/dyld3/shared-cache/dsc_extractor.cpp b/dyld3/shared-cache/dsc_extractor.cpp
new file mode 100644 (file)
index 0000000..022668d
--- /dev/null
@@ -0,0 +1,853 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/syslimits.h>
+#include <libkern/OSByteOrder.h>
+#include <mach-o/arch.h>
+#include <mach-o/loader.h>
+#include <Availability.h>
+
+#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 "dsc_iterator.h"
+#include "dsc_extractor.h"
+#include "DyldSharedCache.h"
+#include "MachOAnalyzer.h"
+#include "SupportedArchs.h"
+#include "Trie.hpp"
+
+#include <vector>
+#include <set>
+#include <map>
+#include <unordered_map>
+#include <algorithm>
+#include <dispatch/dispatch.h>
+
+struct seg_info
+{
+    seg_info(const char* n, uint64_t o, uint64_t s)
+    : segName(n), offset(o), sizem(s) { }
+    const char* segName;
+    uint64_t    offset;
+    uint64_t    sizem;
+};
+
+class CStringHash {
+public:
+    size_t operator()(const char* __s) const {
+        size_t __h = 0;
+        for ( ; *__s; ++__s)
+            __h = 5 * __h + *__s;
+        return __h;
+    };
+};
+class CStringEquals {
+public:
+    bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
+};
+typedef std::unordered_map<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 ExportInfoTrie::Entry &entry) const {
+        return isSymbolReExport(entry);
+    }
+private:
+    bool isSymbolReExport(const ExportInfoTrie::Entry &entry) const {
+        if ( (entry.info.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) != EXPORT_SYMBOL_FLAGS_KIND_REGULAR )
+            return true;
+        if ( (entry.info.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.info.other) != 0 )
+            return true;
+        return false;
+    }
+    const std::set<int> &_reexportDeps;
+};
+
+template <typename P>
+struct LoadCommandInfo {
+};
+
+template <typename A>
+class LinkeditOptimizer {
+    typedef typename A::P P;
+    typedef typename A::P::E E;
+    typedef typename A::P::uint_t pint_t;
+
+private:
+    macho_segment_command<P>* linkEditSegCmd = nullptr;
+    symtab_command* symtab = nullptr;
+    dysymtab_command*    dynamicSymTab = nullptr;
+    linkedit_data_command*    functionStarts = nullptr;
+    linkedit_data_command*    dataInCode = nullptr;
+    uint32_t exportsTrieOffset = 0;
+    uint32_t exportsTrieSize = 0;
+    std::set<int> reexportDeps;
+    
+public:
+    
+    void optimize_loadcommands(dyld3::MachOAnalyzer* mh)
+    {
+        // update header flags
+        mh->flags &= 0x7FFFFFFF; // remove in-cache bit
+        
+        // update load commands
+        __block uint64_t cumulativeFileSize = 0;
+        __block int depIndex = 0;
+        Diagnostics diag;
+        mh->forEachLoadCommand(diag, ^(const load_command* cmd, bool &stop) {
+            switch ( cmd->cmd ) {
+                case macho_segment_command<P>::CMD: {
+                    auto segCmd = (macho_segment_command<P>*)cmd;
+                    segCmd->set_fileoff(cumulativeFileSize);
+                    segCmd->set_filesize(segCmd->vmsize());
+
+                    auto const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
+                    auto const sectionsEnd = &sectionsStart[segCmd->nsects()];
+                    for (auto 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. lldb only uses symbol table
+                    auto dyldInfo = (dyld_info_command*)cmd;
+                    exportsTrieOffset =  dyldInfo->export_off;
+                    exportsTrieSize = dyldInfo->export_size;
+                    dyldInfo->rebase_off = 0;
+                    dyldInfo->rebase_size = 0;
+                    dyldInfo->bind_off = 0;
+                    dyldInfo->bind_size = 0;
+                    dyldInfo->weak_bind_off = 0;
+                    dyldInfo->weak_bind_size = 0;
+                    dyldInfo->lazy_bind_off = 0;
+                    dyldInfo->lazy_bind_size = 0;
+                    dyldInfo->export_off = 0;
+                    dyldInfo->export_size = 0;
+                } break;
+                case LC_DYLD_EXPORTS_TRIE: {
+                    // don't put export trie into extracted dylib.  lldb only uses symbol table
+                    linkedit_data_command* exportsTrie = (linkedit_data_command*)cmd;
+                    exportsTrieOffset =  exportsTrie->dataoff;
+                    exportsTrieSize   = exportsTrie->datasize;
+                    exportsTrie->dataoff = 0;
+                    exportsTrie->datasize = 0;
+                } break;
+                case LC_SYMTAB:
+                    symtab = (symtab_command*)cmd;
+                    break;
+                case LC_DYSYMTAB:
+                    dynamicSymTab = (dysymtab_command*)cmd;
+                    break;
+                case LC_FUNCTION_STARTS:
+                    functionStarts = (linkedit_data_command*)cmd;
+                    break;
+                case LC_DATA_IN_CODE:
+                    dataInCode = (linkedit_data_command*)cmd;
+                    break;
+                case LC_LOAD_DYLIB:
+                case LC_LOAD_WEAK_DYLIB:
+                case LC_REEXPORT_DYLIB:
+                case LC_LOAD_UPWARD_DYLIB:
+                    depIndex++;
+                    if ( cmd->cmd == LC_REEXPORT_DYLIB ) {
+                        reexportDeps.insert(depIndex);
+                    }
+                    break;
+                default:
+                    break;
+            }
+        });
+
+        mh->removeLoadCommand(diag, ^(const load_command* cmd, bool& remove, bool &stop) {
+            switch ( cmd->cmd ) {
+                case LC_SEGMENT_SPLIT_INFO:
+                    // <rdar://problem/23212513> dylibs iOS 9 dyld caches have bogus LC_SEGMENT_SPLIT_INFO
+                    remove = true;
+                    stop = true;
+                    break;
+                default:
+                    break;
+            }
+        });
+}
+
+    int optimize_linkedit(std::vector<uint8_t> &new_linkedit_data, uint64_t textOffsetInCache, const void* mapped_cache)
+    {
+        // rebuild symbol table
+        if ( linkEditSegCmd == nullptr ) {
+            fprintf(stderr, "__LINKEDIT not found\n");
+            return -1;
+        }
+        if ( symtab == nullptr ) {
+            fprintf(stderr, "LC_SYMTAB not found\n");
+            return -1;
+        }
+        if ( dynamicSymTab == nullptr ) {
+            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<ExportInfoTrie::Entry> exports;
+        if ( exportsTrieSize != 0 ) {
+            const uint8_t* exportsStart = ((uint8_t*)mapped_cache) + exportsTrieOffset;
+            const uint8_t* exportsEnd = &exportsStart[exportsTrieSize];
+            ExportInfoTrie::parseTrie(exportsStart, exportsEnd, exports);
+            exports.erase(std::remove_if(exports.begin(), exports.end(), NotReExportSymbol(reexportDeps)), exports.end());
+        }
+
+        const DyldSharedCache*  cache = (DyldSharedCache*)mapped_cache;
+        macho_nlist<P>*         allLocalNlists = (macho_nlist<P>*)cache->getLocalNlistEntries();
+        __block macho_nlist<P>* localNlists = nullptr;
+        __block uint32_t        localNlistCount = 0;
+        cache->forEachLocalSymbolEntry(^(uint32_t dylibOffset, uint32_t nlistStartIndex, uint32_t nlistCount, bool& stop){
+            if (dylibOffset == textOffsetInCache) {
+                localNlists     = &allLocalNlists[nlistStartIndex];
+                localNlistCount = nlistCount;
+                stop            = true;
+            }
+        });
+        // 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 ( localNlistCount != 0 ) {
+            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<ExportInfoTrie::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.c_str(),
+                               it->name.c_str() + (it->name.size() + 1));
+            const char* importName = it->info.importName.c_str();
+            if ( *importName == '\0' )
+                importName = it->name.c_str();
+            t.set_n_value(newSymNames.size());
+            newSymNames.insert(newSymNames.end(),
+                               importName,
+                               importName + (strlen(importName) + 1));
+            newSymTab.push_back(t);
+        }
+        if ( localNlistCount != 0 ) {
+            const char* localStrings = cache->getLocalStrings();
+            // update load command to reflect new count of locals
+            dynamicSymTab->ilocalsym = (uint32_t)newSymTab.size();
+            dynamicSymTab->nlocalsym = localNlistCount;
+            // copy local symbols
+            for (uint32_t i=0; i < localNlistCount; ++i) {
+                const char* localName = &localStrings[localNlists[i].n_strx()];
+                if ( localName > localStrings + cache->getLocalStringsSize() )
+                    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->dataoff = (uint32_t)(newFunctionStartsOffset + linkEditSegCmd->fileoff());
+            functionStarts->datasize = functionStartsSize;
+        }
+        if ( dataInCode != NULL ) {
+            dataInCode->dataoff = (uint32_t)(newDataInCodeOffset + linkEditSegCmd->fileoff());
+            dataInCode->datasize = dataInCodeSize;
+        }
+
+        symtab->nsyms = newSymCount;
+        symtab->symoff = (uint32_t)(newSymTabOffset + linkEditSegCmd->fileoff());
+        symtab->stroff = (uint32_t)(newStringPoolOffset + linkEditSegCmd->fileoff());
+        symtab->strsize = (uint32_t)newSymNames.size();
+        dynamicSymTab->extreloff = 0;
+        dynamicSymTab->nextrel = 0;
+        dynamicSymTab->locreloff = 0;
+        dynamicSymTab->nlocrel = 0;
+        dynamicSymTab->indirectsymoff = (uint32_t)(newIndSymTabOffset + linkEditSegCmd->fileoff());
+        linkEditSegCmd->set_filesize(symtab->stroff + symtab->strsize - linkEditSegCmd->fileoff());
+        linkEditSegCmd->set_vmsize((linkEditSegCmd->filesize() + 4095) & (-4096));
+
+        return 0;
+    }
+
+};
+
+static void make_dirs(const char* file_path)
+{
+    //printf("make_dirs(%s)\n", file_path);
+    char dirs[strlen(file_path)+1];
+    strcpy(dirs, file_path);
+    char* lastSlash = strrchr(dirs, '/');
+    if ( lastSlash == NULL )
+        return;
+    lastSlash[1] = '\0';
+    struct stat stat_buf;
+    if ( stat(dirs, &stat_buf) != 0 ) {
+        char* afterSlash = &dirs[1];
+        char* slash;
+        while ( (slash = strchr(afterSlash, '/')) != NULL ) {
+            *slash = '\0';
+            ::mkdir(dirs, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
+            //printf("mkdir(%s)\n", dirs);
+            *slash = '/';
+            afterSlash = slash+1;
+        }
+    }
+}
+
+
+
+template <typename A>
+void 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) {
+        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;
+
+        //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;
+    dyld3::MachOAnalyzer* mh = (dyld3::MachOAnalyzer*)&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);
+
+    dylib_data.insert(dylib_data.end(), new_dylib_data.begin(), new_dylib_data.end());
+}
+
+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 (auto 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_TRUNC | 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;
+    }
+
+    std::vector<uint8_t> vec;
+    context.dylib_create_func(context.mapped_cache, vec, segInfo);
+    context.progress(context.count++, (unsigned)context.map.size());
+
+    // Write file data
+    if( write(fd, &vec.front(), vec.size()) == -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;
+    for (uint32_t i = 0; i != dyldSharedCache->header.mappingCount; ++i) {
+        sharedCacheRegions.emplace_back(std::make_pair(mappings[i].fileOffset, mappings[i].fileOffset + mappings[i].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);
+
+    // 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;
+    }
+
+    uint32_t pageSize = 1 << cd->pageSize;
+    uint32_t slotCountFromRegions = (uint32_t)((inBbufferSize + pageSize - 1) / pageSize);
+    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:
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+            dscDigestFormat = kCCDigestSHA1;
+#pragma clang diagnostic pop
+            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 + pageSize - 1) / pageSize);
+
+        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;
+            bool isDataPage = false;
+            for (unsigned mappingIndex = 1; mappingIndex != (dyldSharedCache->header.mappingCount - 1); ++mappingIndex) {
+                if ( (fileOffset >= mappings[mappingIndex].fileOffset) && (fileOffset < (mappings[mappingIndex].fileOffset + mappings[mappingIndex].size)) ) {
+                    isDataPage = true;
+                    break;
+                }
+            }
+            if ( isDataPage )
+                continue;
+
+            CCDigest(dscDigestFormat, (uint8_t*)mapped_cache + fileOffset, (size_t)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,
+                                              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;
+    }
+
+    close(cache_fd);
+
+    // 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;
+    }
+
+    // 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;
+    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");
+        munmap(mapped_cache, (size_t)statbuf.st_size);
+        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;
+}
+
+
+
+int dyld_shared_cache_extract_dylibs(const char* shared_cache_file_path, const char* extraction_root_path)
+{
+    return dyld_shared_cache_extract_dylibs_progress(shared_cache_file_path, extraction_root_path,
+                                                     ^(unsigned , unsigned) {} );
+}
+
+
+#if 0
+// test program
+#include <stdio.h>
+#include <stddef.h>
+#include <dlfcn.h>
+
+
+typedef int (*extractor_proc)(const char* shared_cache_file_path, const char* extraction_root_path,
+                              void (^progress)(unsigned current, unsigned total));
+
+int main(int argc, const char* argv[])
+{
+    if ( argc != 3 ) {
+        fprintf(stderr, "usage: dsc_extractor <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
+
+
+
+
diff --git a/dyld3/shared-cache/dsc_extractor.h b/dyld3/shared-cache/dsc_extractor.h
new file mode 100644 (file)
index 0000000..a8620d0
--- /dev/null
@@ -0,0 +1,43 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 
+ *
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef _DYLD_SHARED_CACHE_EXTRACTOR_
+#define _DYLD_SHARED_CACHE_EXTRACTOR_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif 
+
+extern int dyld_shared_cache_extract_dylibs(const char* shared_cache_file_path, const char* extraction_root_path);
+extern int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path, const char* extraction_root_path,
+                                                                                                       void (^progress)(unsigned current, unsigned total));
+
+#ifdef __cplusplus
+}
+#endif 
+
+#endif // _DYLD_SHARED_CACHE_EXTRACTOR_
+
diff --git a/dyld3/shared-cache/dsc_iterator.cpp b/dyld3/shared-cache/dsc_iterator.cpp
new file mode 100644 (file)
index 0000000..f20cf7b
--- /dev/null
@@ -0,0 +1,96 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 
+ *
+ * Copyright (c) 2009-2012 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <Availability.h>
+
+
+#include "dsc_iterator.h"
+#define NO_ULEB
+#include "DyldSharedCache.h"
+#include "MachOAnalyzer.h"
+
+
+static void forEachDylibInCache(const void* shared_cache_file, void (^handler)(const dyld_cache_image_info* cachedDylibInfo, bool isAlias))
+{
+    const dyld_cache_header*       header   = (dyld_cache_header*)shared_cache_file;
+    const dyld_cache_image_info*   dylibs   = (dyld_cache_image_info*)((char*)shared_cache_file + header->imagesOffset);
+    const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)shared_cache_file + header->mappingOffset);
+    if ( mappings[0].fileOffset != 0 )
+        return;
+    uint64_t firstImageOffset = 0;
+    uint64_t firstRegionAddress = mappings[0].address;
+    for (uint32_t i=0; i < header->imagesCount; ++i) {
+        uint64_t offset = dylibs[i].address - firstRegionAddress;
+        if ( firstImageOffset == 0 )
+            firstImageOffset = offset;
+        // skip over aliases
+        bool isAlias = (dylibs[i].pathFileOffset < firstImageOffset);
+        handler(&dylibs[i], isAlias);
+    }
+}
+
+
+extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t shared_cache_size,
+                                         void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) {
+    const dyld_cache_header*       header             = (dyld_cache_header*)shared_cache_file;
+    const dyld_cache_mapping_info* mappings           = (dyld_cache_mapping_info*)((char*)shared_cache_file + header->mappingOffset);
+    const uint64_t                 unslideLoadAddress = mappings[0].address;
+
+    __block uint32_t index = 0;
+    __block int      result = 0;
+    forEachDylibInCache(shared_cache_file, ^(const dyld_cache_image_info* cachedDylibInfo, bool isAlias) {
+        uint64_t                    imageCacheOffset = cachedDylibInfo->address - unslideLoadAddress;
+        const dyld3::MachOAnalyzer* ma               = (dyld3::MachOAnalyzer*)((uint8_t*)shared_cache_file + imageCacheOffset);
+        const char*                 dylibPath        = (char*)shared_cache_file + cachedDylibInfo->pathFileOffset;
+
+        dyld_shared_cache_dylib_info dylibInfo;
+        uuid_t                       uuid;
+        dylibInfo.version    = 2;
+        dylibInfo.machHeader = ma;
+        dylibInfo.path       = dylibPath;
+        dylibInfo.modTime    = cachedDylibInfo->modTime;
+        dylibInfo.inode      = cachedDylibInfo->inode;
+        dylibInfo.isAlias    = isAlias;
+        ma->getUuid(uuid);
+        dylibInfo.uuid       = &uuid;
+        ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) {
+            if ( info.fileSize > info.vmSize ) {
+                stop = true;
+                return;
+            }
+            dyld_shared_cache_segment_info segInfo;
+            segInfo.version       = 2;
+            segInfo.name          = info.segName;
+            segInfo.fileOffset    = info.fileOffset;
+            segInfo.fileSize      = info.vmSize;
+            segInfo.address       = info.vmAddr;
+            segInfo.addressOffset = info.vmAddr - unslideLoadAddress;
+            callback(&dylibInfo, &segInfo);
+        });
+        index++;
+    });
+    return result;
+}
diff --git a/dyld3/shared-cache/dsc_iterator.h b/dyld3/shared-cache/dsc_iterator.h
new file mode 100644 (file)
index 0000000..00fb835
--- /dev/null
@@ -0,0 +1,66 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 
+ *
+ * Copyright (c) 2009-2012 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <stdint.h>
+#include <uuid/uuid.h>
+
+struct dyld_shared_cache_dylib_info {
+       uint32_t                version;                // current version 2
+       // following fields all exist in version 1
+       uint32_t                isAlias;                // this is alternate path (symlink)
+       const void*             machHeader;             // of dylib in mapped cached file
+       const char*             path;                   // of dylib
+       const uuid_t*   uuid;                   // of dylib, or NULL is missing
+       // following fields all exist in version 2
+       uint64_t                inode;                  // of dylib file or path hash
+       uint64_t                modTime;                // of dylib file
+};
+typedef struct dyld_shared_cache_dylib_info dyld_shared_cache_dylib_info;
+
+struct dyld_shared_cache_segment_info {
+       uint64_t                version;                // initial version 1
+       // following fields exist in version 1
+       const char*             name;                   // of segment
+       uint64_t                fileOffset;             // of segment in cache file
+       uint64_t                fileSize;               // of segment
+       uint64_t                address;                // of segment when cache mapped with ASLR (sliding) off
+    // following fields exist in version 2
+       uint64_t                addressOffset;  // of segment from base of mapped cache
+};
+typedef struct dyld_shared_cache_segment_info dyld_shared_cache_segment_info;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+// Given a pointer and size of an in-memory copy of a dyld shared cache file,
+// this routine will call the callback block once for each segment in each dylib 
+// in the shared cache file.  
+// Returns -1 if there was an error, otherwise 0.
+extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t shared_cache_size,
+                                                                       void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo));
+
+#ifdef __cplusplus
+}
+#endif
+
index d5033e27399e5951e97ede9d0e2b82753d2a0954..fd74a227c350f49de09b973ede1b8e82732d87b4 100644 (file)
@@ -38,8 +38,8 @@ struct dyld_cache_header
     uint64_t    dyldBaseAddress;        // base address of dyld when cache was built
     uint64_t    codeSignatureOffset;    // file offset of code signature blob
     uint64_t    codeSignatureSize;      // size of code signature blob (zero means to end of file)
-    uint64_t    slideInfoOffset;        // file offset of kernel slid info
-    uint64_t    slideInfoSize;          // size of kernel slid info
+    uint64_t    slideInfoOffsetUnused;  // unused.  Used to be file offset of kernel slid info
+    uint64_t    slideInfoSizeUnused;    // unused.  Used to be size of kernel slid info
     uint64_t    localSymbolsOffset;     // file offset of where local symbols are stored
     uint64_t    localSymbolsSize;       // size of local symbols information
     uint8_t     uuid[16];               // unique value for each shared cache file
@@ -76,6 +76,8 @@ struct dyld_cache_header
     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
+    uint32_t    mappingWithSlideOffset; // file offset to first dyld_cache_mapping_and_slide_info
+    uint32_t    mappingWithSlideCount;  // number of dyld_cache_mapping_and_slide_info entries
 };
 
 // Uncomment this and check the build errors for the current mapping offset to check against when adding new fields.
@@ -90,6 +92,24 @@ struct dyld_cache_mapping_info {
     uint32_t    initProt;
 };
 
+// Contains the flags for the dyld_cache_mapping_and_slide_info flgs field
+enum {
+    DYLD_CACHE_MAPPING_AUTH_DATA            = 1 << 0U,
+    DYLD_CACHE_MAPPING_DIRTY_DATA           = 1 << 1U,
+    DYLD_CACHE_MAPPING_CONST_DATA           = 1 << 2U,
+};
+
+struct dyld_cache_mapping_and_slide_info {
+    uint64_t    address;
+    uint64_t    size;
+    uint64_t    fileOffset;
+    uint64_t    slideInfoFileOffset;
+    uint64_t    slideInfoFileSize;
+    uint64_t    flags;
+    uint32_t    maxProt;
+    uint32_t    initProt;
+};
+
 struct dyld_cache_image_info
 {
     uint64_t    address;
@@ -474,7 +494,8 @@ struct dyld_cache_patchable_export
 struct dyld_cache_patchable_location
 {
     uint64_t    cacheOffset             : 32,
-                addend                  : 12,    // +/- 2048
+                high7                   : 7,
+                addend                  : 5,    // 0..31
                 authenticated           : 1,
                 usesAddressDiversity    : 1,
                 key                     : 2,
@@ -482,8 +503,12 @@ struct dyld_cache_patchable_location
 };
 
 
+// This is the  location of the macOS shared cache on macOS 11.0 and later
+#define MACOSX_MRM_DYLD_SHARED_CACHE_DIR   "/System/Library/dyld/"
+
+// This is old define for the old location of the dyld cache
+#define MACOSX_DYLD_SHARED_CACHE_DIR       MACOSX_MRM_DYLD_SHARED_CACHE_DIR
 
-#define MACOSX_DYLD_SHARED_CACHE_DIR       "/private/var/db/dyld/"
 #define IPHONE_DYLD_SHARED_CACHE_DIR       "/System/Library/Caches/com.apple.dyld/"
 #if !TARGET_OS_SIMULATOR
   #define DYLD_SHARED_CACHE_BASE_NAME        "dyld_shared_cache_"
index edbc7049a42a68e34a55933525c04960eb9a972b..6038177a86f6d47af5fabc8aa6613a1dbd9d7b96 100644 (file)
@@ -48,6 +48,7 @@
 #include "ClosureBuilder.h"
 #include "ClosurePrinter.h"
 #include "ClosureFileSystemPhysical.h"
+#include "RootsChecker.h"
 
 using dyld3::closure::ImageArray;
 using dyld3::closure::Image;
@@ -81,15 +82,16 @@ static const DyldSharedCache* mapCacheFile(const char* path)
     }
     const dyld_cache_header*       header   = (dyld_cache_header*)firstPage;
        const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(firstPage + header->mappingOffset);
+    const dyld_cache_mapping_info* lastMapping = &mappings[header->mappingCount - 1];
 
-    size_t vmSize = (size_t)(mappings[2].address + mappings[2].size - mappings[0].address);
+    size_t vmSize = (size_t)(lastMapping->address + lastMapping->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) {
+    for (int i=0; i < header->mappingCount; ++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) {
@@ -273,13 +275,8 @@ int main(int argc, const char* argv[])
         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
         size_t cacheLength;
         dyldCache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLength);
-#endif
     }
     dyld3::Platform            platform = dyldCache->platform();
     const dyld3::GradedArchs&  archs    = dyld3::GradedArchs::forName(dyldCache->archName(), true);
@@ -291,11 +288,13 @@ int main(int argc, const char* argv[])
         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());
+        if ( auto others = dyldCache->otherOSImageArray() )
+            imagesArrays.push_back(others);
 
         dyld3::closure::FileSystemPhysical fileSystem(fsRootPath, fsOverlayPath);
+        dyld3::RootsChecker rootsChecker;
         ClosureBuilder::AtPath atPathHanding = allowAtPaths ? ClosureBuilder::AtPath::all : ClosureBuilder::AtPath::none;
-        ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, fileSystem, dyldCache, dyldCacheIsLive, archs, pathOverrides, atPathHanding, true, nullptr, platform, nullptr);
+        ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, fileSystem, rootsChecker, dyldCache, dyldCacheIsLive, archs, pathOverrides, atPathHanding, true, nullptr, platform, nullptr);
         if (forceInvalidFormatVersion)
             builder.setDyldCacheInvalidFormatVersion();
 
@@ -325,13 +324,13 @@ int main(int argc, const char* argv[])
                 else {
                     Diagnostics diag;
                     char realerPath[MAXPATHLEN];
-                    dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, li.image()->path(), archs, platform, realerPath);
+                    dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, li.image()->path(), archs, builder.platform(), realerPath);
                     li.setLoadedAddress((const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent);
                 }
             }
 
             ClosureBuilder::AtPath atPathHandingDlopen = allowAtPaths ? ClosureBuilder::AtPath::all : ClosureBuilder::AtPath::onlyInRPaths;
-            ClosureBuilder dlopenBuilder(nextNum, fileSystem, dyldCache, dyldCacheIsLive, archs, pathOverrides, atPathHandingDlopen, true, nullptr, platform, nullptr);
+            ClosureBuilder dlopenBuilder(nextNum, fileSystem, rootsChecker, dyldCache, dyldCacheIsLive, archs, pathOverrides, atPathHandingDlopen, true, nullptr, builder.platform(), nullptr);
             if (forceInvalidFormatVersion)
                 dlopenBuilder.setDyldCacheInvalidFormatVersion();
 
@@ -373,7 +372,8 @@ int main(int argc, const char* argv[])
         if ( closure != nullptr ) {
             STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 3);
             imagesArrays.push_back(dyldCache->cachedDylibsImageArray());
-            imagesArrays.push_back(dyldCache->otherOSImageArray());
+            if ( auto others = dyldCache->otherOSImageArray() )
+                imagesArrays.push_back(others);
             imagesArrays.push_back(closure->images());
             dyld3::closure::printClosureAsJSON(closure, imagesArrays, verboseFixups, printRaw, dyldCache);
         }
@@ -400,7 +400,8 @@ int main(int argc, const char* argv[])
         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());
+            if ( auto others = dyldCache->otherOSImageArray() )
+                imagesArrays.push_back(others);
             dyld3::closure::printImageAsJSON(image, imagesArrays, verboseFixups);
         }
         else {
index 0bbcc1e3a57fe24a1e9b773a584d3d462550a273..2ec8d1fce57c5787e7ff50ddfa0ec39b73216d5e 100644 (file)
@@ -49,6 +49,7 @@
 
 #include <vector>
 #include <array>
+#include <list>
 #include <set>
 #include <map>
 #include <unordered_set>
@@ -102,18 +103,18 @@ int runCommandAndWait(Diagnostics& diags, const char* args[])
     return res;
 }
 
-void processRoots(Diagnostics& diags, std::set<std::string>& roots, const char *tempRootsDir)
+void processRoots(Diagnostics& diags, std::list<std::string>& roots, const char *tempRootsDir)
 {
-    std::set<std::string> processedRoots;
-    struct stat           sb;
-    int                   res = 0;
-    const char*           args[8];
+    std::list<std::string>  processedRoots;
+    struct stat             sb;
+    int                     res = 0;
+    const char*             args[8];
 
     for (const auto& root : roots) {
         res = stat(root.c_str(), &sb);
 
         if (res == 0 && S_ISDIR(sb.st_mode)) {
-            processedRoots.insert(root);
+            processedRoots.push_back(root);
             continue;
         }
 
@@ -171,13 +172,13 @@ void processRoots(Diagnostics& diags, std::set<std::string>& roots, const char *
                 return;
         }
 
-        processedRoots.insert(tempRootDir);
+        processedRoots.push_back(tempRootDir);
     }
 
     roots = processedRoots;
 }
 
-void writeRootList(const std::string& dstRoot, const std::set<std::string>& roots)
+void writeRootList(const std::string& dstRoot, const std::list<std::string>& roots)
 {
     if (roots.size() == 0)
         return;
@@ -194,16 +195,31 @@ void writeRootList(const std::string& dstRoot, const std::set<std::string>& root
     ::fclose(froots);
 }
 
+struct FilteredCopyOptions {
+    Diagnostics*            diags               = nullptr;
+    std::set<std::string>*  cachePaths          = nullptr;
+    std::set<std::string>*  dylibsFoundInRoots  = nullptr;
+};
+
 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))
+    const FilteredCopyOptions *userData = (const FilteredCopyOptions*)BOMCopierUserData(copier);
+
+    // Don't copy from the artifact if the dylib is actally in a -root
+    if ( userData->dylibsFoundInRoots->count(absolutePath) != 0 ) {
+        userData->diags->verbose("Skipping copying dylib from shared cache artifact as it is in a -root: '%s'\n", absolutePath.c_str());
+        return BOMCopierSkipFile;
+    }
+
+    for (const std::string& cachePath : *userData->cachePaths) {
+        if (startsWith(cachePath, absolutePath)) {
+            userData->diags->verbose("Copying dylib from shared cache artifact: '%s'\n", absolutePath.c_str());
             return BOMCopierContinue;
+        }
     }
-    if (cachePaths->count(absolutePath)) {
+    if (userData->cachePaths->count(absolutePath)) {
+        userData->diags->verbose("Copying dylib from shared cache artifact: '%s'\n", absolutePath.c_str());
         return BOMCopierContinue;
     }
     return BOMCopierSkipFile;
@@ -228,7 +244,7 @@ static Platform stringToPlatform(Diagnostics& diags, const std::string& str) {
         return unknown;
     if (str == "unknown")
         return unknown;
-    if (str == "macOS")
+    if ( (str == "macOS") || (str == "osx") )
         return macOS;
     if (str == "iOS")
         return iOS;
@@ -266,28 +282,33 @@ static FileFlags stringToFileFlags(Diagnostics& diags, const std::string& str) {
         return DylibOrderFile;
     if (str == "DirtyDataOrderFile")
         return DirtyDataOrderFile;
+    if (str == "ObjCOptimizationsFile")
+        return ObjCOptimizationsFile;
     return NoFlags;
 }
 
 struct SharedCacheBuilderOptions {
-    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           buildAllPath;
-    std::string           resultPath;
-    std::string           baselineDifferenceResultPath;
-    std::string           baselineCacheMapPath;
-    bool                  baselineCopyRoots = false;
+    Diagnostics                 diags;
+    std::list<std::string>      roots;
+    std::string                 dylibCacheDir;
+    std::string                 artifactDir;
+    std::string                 release;
+    bool                        emitDevCaches = true;
+    bool                        emitCustomerCaches = true;
+    bool                        emitElidedDylibs = true;
+    bool                        listConfigs = false;
+    bool                        copyRoots = false;
+    bool                        debug = false;
+    bool                        useMRM = false;
+    std::string                 dstRoot;
+    std::string                 emitJSONPath;
+    std::string                 buildAllPath;
+    std::string                 resultPath;
+    std::string                 baselineDifferenceResultPath;
+    std::list<std::string>      baselineCacheMapPaths;
+    bool                        baselineCopyRoots = false;
+    bool                        emitMapFiles = false;
+    std::set<std::string>       cmdLineArchs;
 };
 
 static void loadMRMFiles(Diagnostics& diags,
@@ -343,7 +364,49 @@ static void unloadMRMFiles(std::vector<std::pair<const void*, size_t>>& mappedFi
         ::munmap((void*)mappedFile.first, mappedFile.second);
 }
 
-static void writeMRMResults(bool cacheBuildSuccess, MRMSharedCacheBuilder* sharedCacheBuilder, const SharedCacheBuilderOptions& options) {
+static ssize_t write64(int fildes, const void *buf, size_t nbyte)
+{
+    unsigned char* uchars = (unsigned char*)buf;
+    ssize_t total = 0;
+
+    while (nbyte)
+    {
+        /*
+         * If we were writing socket- or stream-safe code we'd chuck the
+         * entire buf to write(2) and then gracefully re-request bytes that
+         * didn't get written. But write(2) will return EINVAL if you ask it to
+         * write more than 2^31-1 bytes. So instead we actually need to throttle
+         * the input to write.
+         *
+         * Historically code using write(2) to write to disk will assert that
+         * that all of the requested bytes were written. It seems harmless to
+         * re-request bytes as one does when writing to streams, with the
+         * compromise that we will return immediately when write(2) returns 0
+         * bytes written.
+         */
+        size_t limit = 0x7FFFFFFF;
+        size_t towrite = nbyte < limit ? nbyte : limit;
+        ssize_t wrote = write(fildes, uchars, towrite);
+        if (-1 == wrote)
+        {
+            return -1;
+        }
+        else if (0 == wrote)
+        {
+            break;
+        }
+        else
+        {
+            nbyte -= wrote;
+            uchars += wrote;
+            total += wrote;
+        }
+    }
+
+    return total;
+}
+
+static bool writeMRMResults(bool cacheBuildSuccess, MRMSharedCacheBuilder* sharedCacheBuilder, const SharedCacheBuilderOptions& options) {
     if (!cacheBuildSuccess) {
         uint64_t errorCount = 0;
         if (const char* const* errors = getErrors(sharedCacheBuilder, &errorCount)) {
@@ -375,7 +438,7 @@ static void writeMRMResults(bool cacheBuildSuccess, MRMSharedCacheBuilder* share
     }
 
     if (!cacheBuildSuccess) {
-        exit(-1);
+        return false;
     }
 
     // If we built caches, then write everything out.
@@ -404,7 +467,7 @@ static void writeMRMResults(bool cacheBuildSuccess, MRMSharedCacheBuilder* share
                 int fd = mkstemp(pathTemplateSpace);
                 if ( fd != -1 ) {
                     ::ftruncate(fd, result.size);
-                    uint64_t writtenSize = pwrite(fd, result.data, result.size, 0);
+                    uint64_t writtenSize = write64(fd, result.data, result.size);
                     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) {
@@ -425,7 +488,60 @@ static void writeMRMResults(bool cacheBuildSuccess, MRMSharedCacheBuilder* share
                 }
             }
         }
+
+        // Give up if we couldn't write the caches
+        if (!cacheBuildSuccess) {
+            return false;
+        }
     }
+
+    // Emit the map files
+    if ( options.emitMapFiles && !options.dstRoot.empty() ) {
+        uint64_t cacheResultCount = 0;
+        if (const CacheResult* const* cacheResults = getCacheResults(sharedCacheBuilder, &cacheResultCount)) {
+            for (uint64_t i = 0, e = cacheResultCount; i != e; ++i) {
+                const CacheResult& result = *(cacheResults[i]);
+                std::string_view jsonData = result.mapJSON;
+                if ( jsonData.empty() )
+                    continue;
+
+                const std::string path = options.dstRoot + "/System/Library/dyld/" + result.loggingPrefix + ".json";
+                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, jsonData.size());
+                    uint64_t writtenSize = write64(fd, jsonData.data(), jsonData.size());
+                    if ( writtenSize == jsonData.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;
+                }
+            }
+        }
+
+        // Give up if we couldn't write the cache maps
+        if (!cacheBuildSuccess) {
+            return false;
+        }
+    }
+
+    return true;
 }
 
 static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuilderOptions& options,
@@ -464,50 +580,90 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil
         diags.error("Build options archs node is not an array\n");
         return;
     }
+    std::set<std::string> jsonArchs;
     const char* archs[archsNode.array.size()];
-    uint64_t archIndex = 0;
+    uint64_t numArchs = 0;
     for (const dyld3::json::Node& archNode : archsNode.array) {
-        archs[archIndex++] = dyld3::json::parseRequiredString(diags, archNode).c_str();
+        const char* archName = dyld3::json::parseRequiredString(diags, archNode).c_str();
+        jsonArchs.insert(archName);
+        if ( options.cmdLineArchs.empty() || options.cmdLineArchs.count(archName) ) {
+            archs[numArchs++] = archName;
+        }
+    }
+
+    // Check that the command line archs are in the JSON list
+    if ( !options.cmdLineArchs.empty() ) {
+        for (const std::string& cmdLineArch : options.cmdLineArchs) {
+            if ( !jsonArchs.count(cmdLineArch) ) {
+                std::string validArchs = "";
+                for (const std::string& jsonArch : jsonArchs) {
+                    if ( !validArchs.empty() ) {
+                        validArchs += ", ";
+                    }
+                    validArchs += jsonArch;
+                }
+                diags.error("Command line -arch '%s' is not valid for this device.  Valid archs are (%s)\n", cmdLineArch.c_str(), validArchs.c_str());
+                return;
+            }
+        }
     }
 
     // Parse the rest of the options node.
-    BuildOptions_v1 buildOptions;
+    BuildOptions_v2 buildOptions;
     buildOptions.version                            = dyld3::json::parseRequiredInt(diags, dyld3::json::getRequiredValue(diags, buildOptionsNode, "version"));
     buildOptions.updateName                         = dyld3::json::parseRequiredString(diags, dyld3::json::getRequiredValue(diags, buildOptionsNode, "updateName")).c_str();
     buildOptions.deviceName                         = dyld3::json::parseRequiredString(diags, dyld3::json::getRequiredValue(diags, buildOptionsNode, "deviceName")).c_str();
     buildOptions.disposition                        = stringToDisposition(diags, dyld3::json::parseRequiredString(diags, dyld3::json::getRequiredValue(diags, buildOptionsNode, "disposition")));
     buildOptions.platform                           = stringToPlatform(diags, dyld3::json::parseRequiredString(diags, dyld3::json::getRequiredValue(diags, buildOptionsNode, "platform")));
     buildOptions.archs                              = archs;
-    buildOptions.numArchs                           = archsNode.array.size();
+    buildOptions.numArchs                           = numArchs;
     buildOptions.verboseDiagnostics                 = options.debug;
     buildOptions.isLocallyBuiltCache                = true;
 
+    // optimizeForSize was added in version 2
+    buildOptions.optimizeForSize = false;
+    if ( buildOptions.version >= 2 ) {
+        buildOptions.optimizeForSize                = dyld3::json::parseRequiredBool(diags, dyld3::json::getRequiredValue(diags, buildOptionsNode, "optimizeForSize"));
+    }
+
     if (diags.hasError())
         return;
 
     // Override the disposition if we don't want certaion caches.
-    if (!options.emitDevCaches) {
-        switch (buildOptions.disposition) {
-            case Unknown:
-                // Nothing we can do here as we can't assume what caches are built here.
+    switch (buildOptions.disposition) {
+        case Unknown:
+            // Nothing we can do here as we can't assume what caches are built here.
+            break;
+        case InternalDevelopment:
+            if (!options.emitDevCaches && !options.emitCustomerCaches) {
+                diags.error("both -no_customer_cache and -no_development_cache passed\n");
                 break;
-            case InternalDevelopment:
+            }
+            if (!options.emitDevCaches) {
                 // This builds both caches, but we don't want dev
                 buildOptions.disposition = Customer;
-                break;
-            case Customer:
-                // This is already only the customer cache
-                break;
-            case InternalMinDevelopment:
+            }
+            if (!options.emitCustomerCaches) {
+                // This builds both caches, but we don't want customer
+                buildOptions.disposition = InternalMinDevelopment;
+            }
+            break;
+        case Customer:
+            if (!options.emitCustomerCaches) {
+                diags.error("Cannot request no customer cache for Customer as that is already only a customer cache\n");
+            }
+            break;
+        case InternalMinDevelopment:
+            if (!options.emitDevCaches) {
                 diags.error("Cannot request no dev cache for InternalMinDevelopment as that is already only a dev cache\n");
-                break;
-        }
+            }
+            break;
     }
 
     if (diags.hasError())
         return;
 
-    struct MRMSharedCacheBuilder* sharedCacheBuilder = createSharedCacheBuilder(&buildOptions);
+    struct MRMSharedCacheBuilder* sharedCacheBuilder = createSharedCacheBuilder((const BuildOptions_v1*)&buildOptions);
 
     // Parse the files
     if (filesNode.array.empty()) {
@@ -516,8 +672,9 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil
     }
 
     std::vector<std::tuple<std::string, std::string, FileFlags>> inputFiles;
+    std::set<std::string> dylibsFoundInRoots;
     for (const dyld3::json::Node& fileNode : filesNode.array) {
-        const std::string& path = dyld3::json::parseRequiredString(diags, dyld3::json::getRequiredValue(diags, fileNode, "path")).c_str();
+        std::string path = dyld3::json::parseRequiredString(diags, dyld3::json::getRequiredValue(diags, fileNode, "path")).c_str();
         FileFlags fileFlags     = stringToFileFlags(diags, dyld3::json::parseRequiredString(diags, dyld3::json::getRequiredValue(diags, fileNode, "flags")));
 
         // We can optionally have a sourcePath entry which is the path to get the source content from instead of the install path
@@ -537,6 +694,8 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil
             sourcePath = path;
         }
 
+        std::string buildPath = sourcePath;
+
         // Check if one of the -root's has this path
         bool foundInOverlay = false;
         for (const std::string& overlay : options.roots) {
@@ -544,8 +703,9 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil
             std::string filePath = overlay + path;
             if (!stat(filePath.c_str(), &sb)) {
                 foundInOverlay = true;
-                diags.verbose("Taking '%s' from overlay instead of dylib cache\n", path.c_str());
+                diags.verbose("Taking '%s' from overlay '%s' instead of dylib cache\n", path.c_str(), overlay.c_str());
                 inputFiles.push_back({ filePath, path, fileFlags });
+                dylibsFoundInRoots.insert(path);
                 break;
             }
         }
@@ -554,17 +714,15 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil
             continue;
 
         // Build paths are relative to the build artifact root directory.
-        std::string buildPath;
         switch (fileFlags) {
             case NoFlags:
             case MustBeInCache:
             case ShouldBeExcludedFromCacheIfUnusedLeaf:
             case RequiredClosure:
-                buildPath = "." + sourcePath;
-                break;
             case DylibOrderFile:
             case DirtyDataOrderFile:
-                buildPath = "." + sourcePath;
+            case ObjCOptimizationsFile:
+                buildPath = "." + buildPath;
                 break;
         }
         inputFiles.push_back({ buildPath, path, fileFlags });
@@ -573,10 +731,10 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil
     if (diags.hasError())
         return;
 
-    // Parse the baseline from the map if we have it
-    std::set<std::string> baselineDylibs;
-    if ( !options.baselineCacheMapPath.empty() ) {
-        dyld3::json::Node mapNode = dyld3::json::readJSON(diags, options.baselineCacheMapPath.c_str());
+    // Parse the baseline from the map(s) if we have it
+    std::set<std::string> unionBaselineDylibs;
+    for (const std::string& baselineCacheMapPath : options.baselineCacheMapPaths) {
+        dyld3::json::Node mapNode = dyld3::json::readJSON(diags, baselineCacheMapPath.c_str());
         if (diags.hasError())
             return;
 
@@ -614,12 +772,12 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil
                 diags.error("Image path node is not a string\n");
                 return;
             }
-            baselineDylibs.insert(pathNode.value);
+            unionBaselineDylibs.insert(pathNode.value);
         }
     }
 
     std::vector<std::pair<const void*, size_t>> mappedFiles;
-    loadMRMFiles(diags, sharedCacheBuilder, inputFiles, mappedFiles, baselineDylibs);
+    loadMRMFiles(diags, sharedCacheBuilder, inputFiles, mappedFiles, unionBaselineDylibs);
 
     if (diags.hasError())
         return;
@@ -631,7 +789,7 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil
             return;
         }
         for (const dyld3::json::Node& symlinkNode : symlinksNode->array) {
-            const std::string& fromPath = dyld3::json::parseRequiredString(diags, dyld3::json::getRequiredValue(diags, symlinkNode, "path")).c_str();
+            std::string fromPath = dyld3::json::parseRequiredString(diags, dyld3::json::getRequiredValue(diags, symlinkNode, "path")).c_str();
             const std::string& toPath   = dyld3::json::parseRequiredString(diags, dyld3::json::getRequiredValue(diags, symlinkNode, "target")).c_str();
             addSymlink(sharedCacheBuilder, fromPath.c_str(), toPath.c_str());
         }
@@ -641,34 +799,48 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil
         return;
 
     // Don't create a directory if we are skipping writes, which means we have no dstRoot set
-    if (!options.dstRoot.empty())
-        (void)mkpath_np((options.dstRoot + "/System/Library/Caches/com.apple.dyld/").c_str(), 0755);
+    if (!options.dstRoot.empty()) {
+        if ( buildOptions.platform == macOS ) {
+            (void)mkpath_np((options.dstRoot + MACOSX_MRM_DYLD_SHARED_CACHE_DIR).c_str(), 0755);
+        } else {
+            (void)mkpath_np((options.dstRoot + IPHONE_DYLD_SHARED_CACHE_DIR).c_str(), 0755);
+        }
+    }
 
     // Actually build the cache.
     bool cacheBuildSuccess = runSharedCacheBuilder(sharedCacheBuilder);
 
     // Compare this cache to the baseline cache and see if we have any roots to copy over
     if (!options.baselineDifferenceResultPath.empty() || options.baselineCopyRoots) {
-        std::set<std::string> newDylibs;
+        std::set<std::string> dylibsInNewCaches;
+        std::set<std::string> simulatorSupportDylibs;
         if (cacheBuildSuccess) {
             uint64_t fileResultCount = 0;
             if (const char* const* fileResults = getFilesToRemove(sharedCacheBuilder, &fileResultCount)) {
                 for (uint64_t i = 0; i != fileResultCount; ++i)
-                    newDylibs.insert(fileResults[i]);
+                    dylibsInNewCaches.insert(fileResults[i]);
+            }
+            if ( buildOptions.platform == Platform::macOS ) {
+                // macOS has to leave the simulator support binaries on disk
+                // It won't put them in the result of getFilesToRemove() so we need to manually add them
+                simulatorSupportDylibs.insert("/usr/lib/system/libsystem_kernel.dylib");
+                simulatorSupportDylibs.insert("/usr/lib/system/libsystem_platform.dylib");
+                simulatorSupportDylibs.insert("/usr/lib/system/libsystem_pthread.dylib");
             }
         }
 
         if (options.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);
+            // Work out the set of dylibs in the old caches but not the new ones
+            std::set<std::string> dylibsMissingFromNewCaches;
+            for (const std::string& baselineDylib : unionBaselineDylibs) {
+                if ( !dylibsInNewCaches.count(baselineDylib) && !simulatorSupportDylibs.count(baselineDylib))
+                    dylibsMissingFromNewCaches.insert(baselineDylib);
             }
 
-            if (!dylibsMissingFromNewCache.empty()) {
+            if (!dylibsMissingFromNewCaches.empty()) {
                 BOMCopier copier = BOMCopierNewWithSys(BomSys_default());
-                BOMCopierSetUserData(copier, (void*)&dylibsMissingFromNewCache);
+                FilteredCopyOptions userData = { &diags, &dylibsMissingFromNewCaches, &dylibsFoundInRoots };
+                BOMCopierSetUserData(copier, (void*)&userData);
                 BOMCopierSetCopyFileStartedHandler(copier, filteredCopyIncludingPaths);
                 std::string dylibCacheRootDir = realFilePath(options.dylibCacheDir);
                 if (dylibCacheRootDir == "") {
@@ -678,7 +850,7 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil
                 BOMCopierCopy(copier, dylibCacheRootDir.c_str(), options.dstRoot.c_str());
                 BOMCopierFree(copier);
 
-                for (const std::string& dylibMissingFromNewCache : dylibsMissingFromNewCache) {
+                for (const std::string& dylibMissingFromNewCache : dylibsMissingFromNewCaches) {
                     diags.verbose("Dylib missing from new cache: '%s'\n", dylibMissingFromNewCache.c_str());
                 }
             }
@@ -692,7 +864,7 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil
             // Work out the set of dylibs in the cache and taken from the -root
             NSMutableArray<NSString*>* dylibsFromRoots = [NSMutableArray array];
             for (auto& root : options.roots) {
-                for (const std::string& dylibInstallName : newDylibs) {
+                for (const std::string& dylibInstallName : dylibsInNewCaches) {
                     struct stat sb;
                     std::string filePath = root + "/" + dylibInstallName;
                     if (!stat(filePath.c_str(), &sb)) {
@@ -703,8 +875,8 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil
 
             // 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))
+            for (const std::string& newDylib : dylibsInNewCaches) {
+                if (!unionBaselineDylibs.count(newDylib))
                     [dylibsMissingFromBaselineCache addObject:cppToObjStr(newDylib)];
             }
 
@@ -721,11 +893,15 @@ static void buildCacheFromJSONManifest(Diagnostics& diags, const SharedCacheBuil
         }
     }
 
-    writeMRMResults(cacheBuildSuccess, sharedCacheBuilder, options);
+    bool wroteCaches = writeMRMResults(cacheBuildSuccess, sharedCacheBuilder, options);
 
     destroySharedCacheBuilder(sharedCacheBuilder);
 
     unloadMRMFiles(mappedFiles);
+
+    if (!wroteCaches) {
+        exit(-1);
+    }
 }
 
 int main(int argc, const char* argv[])
@@ -752,19 +928,26 @@ int main(int argc, const char* argv[])
                         fprintf(stderr, "-root path doesn't exist: %s\n", argv[i]);
                         exit(-1);
                     }
-                    options.roots.insert(realpath);
+                    if ( std::find(options.roots.begin(), options.roots.end(), realpath) == options.roots.end() ) {
+                        // Push roots on to the front so that each -root overrides previous entries
+                        options.roots.push_front(realpath);
+                    }
                 } else if (strcmp(arg, "-copy_roots") == 0) {
                     options.copyRoots = true;
                 } else if (strcmp(arg, "-dylib_cache") == 0) {
                     options.dylibCacheDir = realPath(argv[++i]);
                 } else if (strcmp(arg, "-artifact") == 0) {
                     options.artifactDir = realPath(argv[++i]);
-                } else if (strcmp(arg, "-no_development_cache") == 0) {
-                    options.emitDevCaches = false;
                 } else if (strcmp(arg, "-no_overflow_dylibs") == 0) {
                     options.emitElidedDylibs = false;
+                } else if (strcmp(arg, "-no_development_cache") == 0) {
+                    options.emitDevCaches = false;
                 } else if (strcmp(arg, "-development_cache") == 0) {
                     options.emitDevCaches = true;
+                } else if (strcmp(arg, "-no_customer_cache") == 0) {
+                    options.emitCustomerCaches = false;
+                } else if (strcmp(arg, "-customer_cache") == 0) {
+                    options.emitCustomerCaches = true;
                 } else if (strcmp(arg, "-overflow_dylibs") == 0) {
                     options.emitElidedDylibs = true;
                 } else if (strcmp(arg, "-mrm") == 0) {
@@ -786,7 +969,17 @@ int main(int argc, const char* argv[])
                 } else if (strcmp(arg, "-baseline_copy_roots") == 0) {
                     options.baselineCopyRoots = true;
                 } else if (strcmp(arg, "-baseline_cache_map") == 0) {
-                    options.baselineCacheMapPath = realPath(argv[++i]);
+                    std::string path = realPath(argv[++i]);
+                    if ( !path.empty() )
+                        options.baselineCacheMapPaths.push_back(path);
+                } else if (strcmp(arg, "-arch") == 0) {
+                    if ( ++i < argc ) {
+                        options.cmdLineArchs.insert(argv[i]);
+                    }
+                    else {
+                        fprintf(stderr, "-arch missing architecture name");
+                        return 1;
+                    }
                 } else {
                     //usage();
                     fprintf(stderr, "unknown option: %s\n", arg);
@@ -839,7 +1032,7 @@ int main(int argc, const char* argv[])
                 fprintf(stderr, "Cannot combine -baseline_copy_roots and -build_all\n");
                 exit(-1);
             }
-            if (!options.baselineCacheMapPath.empty()) {
+            if (!options.baselineCacheMapPaths.empty()) {
                 fprintf(stderr, "Cannot combine -baseline_cache_map and -build_all\n");
                 exit(-1);
             }
@@ -866,22 +1059,22 @@ int main(int argc, const char* argv[])
                 fprintf(stderr, "Cannot use -results with -json_manifest\n");
                 exit(-1);
             }
-            if (!options.baselineDifferenceResultPath.empty() && options.baselineCacheMapPath.empty()) {
+            if (!options.baselineDifferenceResultPath.empty() && options.baselineCacheMapPaths.empty()) {
                 fprintf(stderr, "Must use -baseline_cache_map with -baseline_diff_results when using -json_manifest\n");
                 exit(-1);
             }
-            if (options.baselineCopyRoots && options.baselineCacheMapPath.empty()) {
+            if (options.baselineCopyRoots && options.baselineCacheMapPaths.empty()) {
                 fprintf(stderr, "Must use -baseline_cache_map with -baseline_copy_roots when using -json_manifest\n");
                 exit(-1);
             }
         } else {
-            if (!options.baselineCacheMapPath.empty()) {
+            if (!options.baselineCacheMapPaths.empty()) {
                 fprintf(stderr, "Cannot use -baseline_cache_map without -json_manifest\n");
                 exit(-1);
             }
         }
 
-        if (!options.baselineCacheMapPath.empty()) {
+        if (!options.baselineCacheMapPaths.empty()) {
             if (options.baselineDifferenceResultPath.empty() && options.baselineCopyRoots) {
                 fprintf(stderr, "Must use -baseline_cache_map with -baseline_diff_results or -baseline_copy_roots\n");
                 exit(-1);
diff --git a/dyld3/shared-cache/dyld_shared_cache_util.cpp b/dyld3/shared-cache/dyld_shared_cache_util.cpp
new file mode 100644 (file)
index 0000000..da265ef
--- /dev/null
@@ -0,0 +1,1791 @@
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009-2012 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/syslimits.h>
+#include <mach-o/arch.h>
+#include <mach-o/loader.h>
+#include <mach-o/dyld_priv.h>
+#include <bootstrap.h>
+#include <mach/mach.h>
+#include <dispatch/dispatch.h>
+#include <uuid/uuid.h>
+
+#include <TargetConditionals.h>
+
+#include <map>
+#include <vector>
+#include <iostream>
+#include <optional>
+
+#include "ClosureBuilder.h"
+#include "DyldSharedCache.h"
+#include "ClosureFileSystemPhysical.h"
+#include "JSONWriter.h"
+#include "Trie.hpp"
+#include "dsc_extractor.h"
+
+#include "objc-shared-cache.h"
+
+#if TARGET_OS_OSX
+#define DSC_BUNDLE_REL_PATH "../../lib/dsc_extractor.bundle"
+#else
+#define DSC_BUNDLE_REL_PATH "../lib/dsc_extractor.bundle"
+#endif
+
+using dyld3::closure::ClosureBuilder;
+using dyld3::closure::FileSystemPhysical;
+
+// 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) ) {
+        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);
+    const dyld_cache_mapping_info* lastMapping = &mappings[header->mappingCount - 1];
+
+    size_t vmSize = (size_t)(lastMapping->address + lastMapping->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 < header->mappingCount; ++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);
+    
+    return (DyldSharedCache*)result;
+}
+
+enum Mode {
+    modeNone,
+    modeList,
+    modeMap,
+    modeDependencies,
+    modeSlideInfo,
+    modeVerboseSlideInfo,
+    modeTextInfo,
+    modeLinkEdit,
+    modeLocalSymbols,
+    modeJSONMap,
+    modeJSONDependents,
+    modeSectionSizes,
+    modeStrings,
+    modeInfo,
+    modeSize,
+    modeObjCProtocols,
+    modeObjCImpCaches,
+    modeObjCClasses,
+    modeObjCSelectors,
+    modeExtract,
+    modePatchTable,
+    modeListDylibsWithSection
+};
+
+struct Options {
+    Mode            mode;
+    const char*     dependentsOfPath;
+    const char*     extractionDir;
+    const char*     segmentName;
+    const char*     sectionName;
+    bool            printUUIDs;
+    bool            printVMAddrs;
+    bool            printDylibVersions;
+    bool            printInodes;
+};
+
+
+void usage() {
+    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");
+}
+
+static void checkMode(Mode mode) {
+    if ( mode != modeNone ) {
+        fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -verbose_slide_info, -linkedit, -map, -extract, or -size\n");
+        usage();
+        exit(1);
+    }
+}
+
+struct SegmentInfo
+{
+    uint64_t    vmAddr;
+    uint64_t    vmSize;
+    const char* installName;
+    const char* segName;
+};
+
+static void buildSegmentInfo(const DyldSharedCache* dyldCache, std::vector<SegmentInfo>& segInfos)
+{
+    dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
+        dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
+        ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) {
+            segInfos.push_back({info.vmAddr, info.vmSize, installName, info.segName});
+        });
+    });
+
+    std::sort(segInfos.begin(), segInfos.end(), [](const SegmentInfo& l, const SegmentInfo& r) -> bool {
+        return l.vmAddr < r.vmAddr;
+    });
+}
+
+static void printSlideInfoForDataRegion(const DyldSharedCache* dyldCache, uint64_t dataStartAddress, uint64_t dataSize,
+                                        const uint8_t* dataPagesStart,
+                                        const dyld_cache_slide_info* slideInfoHeader, bool verboseSlideInfo) {
+
+    printf("slide info version=%d\n", slideInfoHeader->version);
+    if ( slideInfoHeader->version == 1 ) {
+        printf("toc_count=%d, data page count=%lld\n", slideInfoHeader->toc_count, dataSize/4096);
+        const dyld_cache_slide_info_entry* entries = (dyld_cache_slide_info_entry*)((char*)slideInfoHeader + slideInfoHeader->entries_offset);
+        const uint16_t* tocs = (uint16_t*)((char*)slideInfoHeader + slideInfoHeader->toc_offset);
+        for(int i=0; i < slideInfoHeader->toc_count; ++i) {
+            printf("0x%08llX: [% 5d,% 5d] ", dataStartAddress + i*4096, i, tocs[i]);
+            const dyld_cache_slide_info_entry* entry = &entries[tocs[i]];
+            for(int j=0; j < slideInfoHeader->entries_size; ++j)
+                printf("%02X", entry->bits[j]);
+            printf("\n");
+        }
+    }
+    else if ( slideInfoHeader->version == 2 ) {
+        const dyld_cache_slide_info2* slideInfo = (dyld_cache_slide_info2*)(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 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 = 0x%016llX\n", i, (uint64_t)(pageOffset), (uint64_t)rawValue, (uint64_t)value);
+                    pageOffset += delta;
+                }
+            };
+            if ( start == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) {
+                printf("page[% 5d]: no rebasing\n", i);
+            }
+            else if ( start & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) {
+                printf("page[% 5d]: ", i);
+                int j=(start & 0x3FFF);
+                bool done = false;
+                do {
+                    uint16_t aStart = extras[j];
+                    printf("start=0x%04X ", aStart & 0x3FFF);
+                    if ( verboseSlideInfo ) {
+                        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 );
+                printf("\n");
+            }
+            else {
+                printf("page[% 5d]: start=0x%04X\n", i, starts[i]);
+                if ( verboseSlideInfo ) {
+                    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 dyld_cache_slide_info3* slideInfo = (dyld_cache_slide_info3*)(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 uintptr_t authValueAdd = (uintptr_t)(slideInfo->auth_value_add);
+        for (int i=0; i < slideInfo->page_starts_count; ++i) {
+            uint16_t delta = slideInfo->page_starts[i];
+            if ( delta == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE ) {
+                printf("page[% 5d]: no rebasing\n", i);
+                continue;
+            }
+
+            printf("page[% 5d]: start=0x%04X\n", i, delta);
+            if ( !verboseSlideInfo )
+                continue;
+
+            delta = delta/sizeof(uint64_t); // initial offset is byte based
+            const uint8_t* pageStart = dataPagesStart + (i * slideInfo->page_size);
+            const dyld_cache_slide_pointer3* loc = (dyld_cache_slide_pointer3*)pageStart;
+            do {
+                loc += delta;
+                delta = loc->plain.offsetToNextPointer;
+                dyld3::MachOLoaded::ChainedFixupPointerOnDisk ptr;
+                ptr.raw64 = *((uint64_t*)loc);
+                if ( loc->auth.authenticated ) {
+                    uint64_t target = authValueAdd + loc->auth.offsetFromSharedCacheBase;
+                    uint64_t targetValue = ptr.arm64e.signPointer((void*)loc, target);
+                    printf("    [% 5d + 0x%04llX]: 0x%016llX (JOP: diversity %d, address %s, %s)\n",
+                           i, (uint64_t)((const uint8_t*)loc - pageStart), targetValue,
+                           ptr.arm64e.authBind.diversity, ptr.arm64e.authBind.addrDiv ? "true" : "false",
+                           ptr.arm64e.keyName());
+                }
+                else {
+                    uint64_t targetValue = ptr.arm64e.unpackTarget();
+                    printf("    [% 5d + 0x%04llX]: 0x%016llX\n", i, (uint64_t)((const uint8_t*)loc - 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;
+                }
+            };
+            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 ( verboseSlideInfo ) {
+                        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 ( verboseSlideInfo ) {
+                    uint8_t* page = (uint8_t*)(long)(dataPagesStart + (slideInfo->page_size*i));
+                    uint16_t pageStartOffset = start*4;
+                    rebaseChainV4(page, pageStartOffset);
+                }
+            }
+        }
+    }
+}
+
+
+static void findImageAndSegment(const DyldSharedCache* dyldCache, const std::vector<SegmentInfo>& segInfos, uint32_t cacheOffset, SegmentInfo* found)
+{
+    const uint64_t locVmAddr = dyldCache->unslidLoadAddress() + cacheOffset;
+    const SegmentInfo target = { locVmAddr, 0, NULL, NULL };
+    const auto lowIt = std::lower_bound(segInfos.begin(), segInfos.end(), target,
+                                                                        [](const SegmentInfo& l, const SegmentInfo& r) -> bool {
+                                                                            return l.vmAddr+l.vmSize < r.vmAddr+r.vmSize;
+                                                                    });
+    *found = *lowIt;
+}
+
+
+int main (int argc, const char* argv[]) {
+
+    const char* sharedCachePath = nullptr;
+
+    Options options;
+    options.mode = modeNone;
+    options.printUUIDs = false;
+    options.printVMAddrs = false;
+    options.printDylibVersions = false;
+    options.printInodes = false;
+    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] == '-') {
+            if (strcmp(opt, "-list") == 0) {
+                checkMode(options.mode);
+                options.mode = modeList;
+            }
+            else if (strcmp(opt, "-dependents") == 0) {
+                checkMode(options.mode);
+                options.mode = modeDependencies;
+                options.dependentsOfPath = argv[++i];
+                if ( i >= argc ) {
+                    fprintf(stderr, "Error: option -depdendents requires an argument\n");
+                    usage();
+                    exit(1);
+                }
+            }
+            else if (strcmp(opt, "-linkedit") == 0) {
+                checkMode(options.mode);
+                options.mode = modeLinkEdit;
+            }
+            else if (strcmp(opt, "-info") == 0) {
+                checkMode(options.mode);
+                options.mode = modeInfo;
+            }
+            else if (strcmp(opt, "-slide_info") == 0) {
+                checkMode(options.mode);
+                options.mode = modeSlideInfo;
+            }
+            else if (strcmp(opt, "-verbose_slide_info") == 0) {
+                checkMode(options.mode);
+                options.mode = modeVerboseSlideInfo;
+            }
+            else if (strcmp(opt, "-text_info") == 0) {
+                checkMode(options.mode);
+                options.mode = modeTextInfo;
+            }
+            else if (strcmp(opt, "-local_symbols") == 0) {
+                checkMode(options.mode);
+                options.mode = modeLocalSymbols;
+            }
+            else if (strcmp(opt, "-strings") == 0) {
+                if (options.mode != modeStrings)
+                    checkMode(options.mode);
+                options.mode = modeStrings;
+                printStrings = true;
+            }
+            else if (strcmp(opt, "-sections") == 0) {
+                checkMode(options.mode);
+                options.mode = modeSectionSizes;
+            }
+            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;
+            }
+            else if (strcmp(opt, "-json-map") == 0) {
+                checkMode(options.mode);
+                options.mode = modeJSONMap;
+            }
+            else if (strcmp(opt, "-json-dependents") == 0) {
+                checkMode(options.mode);
+                options.mode = modeJSONDependents;
+            }
+            else if (strcmp(opt, "-size") == 0) {
+                checkMode(options.mode);
+                options.mode = modeSize;
+            }
+            else if (strcmp(opt, "-objc-protocols") == 0) {
+                checkMode(options.mode);
+                options.mode = modeObjCProtocols;
+            }
+            else if (strcmp(opt, "-objc-imp-caches") == 0) {
+                checkMode(options.mode);
+                options.mode = modeObjCImpCaches;
+            }
+            else if (strcmp(opt, "-objc-classes") == 0) {
+                checkMode(options.mode);
+                options.mode = modeObjCClasses;
+            }
+            else if (strcmp(opt, "-objc-selectors") == 0) {
+                checkMode(options.mode);
+                options.mode = modeObjCSelectors;
+            }
+            else if (strcmp(opt, "-extract") == 0) {
+                checkMode(options.mode);
+                options.mode = modeExtract;
+                options.extractionDir = argv[++i];
+                if ( i >= argc ) {
+                    fprintf(stderr, "Error: option -extract requires a directory argument\n");
+                    usage();
+                    exit(1);
+                }
+            }
+            else if (strcmp(opt, "-uuid") == 0) {
+                options.printUUIDs = true;
+            }
+            else if (strcmp(opt, "-inode") == 0) {
+                options.printInodes = true;
+            }
+            else if (strcmp(opt, "-versions") == 0) {
+                options.printDylibVersions = true;
+            }
+            else if (strcmp(opt, "-vmaddr") == 0) {
+                options.printVMAddrs = true;
+            }
+            else if (strcmp(opt, "-patch_table") == 0) {
+                options.mode = modePatchTable;
+            }
+            else if (strcmp(opt, "-list_dylibs_with_section") == 0) {
+                options.mode = modeListDylibsWithSection;
+                options.segmentName = argv[++i];
+                options.sectionName = argv[++i];
+                if ( i >= argc ) {
+                    fprintf(stderr, "Error: option -list_dylibs_with_section requires a segment and section name\n");
+                    usage();
+                    exit(1);
+                }
+            }
+            else {
+                fprintf(stderr, "Error: unrecognized option %s\n", opt);
+                usage();
+                exit(1);
+            }
+        }
+        else {
+            sharedCachePath = opt;
+        }
+    }
+
+    if ( options.mode == modeNone ) {
+        fprintf(stderr, "Error: select one of -list, -dependents, -info, -linkedit, or -map\n");
+        usage();
+        exit(1);
+    }
+
+    if ( options.mode != modeSlideInfo && options.mode != modeVerboseSlideInfo ) {
+        if ( options.printUUIDs && (options.mode != modeList) )
+            fprintf(stderr, "Warning: -uuid option ignored outside of -list mode\n");
+
+        if ( options.printVMAddrs && (options.mode != modeList) )
+            fprintf(stderr, "Warning: -vmaddr option ignored outside of -list mode\n");
+
+        if ( options.printDylibVersions && (options.mode != modeDependencies) )
+            fprintf(stderr, "Warning: -versions option ignored outside of -dependents mode\n");
+
+        if ( (options.mode == modeDependencies) && (options.dependentsOfPath == NULL) ) {
+            fprintf(stderr, "Error: -dependents given, but no dylib path specified\n");
+            usage();
+            exit(1);
+        }
+    }
+
+    const DyldSharedCache* dyldCache = nullptr;
+    if ( sharedCachePath != nullptr ) {
+        dyldCache = mapCacheFile(sharedCachePath);
+        // mapCacheFile prints an error if something goes wrong, so just return in that case.
+        if ( dyldCache == nullptr )
+            return 1;
+    }
+    else {
+        size_t cacheLength;
+        dyldCache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLength);
+        if (dyldCache == nullptr) {
+            fprintf(stderr, "Could not get in-memory shared cache\n");
+            return 1;
+        }
+        if ( options.mode == modeObjCClasses ) {
+            fprintf(stderr, "Cannot use -objc-classes with a live cache.  Please run with a path to an on-disk cache file\n");
+            return 1;
+        }
+    }
+
+    if ( options.mode == modeSlideInfo || options.mode == modeVerboseSlideInfo ) {
+        if ( !dyldCache->hasSlideInfo() ) {
+            fprintf(stderr, "Error: dyld shared cache does not contain slide info\n");
+            exit(1);
+        }
+
+        const bool verboseSlideInfo = (options.mode == modeVerboseSlideInfo);
+        dyldCache->forEachSlideInfo(^(uint64_t mappingStartAddress, uint64_t mappingSize, const uint8_t *mappingPagesStart,
+                                      uint64_t slideInfoOffset, uint64_t slideInfoSize, const dyld_cache_slide_info *slideInfoHeader) {
+            printSlideInfoForDataRegion(dyldCache, mappingStartAddress, mappingSize, mappingPagesStart,
+                                        slideInfoHeader, verboseSlideInfo);
+        });
+        return 0;
+    }
+    else if ( options.mode == modeInfo ) {
+        const dyld_cache_header* header = &dyldCache->header;
+        uuid_string_t uuidString;
+        uuid_unparse_upper(header->uuid, uuidString);
+        printf("uuid: %s\n", uuidString);
+
+        dyld3::Platform platform = dyldCache->platform();
+        printf("platform: %s\n", dyld3::MachOFile::platformName(platform));
+        printf("built by: %s\n", header->locallyBuiltCache ? "local machine" : "B&I");
+        printf("cache type: %s\n", header->cacheType ? "production" : "development");
+        printf("image count: %u\n", header->imagesCount);
+        if ( (header->mappingOffset >= 0x78) && (header->branchPoolsOffset != 0) ) {
+            printf("branch pool count:  %u\n", header->branchPoolsCount);
+        }
+        if ( dyldCache->hasSlideInfo() ) {
+            uint32_t pageSize            = 0x4000; // fix me for intel
+            uint32_t possibleSlideValues = (uint32_t)(header->maxSlide/pageSize);
+            uint32_t entropyBits         = 32 - __builtin_clz(possibleSlideValues - 1);
+            printf("ASLR entropy: %u-bits\n", entropyBits);
+        }
+        printf("mappings:\n");
+        dyldCache->forEachRegion(^(const void *content, uint64_t vmAddr, uint64_t size, uint32_t permissions,
+                                   uint64_t flags) {
+            std::string mappingName = "";
+            if ( permissions & VM_PROT_EXECUTE ) {
+                mappingName = "__TEXT";
+            } else if ( permissions & VM_PROT_WRITE ) {
+                // Start off with __DATA or __AUTH
+                if ( flags & DYLD_CACHE_MAPPING_AUTH_DATA )
+                    mappingName = "__AUTH";
+                else
+                    mappingName = "__DATA";
+                // Then add one of "", _DIRTY, or _CONST
+                if ( flags & DYLD_CACHE_MAPPING_DIRTY_DATA )
+                    mappingName += "_DIRTY";
+                else if ( flags & DYLD_CACHE_MAPPING_CONST_DATA )
+                    mappingName += "_CONST";
+            }
+            else if ( permissions & VM_PROT_READ ) {
+                mappingName = "__LINKEDIT";
+            } else {
+                mappingName = "*unknown*";
+            }
+            uint64_t fileOffset = (uint8_t*)content - (uint8_t*)dyldCache;
+            printf("%16s %4lluMB,  file offset: 0x%08llX -> 0x%08llX,  address: 0x%08llX -> 0x%08llX\n",
+                   mappingName.c_str(), size / (1024*1024), fileOffset, fileOffset + size, vmAddr, vmAddr + size);
+        });
+        if ( header->codeSignatureOffset != 0 ) {
+            uint64_t size = header->codeSignatureSize;
+            uint64_t csAddr = dyldCache->getCodeSignAddress();
+            if ( size != 0 )
+                printf("%16s %4lluMB,  file offset: 0x%08llX -> 0x%08llX,  address: 0x%08llX -> 0x%08llX\n",
+                       "code sign", size/(1024*1024), header->codeSignatureOffset, header->codeSignatureOffset + size, csAddr, csAddr + size);
+        }
+        dyldCache->forEachSlideInfo(^(uint64_t mappingStartAddress, uint64_t mappingSize, const uint8_t *mappingPagesStart,
+                                      uint64_t slideInfoOffset, uint64_t slideInfoSize, const dyld_cache_slide_info *slideInfoHeader) {
+
+            printf("slide info:      %4lluKB,  file offset: 0x%08llX -> 0x%08llX\n",
+                   slideInfoSize/1024, slideInfoOffset, slideInfoOffset + slideInfoSize);
+        });
+        if ( header->localSymbolsOffset != 0 )
+            printf("local symbols:    %3lluMB,  file offset: 0x%08llX -> 0x%08llX\n",
+                   header->localSymbolsSize/(1024*1024), header->localSymbolsOffset, header->localSymbolsOffset + header->localSymbolsSize);
+    }
+    else if ( options.mode == modeTextInfo ) {
+        const dyld_cache_header* header = &dyldCache->header;
+        printf("dylib text infos (count=%llu):\n", header->imagesTextCount);
+        dyldCache->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const unsigned char *dylibUUID, const char *installName, bool &stop) {
+            uuid_string_t uuidString;
+            uuid_unparse_upper(dylibUUID, uuidString);
+            printf("   0x%09llX -> 0x%09llX  <%s>  %s\n", loadAddressUnslid, loadAddressUnslid + textSegmentSize, uuidString, installName);
+        });
+    }
+    else if ( options.mode == modeLocalSymbols ) {
+        if ( !dyldCache->hasLocalSymbolsInfo() ) {
+            fprintf(stderr, "Error: dyld shared cache does not contain local symbols info\n");
+            exit(1);
+        }
+        const bool is64 = (strstr(dyldCache->archName(), "64") != NULL);
+        const uint32_t nlistFileOffset = (uint32_t)((uint8_t*)dyldCache->getLocalNlistEntries() - (uint8_t*)dyldCache);
+        const uint32_t nlistCount = dyldCache->getLocalNlistCount();
+        const uint32_t nlistByteSize = is64 ? nlistCount*16 : nlistCount*12;
+        const char* localStrings = dyldCache->getLocalStrings();
+        const uint32_t stringsFileOffset = (uint32_t)((uint8_t*)localStrings - (uint8_t*)dyldCache);
+        const uint32_t stringsSize = dyldCache->getLocalStringsSize();
+
+        printf("local symbols nlist array:  %3uMB,  file offset: 0x%08X -> 0x%08X\n", nlistByteSize/(1024*1024), nlistFileOffset, nlistFileOffset+nlistByteSize);
+        printf("local symbols string pool:  %3uMB,  file offset: 0x%08X -> 0x%08X\n", stringsSize/(1024*1024), stringsFileOffset, stringsFileOffset+stringsSize);
+
+        __block uint32_t entriesCount = 0;
+        dyldCache->forEachLocalSymbolEntry(^(uint32_t dylibOffset, uint32_t nlistStartIndex, uint32_t nlistCount, bool &stop) {
+            const char* imageName = dyldCache->getIndexedImagePath(entriesCount);
+            printf("   nlistStartIndex=%5d, nlistCount=%5d, image=%s\n", nlistStartIndex, nlistCount, imageName);
+#if 0
+            if ( is64 ) {
+                const nlist_64* symTab = (nlist_64*)((char*)dyldCache + nlistFileOffset);
+                for (int e = 0; e < nlistCount; ++e) {
+                    const nlist_64* entry = &symTab[nlistStartIndex + e];
+                    printf("     nlist[%d].str=%d, %s\n", e, entry->n_un.n_strx, &localStrings[entry->n_un.n_strx]);
+                    printf("     nlist[%d].value=0x%0llX\n", e, entry->n_value);
+                }
+            }
+#endif
+            entriesCount++;
+        });
+        printf("local symbols by dylib (count=%d):\n", entriesCount);
+    }
+    else if ( options.mode == modeJSONMap ) {
+        std::string buffer = dyldCache->generateJSONMap("unknown");
+        printf("%s\n", buffer.c_str());
+    }
+    else if ( options.mode == modeJSONDependents ) {
+        std::cout <<  dyldCache->generateJSONDependents();
+    }
+    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 == modeSectionSizes ) {
+        __block std::map<std::string, uint64_t> sectionSizes;
+        dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
+            const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
+            ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo &sectInfo, bool malformedSectionRange, bool &stop) {
+                std::string section = std::string(sectInfo.segInfo.segName) + " " + sectInfo.sectName;
+                sectionSizes[section] += sectInfo.sectSize;
+            });
+        });
+        for (const auto& keyAndValue : sectionSizes) {
+            printf("%lld %s\n", keyAndValue.second, keyAndValue.first.c_str());
+        }
+    }
+    else if ( options.mode == modeObjCProtocols ) {
+        if ( dyldCache->objcOpt() == nullptr ) {
+            fprintf(stderr, "Error: could not get optimized objc\n");
+            return 1;
+        }
+        objc_opt::objc_protocolopt2_t* protocols = dyldCache->objcOpt()->protocolopt2();
+        if ( protocols == nullptr ) {
+            fprintf(stderr, "Error: could not get optimized objc protocols\n");
+            return 1;
+        }
+
+        for (uint64_t index = 0; index != protocols->capacity; ++index) {
+            const objc_opt::objc_classheader_t& clshi = protocols->classOffsets()[index];
+            if ( clshi.clsOffset == 0 ) {
+                fprintf(stderr, "[% 5lld]\n", index);
+                continue;
+            }
+            const char* name = (const char*)(((const uint8_t*)protocols) + protocols->offsets()[index]);
+            if ( !clshi.isDuplicate() ) {
+                fprintf(stderr, "[% 5lld] -> (% 8d, % 8d) = %s\n", index, clshi.clsOffset, clshi.hiOffset, name);
+                continue;
+            }
+
+            // class appears in more than one header
+            uint32_t count = clshi.duplicateCount();
+            fprintf(stderr, "[% 5lld] -> duplicates [% 5d..% 5d] = %s\n",
+                    index, clshi.duplicateIndex(), clshi.duplicateIndex() + clshi.duplicateCount() - 1, name);
+
+            const objc_opt::objc_classheader_t *list = &protocols->duplicateOffsets()[clshi.duplicateIndex()];
+            for (uint32_t i = 0; i < count; i++) {
+                fprintf(stderr, "  - [% 5lld] -> (% 8d, % 8d)\n", (uint64_t)(clshi.duplicateIndex() + i), list[i].clsOffset, list[i].hiOffset);
+            }
+        }
+    }
+    else if ( options.mode == modeObjCClasses ) {
+        using dyld3::json::Node;
+        using dyld3::json::NodeValueType;
+        using ObjCClassInfo = dyld3::MachOAnalyzer::ObjCClassInfo;
+        const bool rebased = false;
+
+        std::string instancePrefix("-");
+        std::string classPrefix("+");
+
+        auto getString = ^const char *(const dyld3::MachOAnalyzer* ma, uint64_t nameVMAddr){
+            dyld3::MachOAnalyzer::PrintableStringResult result;
+            const char* name = ma->getPrintableString(nameVMAddr, result);
+            if (result == dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
+                return name;
+            return nullptr;
+        };
+
+        // Build a map of class vm addrs to their names so that categories know the
+        // name of the class they are attaching to
+        __block std::unordered_map<uint64_t, const char*> classVMAddrToName;
+        __block std::unordered_map<uint64_t, const char*> metaclassVMAddrToName;
+        dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
+            const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
+            const uint32_t pointerSize = ma->pointerSize();
+
+            auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
+                                uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
+                                const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
+                if (auto className = getString(ma, objcClass.nameVMAddr(pointerSize))) {
+                    if (isMetaClass)
+                        metaclassVMAddrToName[classVMAddr] = className;
+                    else
+                        classVMAddrToName[classVMAddr] = className;
+                }
+            };
+
+            Diagnostics diag;
+
+            dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = dyldCache->makeVMAddrConverter(rebased);
+            ma->forEachObjCClass(diag, vmAddrConverter, visitClass);
+        });
+
+        // These are used only for the on-disk binaries we analyze
+        __block std::vector<const char*>        onDiskChainedFixupBindTargets;
+        __block std::unordered_map<uint64_t, const char*> onDiskClassVMAddrToName;
+        __block std::unordered_map<uint64_t, const char*> onDiskMetaclassVMAddrToName;
+
+        auto getProperties = ^(const dyld3::MachOAnalyzer* ma, uint64_t propertiesVMAddr,
+                               const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) {
+            __block Node propertiesNode;
+            auto visitProperty = ^(uint64_t propertyVMAddr, const dyld3::MachOAnalyzer::ObjCProperty& property) {
+                // Get the name && attributes
+                auto propertyName = getString(ma, property.nameVMAddr);
+                auto propertyAttributes = getString(ma, property.attributesVMAddr);
+
+                if (!propertyName || !propertyAttributes)
+                    return;
+
+                Node propertyNode;
+                propertyNode.map["name"] = Node{propertyName};
+                propertyNode.map["attributes"] = Node{propertyAttributes};
+                propertiesNode.array.push_back(propertyNode);
+            };
+            ma->forEachObjCProperty(propertiesVMAddr, vmAddrConverter, visitProperty);
+            return propertiesNode.array.empty() ? std::optional<Node>() : propertiesNode;
+        };
+
+        auto getClassProtocols = ^(const dyld3::MachOAnalyzer* ma, uint64_t protocolsVMAddr,
+                                   const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) {
+            __block Node protocolsNode;
+
+            auto visitProtocol = ^(uint64_t protocolVMAddr, const dyld3::MachOAnalyzer::ObjCProtocol& protocol) {
+                if (const char *name = getString(ma, protocol.nameVMAddr)) {
+                    protocolsNode.array.push_back(Node{name});
+                }
+            };
+
+            ma->forEachObjCProtocol(protocolsVMAddr, vmAddrConverter, visitProtocol);
+
+            return protocolsNode.array.empty() ? std::optional<Node>() : protocolsNode;
+        };
+
+        auto getProtocols = ^(const dyld3::MachOAnalyzer* ma,
+                              const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) {
+            __block Node protocols;
+
+            auto getMethods = ^(const dyld3::MachOAnalyzer* ma, uint64_t methodListVMAddr, const std::string &prefix, Node &node){
+                auto visitMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
+                    if (auto name = getString(ma, method.nameVMAddr)) {
+                        node.array.push_back(Node{prefix + name});
+                    }
+                };
+
+                ma->forEachObjCMethod(methodListVMAddr, vmAddrConverter, visitMethod);
+            };
+
+            auto visitProtocol = ^(Diagnostics& diag, uint64_t protoVMAddr,
+                                   const dyld3::MachOAnalyzer::ObjCProtocol& objcProto) {
+                const char* protoName = getString(ma, objcProto.nameVMAddr);
+                if (!protoName)
+                    return;
+
+                Node entry;
+                entry.map["protocolName"] = Node{protoName};
+
+                if ( objcProto.protocolsVMAddr != 0 ) {
+                    __block Node protocols;
+
+                    auto visitProtocol = ^(uint64_t protocolRefVMAddr, const dyld3::MachOAnalyzer::ObjCProtocol &protocol) {
+                        if (auto name = getString(ma, protocol.nameVMAddr)) {
+                            protocols.array.push_back(Node{name});
+                        }
+                    };
+
+                    ma->forEachObjCProtocol(objcProto.protocolsVMAddr, vmAddrConverter, visitProtocol);
+                    if (!protocols.array.empty()) {
+                        entry.map["protocols"] = protocols;
+                    }
+                }
+
+                Node methods;
+                getMethods(ma, objcProto.instanceMethodsVMAddr, instancePrefix, methods);
+                getMethods(ma, objcProto.classMethodsVMAddr, classPrefix, methods);
+                if (!methods.array.empty()) {
+                    entry.map["methods"] = methods;
+                }
+
+                Node optMethods;
+                getMethods(ma, objcProto.optionalInstanceMethodsVMAddr, instancePrefix, optMethods);
+                getMethods(ma, objcProto.optionalClassMethodsVMAddr, classPrefix, optMethods);
+                if (!optMethods.array.empty()) {
+                    entry.map["optionalMethods"] = optMethods;
+                }
+
+                protocols.array.push_back(entry);
+            };
+
+            Diagnostics diag;
+            ma->forEachObjCProtocol(diag, vmAddrConverter, visitProtocol);
+
+            return protocols.array.empty() ? std::optional<Node>() : protocols;
+        };
+
+        auto getSelRefs = ^(const dyld3::MachOAnalyzer* ma,
+                            const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) {
+            __block std::vector<const char *> selNames;
+
+            auto visitSelRef = ^(uint64_t selRefVMAddr, uint64_t selRefTargetVMAddr) {
+                if (auto selValue = getString(ma, selRefTargetVMAddr)) {
+                    selNames.push_back(selValue);
+                }
+            };
+
+            Diagnostics diag;
+            ma->forEachObjCSelectorReference(diag, vmAddrConverter, visitSelRef);
+
+            std::sort(selNames.begin(), selNames.end(),
+                      [](const char* a, const char* b) {
+                return strcasecmp(a, b) < 0;
+            });
+
+            Node selrefs;
+            for (auto s: selNames) {
+                selrefs.array.push_back(Node{s});
+            }
+
+            return selrefs.array.empty() ? std::optional<Node>() : selrefs;
+        };
+
+        auto getClasses = ^(const dyld3::MachOAnalyzer* ma,
+                            const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) {
+            Diagnostics diag;
+            const uint32_t pointerSize = ma->pointerSize();
+
+            // Get the vmAddrs for all exported symbols as we want to know if classes
+            // are exported
+            std::set<uint64_t> exportedSymbolVMAddrs;
+            {
+                uint64_t loadAddress = ma->preferredLoadAddress();
+
+                uint32_t exportTrieRuntimeOffset;
+                uint32_t exportTrieSize;
+                if ( ma->hasExportTrie(exportTrieRuntimeOffset, exportTrieSize) ) {
+                    const uint8_t* start = (uint8_t*)ma + exportTrieRuntimeOffset;
+                    const uint8_t* end = start + exportTrieSize;
+                    std::vector<ExportInfoTrie::Entry> exports;
+                    if ( ExportInfoTrie::parseTrie(start, end, exports) ) {
+                        for (const ExportInfoTrie::Entry& entry: exports) {
+                            exportedSymbolVMAddrs.insert(loadAddress + entry.info.address);
+                        }
+                    }
+                }
+            }
+
+            __block Node classesNode;
+            __block bool skippedPreviousClass = false;
+            auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
+                                uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
+                                const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
+                if (isMetaClass) {
+                    if (skippedPreviousClass) {
+                        // If the class was bad, then skip the meta class too
+                        skippedPreviousClass = false;
+                        return;
+                    }
+                } else {
+                    skippedPreviousClass = true;
+                }
+
+                std::string classType = "-";
+                if (isMetaClass)
+                    classType = "+";
+                dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
+                const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
+                if (classNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint) {
+                    return;
+                }
+
+                const char* superClassName = nullptr;
+                if ( ma->inDyldCache() ) {
+                    if ( objcClass.superclassVMAddr != 0 ) {
+                        if (isMetaClass) {
+                            // If we are root class, then our superclass should actually point to our own class
+                            const uint32_t RO_ROOT = (1<<1);
+                            if ( objcClass.flags(pointerSize) & RO_ROOT ) {
+                                auto it = classVMAddrToName.find(objcClass.superclassVMAddr);
+                                assert(it != classVMAddrToName.end());
+                                superClassName = it->second;
+                            } else {
+                                auto it = metaclassVMAddrToName.find(objcClass.superclassVMAddr);
+                                assert(it != metaclassVMAddrToName.end());
+                                superClassName = it->second;
+                            }
+                        } else {
+                            auto it = classVMAddrToName.find(objcClass.superclassVMAddr);
+                            assert(it != classVMAddrToName.end());
+                            superClassName = it->second;
+                        }
+                    }
+                } else {
+                    // On-disk binary.  Lets crack the chain to work out what we are pointing at
+                    dyld3::MachOAnalyzer::ChainedFixupPointerOnDisk fixup;
+                    fixup.raw64 = objcClass.superclassVMAddr;
+                    if (fixup.arm64e.bind.bind) {
+                        uint64_t bindOrdinal = fixup.arm64e.authBind.auth ? fixup.arm64e.authBind.ordinal : fixup.arm64e.bind.ordinal;
+                        // Bind to another image.  Use the bind table to work out which name to bind to
+                        const char* symbolName = onDiskChainedFixupBindTargets[bindOrdinal];
+                        if (isMetaClass) {
+                            if ( strstr(symbolName, "_OBJC_METACLASS_$_") == symbolName ) {
+                                superClassName = symbolName + strlen("_OBJC_METACLASS_$_");
+                            } else {
+                                // Swift classes don't start with these prefixes so just skip them
+                                if (objcClass.isSwiftLegacy || objcClass.isSwiftStable)
+                                    return;
+                            }
+                        } else {
+                            if ( strstr(symbolName, "_OBJC_CLASS_$_") == symbolName ) {
+                                superClassName = symbolName + strlen("_OBJC_CLASS_$_");
+                            } else {
+                                // Swift classes don't start with these prefixes so just skip them
+                                if (objcClass.isSwiftLegacy || objcClass.isSwiftStable)
+                                    return;
+                            }
+                        }
+                    } else {
+                        // Rebase within this image.
+                        if (isMetaClass) {
+                            auto it = onDiskMetaclassVMAddrToName.find(objcClass.superclassVMAddr);
+                            assert(it != onDiskMetaclassVMAddrToName.end());
+                            superClassName = it->second;
+                        } else {
+                            auto it = onDiskClassVMAddrToName.find(objcClass.superclassVMAddr);
+                            assert(it != onDiskClassVMAddrToName.end());
+                            superClassName = it->second;
+                        }
+                    }
+                }
+
+                // Print the methods on this class
+                __block Node methodsNode;
+                auto visitMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
+                    dyld3::MachOAnalyzer::PrintableStringResult methodNameResult;
+                    const char* methodName = ma->getPrintableString(method.nameVMAddr, methodNameResult);
+                    if (methodNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
+                        return;
+                    methodsNode.array.push_back(Node{classType + methodName});
+                };
+                ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), vmAddrConverter,
+                                      visitMethod);
+
+                std::optional<Node> properties = getProperties(ma, objcClass.basePropertiesVMAddr(pointerSize), vmAddrConverter);
+
+                if (isMetaClass) {
+                    assert(!classesNode.array.empty());
+                    Node& currentClassNode = classesNode.array.back();
+                    assert(currentClassNode.map["className"].value == className);
+                    if (!methodsNode.array.empty()) {
+                        Node& currentMethodsNode = currentClassNode.map["methods"];
+                        currentMethodsNode.array.insert(currentMethodsNode.array.end(),
+                                                        methodsNode.array.begin(),
+                                                        methodsNode.array.end());
+                    }
+                    if (properties.has_value()) {
+                        Node& currentPropertiesNode = currentClassNode.map["properties"];
+                        currentPropertiesNode.array.insert(currentPropertiesNode.array.end(),
+                                                           properties->array.begin(),
+                                                           properties->array.end());
+                    }
+                    return;
+                }
+
+                Node currentClassNode;
+                currentClassNode.map["className"] = Node{className};
+                if ( superClassName != nullptr )
+                    currentClassNode.map["superClassName"] = Node{superClassName};
+                if (!methodsNode.array.empty())
+                    currentClassNode.map["methods"] = methodsNode;
+                if (properties.has_value())
+                    currentClassNode.map["properties"] = properties.value();
+                if (std::optional<Node> protocols = getClassProtocols(ma, objcClass.baseProtocolsVMAddr(pointerSize), vmAddrConverter))
+                    currentClassNode.map["protocols"] = protocols.value();
+
+                currentClassNode.map["exported"] = Node{exportedSymbolVMAddrs.count(classVMAddr) != 0};
+
+                // We didn't skip this class so mark it as such
+                skippedPreviousClass = false;
+
+                classesNode.array.push_back(currentClassNode);
+            };
+
+            ma->forEachObjCClass(diag, vmAddrConverter, visitClass);
+            return classesNode.array.empty() ? std::optional<Node>() : classesNode;
+        };
+
+        auto getCategories = ^(const dyld3::MachOAnalyzer* ma,
+                               const dyld3::MachOAnalyzer::VMAddrConverter& vmAddrConverter) {
+            Diagnostics diag;
+
+            __block Node categoriesNode;
+            auto visitCategory = ^(Diagnostics& diag, uint64_t categoryVMAddr,
+                                   const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) {
+                dyld3::MachOAnalyzer::PrintableStringResult categoryNameResult;
+                const char* categoryName = ma->getPrintableString(objcCategory.nameVMAddr, categoryNameResult);
+                if (categoryNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
+                    return;
+
+                    const char* className = nullptr;
+                    if ( ma->inDyldCache() ) {
+                        auto it = classVMAddrToName.find(objcCategory.clsVMAddr);
+                        assert(it != classVMAddrToName.end());
+                        className = it->second;
+                    } else {
+                        // On-disk binary.  Lets crack the chain to work out what we are pointing at
+                        dyld3::MachOAnalyzer::ChainedFixupPointerOnDisk fixup;
+                        fixup.raw64 = objcCategory.clsVMAddr;
+                        if (fixup.arm64e.bind.bind) {
+                            uint64_t bindOrdinal = fixup.arm64e.authBind.auth ? fixup.arm64e.authBind.ordinal : fixup.arm64e.bind.ordinal;
+                            // Bind to another image.  Use the bind table to work out which name to bind to
+                            const char* symbolName = onDiskChainedFixupBindTargets[bindOrdinal];
+                            if ( strstr(symbolName, "_OBJC_CLASS_$_") == symbolName ) {
+                                className = symbolName + strlen("_OBJC_CLASS_$_");
+                            } else {
+                                // Swift classes don't start with these prefixes so just skip them
+                                // We don't know that this is a Swift class/category though, but skip it anyway
+                                return;
+                            }
+                        } else {
+                            auto it = onDiskClassVMAddrToName.find(objcCategory.clsVMAddr);
+                            if (it == onDiskClassVMAddrToName.end()) {
+                                // This is an odd binary with perhaps a Swift class.  Just skip this entry
+                                return;
+                            }
+                            className = it->second;
+                        }
+                    }
+
+                // Print the instance methods on this category
+                __block Node methodsNode;
+                auto visitInstanceMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
+                    if (auto methodName = getString(ma, method.nameVMAddr))
+                        methodsNode.array.push_back(Node{instancePrefix + methodName});
+                };
+                ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, vmAddrConverter,
+                                      visitInstanceMethod);
+
+                // Print the instance methods on this category
+                __block Node classMethodsNode;
+                auto visitClassMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
+                    if (auto methodName = getString(ma, method.nameVMAddr))
+                        methodsNode.array.push_back(Node{classPrefix + methodName});
+                };
+                ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, vmAddrConverter,
+                                      visitClassMethod);
+
+                Node currentCategoryNode;
+                currentCategoryNode.map["categoryName"] = Node{categoryName};
+                currentCategoryNode.map["className"] = Node{className};
+                if (!methodsNode.array.empty())
+                    currentCategoryNode.map["methods"] = methodsNode;
+                if (std::optional<Node> properties = getProperties(ma, objcCategory.instancePropertiesVMAddr, vmAddrConverter))
+                    currentCategoryNode.map["properties"] = properties.value();
+                if (std::optional<Node> protocols = getClassProtocols(ma, objcCategory.protocolsVMAddr, vmAddrConverter))
+                    currentCategoryNode.map["protocols"] = protocols.value();
+
+                categoriesNode.array.push_back(currentCategoryNode);
+            };
+
+            ma->forEachObjCCategory(diag, vmAddrConverter, visitCategory);
+            return categoriesNode.array.empty() ? std::optional<Node>() : categoriesNode;
+        };
+
+        __block bool needsComma = false;
+
+        dyld3::json::streamArrayBegin(needsComma);
+
+        dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
+            dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = dyldCache->makeVMAddrConverter(rebased);
+            const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
+
+            Node imageRecord;
+            imageRecord.map["imagePath"] = Node{installName};
+            imageRecord.map["imageType"] = Node{"cache-dylib"};
+            std::optional<Node> classes = getClasses(ma, vmAddrConverter);
+            std::optional<Node> categories = getCategories(ma, vmAddrConverter);
+            std::optional<Node> protocols = getProtocols(ma, vmAddrConverter);
+            std::optional<Node> selrefs = getSelRefs(ma, vmAddrConverter);
+
+            // Skip emitting images with no objc data
+            if (!classes.has_value() && !categories.has_value() && !protocols.has_value() && !selrefs.has_value())
+                return;
+            if (classes.has_value())
+                imageRecord.map["classes"] = classes.value();
+            if (categories.has_value())
+                imageRecord.map["categories"] = categories.value();
+            if (protocols.has_value())
+                imageRecord.map["protocols"] = protocols.value();
+            if (selrefs.has_value())
+                imageRecord.map["selrefs"] = selrefs.value();
+
+            dyld3::json::streamArrayNode(needsComma, imageRecord);
+        });
+
+        FileSystemPhysical fileSystem;
+        dyld3::Platform            platform = dyldCache->platform();
+        const dyld3::GradedArchs&  archs    = dyld3::GradedArchs::forName(dyldCache->archName(), true);
+
+        dyldCache->forEachLaunchClosure(^(const char *executableRuntimePath, const dyld3::closure::LaunchClosure *closure) {
+            Diagnostics diag;
+            char realerPath[MAXPATHLEN];
+            dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, executableRuntimePath, archs, platform, realerPath);
+            const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
+            uint32_t pointerSize = ma->pointerSize();
+
+            // Populate the bind targets for classes from other images
+            onDiskChainedFixupBindTargets.clear();
+            ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
+                onDiskChainedFixupBindTargets.push_back(symbolName);
+            });
+            if ( diag.hasError() )
+                return;
+
+            // Populate the rebase targets for class names
+            onDiskMetaclassVMAddrToName.clear();
+            onDiskClassVMAddrToName.clear();
+            auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
+                                uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
+                                const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
+                if (auto className = getString(ma, objcClass.nameVMAddr(pointerSize))) {
+                    if (isMetaClass)
+                        onDiskMetaclassVMAddrToName[classVMAddr] = className;
+                    else
+                        onDiskClassVMAddrToName[classVMAddr] = className;
+                }
+            };
+
+            // Get a vmAddrConverter for this on-disk binary.  We can't use the shared cache one
+            dyld3::MachOAnalyzer::VMAddrConverter onDiskVMAddrConverter = ma->makeVMAddrConverter(rebased);
+
+            ma->forEachObjCClass(diag, onDiskVMAddrConverter, visitClass);
+
+            Node imageRecord;
+            imageRecord.map["imagePath"] = Node{executableRuntimePath};
+            imageRecord.map["imageType"] = Node{"executable"};
+            std::optional<Node> classes = getClasses(ma, onDiskVMAddrConverter);
+            std::optional<Node> categories = getCategories(ma, onDiskVMAddrConverter);
+            // TODO: protocols
+            std::optional<Node> selrefs = getSelRefs(ma, onDiskVMAddrConverter);
+
+            // Skip emitting images with no objc data
+            if (!classes.has_value() && !categories.has_value() && !selrefs.has_value())
+                return;
+            if (classes.has_value())
+                imageRecord.map["classes"] = classes.value();
+            if (categories.has_value())
+                imageRecord.map["categories"] = categories.value();
+            if (selrefs.has_value())
+                imageRecord.map["selrefs"] = selrefs.value();
+
+            dyld3::json::streamArrayNode(needsComma, imageRecord);
+        });
+
+        dyldCache->forEachDlopenImage(^(const char *runtimePath, const dyld3::closure::Image *image) {
+            Diagnostics diag;
+            char realerPath[MAXPATHLEN];
+            dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, runtimePath, archs, platform, realerPath);
+            const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
+            uint32_t pointerSize = ma->pointerSize();
+
+            // Populate the bind targets for classes from other images
+            onDiskChainedFixupBindTargets.clear();
+            ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
+                onDiskChainedFixupBindTargets.push_back(symbolName);
+            });
+            if ( diag.hasError() )
+                return;
+
+            // Populate the rebase targets for class names
+            onDiskMetaclassVMAddrToName.clear();
+            onDiskClassVMAddrToName.clear();
+            auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
+                                uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
+                                const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
+                if (auto className = getString(ma, objcClass.nameVMAddr(pointerSize))) {
+                    if (isMetaClass)
+                        onDiskMetaclassVMAddrToName[classVMAddr] = className;
+                    else
+                        onDiskClassVMAddrToName[classVMAddr] = className;
+                }
+            };
+
+            // Get a vmAddrConverter for this on-disk binary.  We can't use the shared cache one
+            dyld3::MachOAnalyzer::VMAddrConverter onDiskVMAddrConverter = ma->makeVMAddrConverter(rebased);
+
+            ma->forEachObjCClass(diag, onDiskVMAddrConverter, visitClass);
+
+            Node imageRecord;
+            imageRecord.map["imagePath"] = Node{runtimePath};
+            imageRecord.map["imageType"] = Node{"non-cache-dylib"};
+            std::optional<Node> classes = getClasses(ma, onDiskVMAddrConverter);
+            std::optional<Node> categories = getCategories(ma, onDiskVMAddrConverter);
+            // TODO: protocols
+            std::optional<Node> selrefs = getSelRefs(ma, onDiskVMAddrConverter);
+
+            // Skip emitting images with no objc data
+            if (!classes.has_value() && !categories.has_value() && !selrefs.has_value())
+                return;
+            if (classes.has_value())
+                imageRecord.map["classes"] = classes.value();
+            if (categories.has_value())
+                imageRecord.map["categories"] = categories.value();
+            if (selrefs.has_value())
+                imageRecord.map["selrefs"] = selrefs.value();
+
+            dyld3::json::streamArrayNode(needsComma, imageRecord);
+        });
+
+        dyld3::json::streamArrayEnd(needsComma);
+    }
+    else if ( options.mode == modeObjCSelectors ) {
+        if ( dyldCache->objcOpt() == nullptr ) {
+            fprintf(stderr, "Error: could not get optimized objc\n");
+            return 1;
+        }
+        const objc_opt::objc_selopt_t* selectors = dyldCache->objcOpt()->selopt();
+        if ( selectors == nullptr ) {
+            fprintf(stderr, "Error: could not get optimized objc selectors\n");
+            return 1;
+        }
+
+        std::vector<const char*> selNames;
+        for (uint64_t index = 0; index != selectors->capacity; ++index) {
+            objc_opt::objc_stringhash_offset_t offset = selectors->offsets()[index];
+            if ( offset == 0 )
+                continue;
+            const char* selName = selectors->getEntryForIndex((uint32_t)index);
+            selNames.push_back(selName);
+        }
+
+        std::sort(selNames.begin(), selNames.end(),
+                  [](const char* a, const char* b) {
+            // Sort by offset, not string value
+            return a < b;
+        });
+
+        dyld3::json::Node root;
+        for (const char* selName : selNames) {
+            dyld3::json::Node selNode;
+            selNode.map["selectorName"] = dyld3::json::Node{selName};
+            selNode.map["offset"] = dyld3::json::Node{(int64_t)selName - (int64_t)dyldCache};
+
+            root.array.push_back(selNode);
+        }
+
+        dyld3::json::printJSON(root, 0, std::cout);
+    }
+    else if ( options.mode == modeExtract ) {
+        return dyld_shared_cache_extract_dylibs(sharedCachePath, options.extractionDir);
+    }
+    else if ( options.mode == modeObjCImpCaches ) {
+        if (sharedCachePath == nullptr) {
+            fprintf(stderr, "Cannot emit imp caches with live cache.  Run again with the path to the cache file\n");
+            return 1;
+        }
+        __block std::map<uint64_t, const char*> methodToClassMap;
+        __block std::map<uint64_t, const char*> classVMAddrToNameMap;
+        const bool contentRebased = false;
+        const uint32_t pointerSize = 8;
+
+        // Get the base pointers from the magic section in objc
+        __block uint64_t objcCacheOffsetsSize = 0;
+        __block const void* objcCacheOffsets = nullptr;
+        __block Diagnostics diag;
+        dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
+            if ( !strcmp(installName, "/usr/lib/libobjc.A.dylib") ) {
+                const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
+                objcCacheOffsets = ma->findSectionContent("__DATA_CONST", "__objc_scoffs", objcCacheOffsetsSize);
+            }
+        });
+
+        if ( objcCacheOffsets == nullptr ) {
+            fprintf(stderr, "Unable to print imp-caches as cannot find __DATA_CONST __objc_scoffs inside /usr/lib/libobjc.A.dylib\n");
+            return 1;
+        }
+
+        if ( objcCacheOffsetsSize < (4 * pointerSize) ) {
+            fprintf(stderr, "Unable to print imp-caches as __DATA_CONST __objc_scoffs is too small (%lld vs required %u)\n", objcCacheOffsetsSize, (4 * pointerSize));
+            return 1;
+        }
+
+        dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = dyldCache->makeVMAddrConverter(contentRebased);
+
+        uint64_t selectorStringVMAddrStart  = vmAddrConverter.convertToVMAddr(((uint64_t*)objcCacheOffsets)[0]);
+        uint64_t selectorStringVMAddrEnd    = vmAddrConverter.convertToVMAddr(((uint64_t*)objcCacheOffsets)[1]);
+
+        dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
+            if (diag.hasError())
+                return;
+
+            const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
+            intptr_t slide = ma->getSlide();
+
+            ma->forEachObjCClass(diag, vmAddrConverter, ^(Diagnostics& diag,
+                                                          uint64_t classVMAddr,
+                                                          uint64_t classSuperclassVMAddr,
+                                                          uint64_t classDataVMAddr,
+                                                          const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass,
+                                                          bool isMetaClass) {
+                const char* className = (const char*)objcClass.nameVMAddr(pointerSize) + slide;
+                classVMAddrToNameMap[classVMAddr] = className;
+                ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), vmAddrConverter,
+                                      ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
+                    // const char* methodName = (const char*)(method.nameVMAddr + slide);
+                    methodToClassMap[method.impVMAddr] = className;
+                });
+            });
+
+            ma->forEachObjCCategory(diag, vmAddrConverter, ^(Diagnostics& diag, uint64_t categoryVMAddr, const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) {
+                ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, vmAddrConverter, ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
+                    const char* catName = (const char*)objcCategory.nameVMAddr + slide;
+                    // const char* methodName = (const char*)(method.nameVMAddr + slide);
+                    methodToClassMap[method.impVMAddr] = catName;
+                });
+
+                ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, vmAddrConverter, ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
+                    const char* catName = (const char*)objcCategory.nameVMAddr + slide;
+                    // const char* methodName = (const char*)(method.nameVMAddr + slide);
+                    methodToClassMap[method.impVMAddr] = catName;
+                });
+            });
+        });
+        if (diag.hasError())
+            return 1;
+
+        dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
+            if (diag.hasError())
+                return;
+
+            const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
+            intptr_t slide = ma->getSlide();
+
+            ma->forEachObjCClass(diag, vmAddrConverter, ^(Diagnostics& diag,
+                                                          uint64_t classVMAddr,
+                                                          uint64_t classSuperclassVMAddr,
+                                                          uint64_t classDataVMAddr,
+                                                          const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass,
+                                                          bool isMetaClass) {
+                const char* type = "class";
+                if (isMetaClass)
+                    type = "meta-class";
+                const char* className = (const char*)objcClass.nameVMAddr(pointerSize) + slide;
+
+                if (objcClass.methodCacheVMAddr == 0) {
+                    printf("%s (%s): empty\n", className, type);
+                    return;
+                }
+
+                struct Bucket {
+                    uint32_t selOffset;
+                    uint32_t impOffset;
+                };
+                struct ImpCache {
+                    int32_t  fallback_class_offset;
+                    uint32_t cache_shift :  5;
+                    uint32_t cache_mask  : 11;
+                    uint32_t occupied    : 14;
+                    uint32_t has_inlines :  1;
+                    uint32_t bit_one     :  1;
+                    struct Bucket buckets[];
+                };
+
+                const ImpCache* impCache = (const ImpCache*)(objcClass.methodCacheVMAddr + slide);
+                printf("%s (%s): %d buckets\n", className, type, impCache->cache_mask + 1);
+
+                if ((classVMAddr + impCache->fallback_class_offset) != objcClass.superclassVMAddr) {
+                    printf("Flattening fallback: %s\n", classVMAddrToNameMap[classVMAddr + impCache->fallback_class_offset]);
+                }
+                // Buckets are a 32-bit offset from the impcache itself
+                for (uint32_t i = 0; i <= impCache->cache_mask ; ++i) {
+                    const Bucket& b = impCache->buckets[i];
+                    uint64_t sel = (uint64_t)b.selOffset + selectorStringVMAddrStart;
+                    uint64_t imp = classVMAddr - (uint64_t)b.impOffset;
+                    if (b.selOffset == 0xFFFFFFFF) {
+                        // Empty bucket
+                        printf("  - 0x%016llx: %s\n", 0ULL, "");
+                    } else {
+                        assert(sel < selectorStringVMAddrEnd);
+
+                        auto it = methodToClassMap.find(imp);
+                        if (it == methodToClassMap.end()) {
+                            fprintf(stderr, "Could not find IMP %llx (for %s)\n", imp, (const char*)(sel + slide));
+                        }
+                        assert(it != methodToClassMap.end());
+                        printf("  - 0x%016llx: %s (from %s)\n", imp, (const char*)(sel + slide), it->second);
+                    }
+                }
+           });
+        });
+    } else {
+        switch ( options.mode ) {
+            case modeList: {
+                // list all dylibs, including their aliases (symlinks to them) with option vmaddr
+                __block std::vector<std::unordered_set<std::string>> indexToPaths;
+                __block std::vector<uint64_t> indexToAddr;
+                __block std::vector<std::string> indexToUUID;
+                dyldCache->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const unsigned char* dylibUUID, const char* installName, bool& stop) {
+                    std::unordered_set<std::string> empty;
+                    if ( options.printVMAddrs )
+                        indexToAddr.push_back(loadAddressUnslid);
+                    if ( options.printUUIDs ) {
+                        uuid_string_t uuidString;
+                        uuid_unparse_upper(dylibUUID, uuidString);
+                        indexToUUID.push_back(uuidString);
+                    }
+                    indexToPaths.push_back(empty);
+                    indexToPaths.back().insert(installName);
+                });
+                dyldCache->forEachDylibPath(^(const char* dylibPath, uint32_t index) {
+                    indexToPaths[index].insert(dylibPath);
+                });
+                int index = 0;
+                for (const std::unordered_set<std::string>& paths : indexToPaths) {
+                    for (const std::string& path: paths) {
+                        if ( options.printVMAddrs )
+                            printf("0x%08llX ", indexToAddr[index]);
+                        if ( options.printUUIDs )
+                             printf("<%s> ", indexToUUID[index].c_str());
+                       printf("%s\n", path.c_str());
+                    }
+                    ++index;
+                }
+                break;
+            }
+            case modeListDylibsWithSection: {
+                dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
+                    dyld3::MachOFile* mf = (dyld3::MachOFile*)mh;
+                    mf->forEachSection(^(const dyld3::MachOFile::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+                        if ( (strcmp(sectInfo.sectName, options.sectionName) == 0) && (strcmp(sectInfo.segInfo.segName, options.segmentName) == 0) ) {
+                            printf("%s\n", installName);
+                            stop = true;
+                        }
+                    });
+                });
+                break;
+            }
+            case modeMap: {
+                __block std::map<uint64_t, const char*> dataSegNames;
+                __block std::map<uint64_t, uint64_t>    dataSegEnds;
+                dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
+                    dyld3::MachOFile* mf = (dyld3::MachOFile*)mh;
+                    mf->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
+                        printf("0x%08llX - 0x%08llX %s %s\n", info.vmAddr, info.vmAddr + info.vmSize, info.segName, installName);
+                        if ( strncmp(info.segName, "__DATA", 6) == 0 ) {
+                            dataSegNames[info.vmAddr] = installName;
+                            dataSegEnds[info.vmAddr] = info.vmAddr + info.vmSize;
+                        }
+                    });
+                });
+                // <rdar://problem/51084507> Enhance dyld_shared_cache_util to show where section alignment added padding
+                uint64_t lastEnd = 0;
+                for (const auto& entry : dataSegEnds) {
+                    uint64_t padding = entry.first - lastEnd;
+                    if ( (padding > 32) && (lastEnd != 0) ) {
+                        printf("0x%08llX - 0x%08llX PADDING %lluKB\n", lastEnd, entry.first, padding/1024);
+                    }
+                    lastEnd = entry.second;
+                }
+                break;
+            }
+            case modeDependencies: {
+                __block bool dependentTargetFound = false;
+                dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
+                    if ( strcmp(options.dependentsOfPath, installName) != 0 )
+                        return;
+                    dependentTargetFound = true;
+
+                    auto printDep = [&options](const char *loadPath, uint32_t compatVersion, uint32_t curVersion) {
+                        if ( options.printDylibVersions ) {
+                            uint32_t compat_vers = compatVersion;
+                            uint32_t current_vers = curVersion;
+                            printf("\t%s", loadPath);
+                            if ( compat_vers != 0xFFFFFFFF ) {
+                                printf("(compatibility version %u.%u.%u, current version %u.%u.%u)\n",
+                                       (compat_vers >> 16),
+                                       (compat_vers >> 8) & 0xff,
+                                       (compat_vers) & 0xff,
+                                       (current_vers >> 16),
+                                       (current_vers >> 8) & 0xff,
+                                       (current_vers) & 0xff);
+                            }
+                            else {
+                                printf("\n");
+                            }
+                        }
+                        else {
+                            printf("\t%s\n", loadPath);
+                        }
+                    };
+
+                    dyld3::MachOFile* mf = (dyld3::MachOFile*)mh;
+
+                    // First print out our dylib and version.
+                    const char* dylibInstallName;
+                    uint32_t currentVersion;
+                    uint32_t compatVersion;
+                    if ( mf->getDylibInstallName(&dylibInstallName, &compatVersion, &currentVersion) ) {
+                        printDep(dylibInstallName, compatVersion, currentVersion);
+                    }
+
+                    // Then the dependent dylibs.
+                    mf->forEachDependentDylib(^(const char *loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
+                        printDep(loadPath, compatVersion, curVersion);
+                    });
+                });
+                if (options.dependentsOfPath && !dependentTargetFound) {
+                    fprintf(stderr, "Error: could not find '%s' in the shared cache at\n  %s\n", options.dependentsOfPath, sharedCachePath);
+                    exit(1);
+                }
+                break;
+            }
+            case modeLinkEdit: {
+                std::map<uint32_t, const char*> pageToContent;
+                auto add_linkedit = [&pageToContent](uint32_t pageStart, uint32_t pageEnd, const char* message) {
+                    for (uint32_t p = pageStart; p <= pageEnd; p += 4096) {
+                        std::map<uint32_t, const char*>::iterator pos = pageToContent.find(p);
+                        if ( pos == pageToContent.end() ) {
+                            pageToContent[p] = strdup(message);
+                        }
+                        else {
+                            const char* oldMessage = pos->second;
+                            char* newMesssage;
+                            asprintf(&newMesssage, "%s, %s", oldMessage, message);
+                            pageToContent[p] = newMesssage;
+                            ::free((void*)oldMessage);
+                        }
+                    }
+                };
+
+                dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
+                    dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
+                    Diagnostics diag;
+                    dyld3::MachOAnalyzer::LinkEditInfo leInfo;
+                    ma->getLinkEditPointers(diag, leInfo);
+
+                    if (diag.hasError())
+                        return;
+
+                    char message[1000];
+                    const char* shortName = strrchr(installName, '/') + 1;
+                    // add export trie info
+                    if ( leInfo.dyldInfo->export_size != 0 ) {
+                        //printf("export_off=0x%X\n", leInfo.dyldInfo->export_off());
+                        uint32_t exportPageOffsetStart = leInfo.dyldInfo->export_off & (-4096);
+                        uint32_t exportPageOffsetEnd = (leInfo.dyldInfo->export_off + leInfo.dyldInfo->export_size) & (-4096);
+                        sprintf(message, "exports from %s", shortName);
+                        add_linkedit(exportPageOffsetStart, exportPageOffsetEnd, message);
+                    }
+                    // add binding info
+                    if ( leInfo.dyldInfo->bind_size != 0 ) {
+                        uint32_t bindPageOffsetStart = leInfo.dyldInfo->bind_off & (-4096);
+                        uint32_t bindPageOffsetEnd = (leInfo.dyldInfo->bind_off + leInfo.dyldInfo->bind_size) & (-4096);
+                        sprintf(message, "bindings from %s", shortName);
+                        add_linkedit(bindPageOffsetStart, bindPageOffsetEnd, message);
+                    }
+                    // add lazy binding info
+                    if ( leInfo.dyldInfo->lazy_bind_size != 0 ) {
+                        uint32_t lazybindPageOffsetStart = leInfo.dyldInfo->lazy_bind_off & (-4096);
+                        uint32_t lazybindPageOffsetEnd = (leInfo.dyldInfo->lazy_bind_off + leInfo.dyldInfo->lazy_bind_size) & (-4096);
+                        sprintf(message, "lazy bindings from %s", shortName);
+                        add_linkedit(lazybindPageOffsetStart, lazybindPageOffsetEnd, message);
+                    }
+                    // add weak binding info
+                    if ( leInfo.dyldInfo->weak_bind_size != 0 ) {
+                        uint32_t weakbindPageOffsetStart = leInfo.dyldInfo->weak_bind_off & (-4096);
+                        uint32_t weakbindPageOffsetEnd = (leInfo.dyldInfo->weak_bind_off + leInfo.dyldInfo->weak_bind_size) & (-4096);
+                        sprintf(message, "weak bindings from %s", shortName);
+                        add_linkedit(weakbindPageOffsetStart, weakbindPageOffsetEnd, message);
+                    }
+                });
+
+                for (std::map<uint32_t, const char*>::iterator it = pageToContent.begin(); it != pageToContent.end(); ++it) {
+                    printf("0x%08X %s\n", it->first, it->second);
+                }
+                break;
+            }
+            case modeSize: {
+                struct TextInfo {
+                    uint64_t    textSize;
+                    const char* path;
+                };
+                __block std::vector<TextInfo> textSegments;
+                dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
+
+                    dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
+                    ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
+                        if ( strcmp(info.segName, "__TEXT") != 0 )
+                            return;
+                        textSegments.push_back({ info.fileSize, installName });
+                    });
+                });
+                std::sort(textSegments.begin(), textSegments.end(), [](const TextInfo& left, const TextInfo& right) {
+                    return (left.textSize > right.textSize);
+                });
+                for (std::vector<TextInfo>::iterator it = textSegments.begin(); it != textSegments.end(); ++it) {
+                    printf(" 0x%08llX  %s\n", it->textSize, it->path);
+                }
+                break;
+            }
+            case modePatchTable: {
+                std::vector<SegmentInfo> segInfos;
+                buildSegmentInfo(dyldCache, segInfos);
+                __block uint32_t imageIndex = 0;
+                dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
+                    printf("%s:\n", installName);
+                    dyldCache->forEachPatchableExport(imageIndex, ^(uint32_t cacheOffsetOfImpl, const char* exportName) {
+                        printf("    export: 0x%08X  %s\n", cacheOffsetOfImpl, exportName);
+                        dyldCache->forEachPatchableUseOfExport(imageIndex, cacheOffsetOfImpl, ^(dyld_cache_patchable_location patchLocation) {
+                            SegmentInfo usageAt;
+                            const uint64_t patchLocVmAddr = dyldCache->unslidLoadAddress() + patchLocation.cacheOffset;
+                            findImageAndSegment(dyldCache, segInfos, patchLocation.cacheOffset, &usageAt);
+                            if ( patchLocation.addend == 0 )
+                                printf("        used by: %s+0x%04llX in %s\n", usageAt.segName, patchLocVmAddr-usageAt.vmAddr, usageAt.installName);
+                            else
+                                printf("        used by: %s+0x%04llX (addend=%d) in %s\n", usageAt.segName, patchLocVmAddr-usageAt.vmAddr, patchLocation.addend, usageAt.installName);
+                        });
+                    });
+                    ++imageIndex;
+                });
+                break;
+            }
+            case modeNone:
+            case modeInfo:
+            case modeSlideInfo:
+            case modeVerboseSlideInfo:
+            case modeTextInfo:
+            case modeLocalSymbols:
+            case modeJSONMap:
+            case modeJSONDependents:
+            case modeSectionSizes:
+            case modeStrings:
+            case modeObjCProtocols:
+            case modeObjCImpCaches:
+            case modeObjCClasses:
+            case modeObjCSelectors:
+            case modeExtract:
+                break;
+        }
+    }
+    return 0;
+}
index 70ab9857c8d00f879efeb71e7331f6892fa721d1..7e4361d21137f1bdc4888563dca59e96260bda0e 100644 (file)
@@ -30,6 +30,7 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
+#include <mach-o/dyld_priv.h>
 
 #include <vector>
 #include <tuple>
 #include "MachOFile.h"
 #include "MachOLoaded.h"
 #include "MachOAnalyzer.h"
+#include "MachOAnalyzerSet.h"
 #include "ClosureFileSystemPhysical.h"
+#include "DyldSharedCache.h"
 
+typedef  dyld3::MachOLoaded::ChainedFixupPointerOnDisk   ChainedFixupPointerOnDisk;
 
 static void versionToString(uint32_t value, char buffer[32])
 {
@@ -67,26 +71,47 @@ static void printPlatforms(const dyld3::MachOAnalyzer* ma)
     });
 }
 
-static void printSegments(const dyld3::MachOAnalyzer* ma)
+static void permString(uint32_t permFlags, char str[4])
 {
-    printf("    -segments:\n");
-    printf("        load-offset   segment section        sect-size  seg-size perm\n");
-    __block const char* lastSegName = "";
-    __block uint64_t    firstSegVmAddr = 0;
-    ma->forEachSection(^(const dyld3::MachOFile::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
-        if ( lastSegName[0] == '\0' )
-            firstSegVmAddr = sectInfo.segInfo.vmAddr;
-        if ( strcmp(lastSegName, sectInfo.segInfo.segName) != 0 ) {
-            char r = (sectInfo.segInfo.protections & VM_PROT_READ)    ? 'r' : '.';
-            char w = (sectInfo.segInfo.protections & VM_PROT_WRITE)   ? 'w' : '.';
-            char x = (sectInfo.segInfo.protections & VM_PROT_EXECUTE) ? 'x' : '.';
-            printf("        0x%08llX    %-12s                      %6lluKB %c%c%c\n", sectInfo.segInfo.vmAddr - firstSegVmAddr, sectInfo.segInfo.segName, sectInfo.segInfo.vmSize/1024, r, w, x);
-            lastSegName = sectInfo.segInfo.segName;
-        }
-            printf("        0x%08llX             %-16s %6llu\n", sectInfo.sectAddr-firstSegVmAddr, sectInfo.sectName, sectInfo.sectSize);
-
-    });
+    str[0] = (permFlags & VM_PROT_READ)    ? 'r' : '.';
+    str[1] = (permFlags & VM_PROT_WRITE)   ? 'w' : '.';
+    str[2] = (permFlags & VM_PROT_EXECUTE) ? 'x' : '.';
+    str[3] = '\0';
+}
 
+static void printSegments(const dyld3::MachOAnalyzer* ma)
+{
+    if ( ma->inDyldCache() ) {
+        printf("    -segments:\n");
+        printf("       load-address    segment section        sect-size  seg-size perm\n");
+        __block const char* lastSegName = "";
+        ma->forEachSection(^(const dyld3::MachOFile::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+            if ( strcmp(lastSegName, sectInfo.segInfo.segName) != 0 ) {
+                char permChars[8];
+                permString(sectInfo.segInfo.protections, permChars);
+                printf("        0x%08llX    %-16s            %16lluKB %s\n", sectInfo.segInfo.vmAddr, sectInfo.segInfo.segName, sectInfo.segInfo.vmSize/1024, permChars);
+                lastSegName = sectInfo.segInfo.segName;
+            }
+                printf("        0x%08llX             %-16s %6llu\n", sectInfo.sectAddr, sectInfo.sectName, sectInfo.sectSize);
+        });
+    }
+    else {
+        printf("    -segments:\n");
+        printf("        load-offset   segment section        sect-size  seg-size perm\n");
+        __block const char* lastSegName = "";
+        __block uint64_t    firstSegVmAddr = 0;
+        ma->forEachSection(^(const dyld3::MachOFile::SectionInfo& sectInfo, bool malformedSectionRange, bool& stop) {
+            if ( lastSegName[0] == '\0' )
+                firstSegVmAddr = sectInfo.segInfo.vmAddr;
+            if ( strcmp(lastSegName, sectInfo.segInfo.segName) != 0 ) {
+                char permChars[8];
+                permString(sectInfo.segInfo.protections, permChars);
+                printf("        0x%08llX    %-16s                      %6lluKB %s\n", sectInfo.segInfo.vmAddr - firstSegVmAddr, sectInfo.segInfo.segName, sectInfo.segInfo.vmSize/1024, permChars);
+                lastSegName = sectInfo.segInfo.segName;
+            }
+                printf("        0x%08llX             %-16s %6llu\n", sectInfo.sectAddr-firstSegVmAddr, sectInfo.sectName, sectInfo.sectSize);
+        });
+    }
  }
 
 
@@ -106,47 +131,126 @@ static void printDependents(const dyld3::MachOAnalyzer* ma)
     });
 }
 
-static const char* rebaseTypeName(uint8_t type)
+static bool liveMachO(const dyld3::MachOAnalyzer* ma, const DyldSharedCache* dyldCache, size_t cacheLen)
 {
-    switch (type ){
-        case REBASE_TYPE_POINTER:
-            return "rebase pointer";
-        case REBASE_TYPE_TEXT_ABSOLUTE32:
-            return "rebase text abs32";
-        case REBASE_TYPE_TEXT_PCREL32:
-            return "rebase text rel32";
-    }
-    return "!!unknown!!";
+    if ( dyldCache == nullptr )
+        return false;
+    const uint8_t* cacheStart = (uint8_t*)dyldCache;
+    const uint8_t* cacheEnd   = &cacheStart[cacheLen];
+    if ( (uint8_t*)ma < cacheStart)
+        return false;
+    if ( (uint8_t*)ma > cacheEnd)
+        return false;
+
+    // only return true for live images
+    return ( dyld_image_header_containing_address(ma) != nullptr );
 }
 
-static const char* bindTypeName(uint8_t type)
+static void printInitializers(const dyld3::MachOAnalyzer* ma, const DyldSharedCache* dyldCache, size_t cacheLen)
 {
-    switch (type ){
-        case BIND_TYPE_POINTER:
-            return "bind pointer";
-        case BIND_TYPE_TEXT_ABSOLUTE32:
-            return "bind text abs32";
-        case BIND_TYPE_TEXT_PCREL32:
-            return "bind text rel32";
+    printf("    -inits:\n");
+    Diagnostics diag;
+    const dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = (ma->inDyldCache() ? dyldCache->makeVMAddrConverter(true) : ma->makeVMAddrConverter(false));
+    ma->forEachInitializer(diag, vmAddrConverter, ^(uint32_t offset) {
+        uint64_t    targetLoadAddr = (uint64_t)ma+offset;
+        const char* symbolName;
+        uint64_t    symbolLoadAddr;
+        if ( ma->findClosestSymbol(targetLoadAddr, &symbolName, &symbolLoadAddr) ) {
+            uint64_t delta = targetLoadAddr - symbolLoadAddr;
+            if ( delta == 0 )
+                printf("        0x%08X  %s\n", offset, symbolName);
+            else
+                printf("        0x%08X  %s + 0x%llX\n", offset, symbolName, delta);
+        }
+        else
+            printf("        0x%08X\n", offset);
+    });
+    if ( ma->hasPlusLoadMethod(diag) ) {
+        // can't inspect ObjC of a live dylib
+        if ( liveMachO(ma, dyldCache, cacheLen) ) {
+            printf("         <<<cannot print objc data on live dylib>>>\n");
+            return;
+        }
+        const uint32_t pointerSize = ma->pointerSize();
+        uint64_t prefLoadAddress = ma->preferredLoadAddress();
+        // print all +load methods on classes in this image
+        ma->forEachObjCClass(diag, vmAddrConverter, ^(Diagnostics& diag, uint64_t classVMAddr,
+                             uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
+                             const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
+            if (!isMetaClass)
+                return;
+            dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
+            const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
+            if ( classNameResult == dyld3::MachOAnalyzer::PrintableStringResult::CanPrint ) {
+                ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), vmAddrConverter, ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
+                    dyld3::MachOAnalyzer::PrintableStringResult methodNameResult;
+                    const char* methodName = ma->getPrintableString(method.nameVMAddr, methodNameResult);
+                    if ( methodNameResult == dyld3::MachOAnalyzer::PrintableStringResult::CanPrint ) {
+                        if ( strcmp(methodName, "load") == 0 )
+                            printf("        0x%08llX  +[%s %s]\n", methodVMAddr-prefLoadAddress, className, methodName);
+                    }
+                });
+            }
+        });
+        // print all +load methods on categories in this image
+        ma->forEachObjCCategory(diag, vmAddrConverter, ^(Diagnostics& diag, uint64_t categoryVMAddr,
+                                                         const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) {
+            dyld3::MachOAnalyzer::PrintableStringResult categoryNameResult;
+            const char* categoryName = ma->getPrintableString(objcCategory.nameVMAddr, categoryNameResult);
+            if ( categoryNameResult == dyld3::MachOAnalyzer::PrintableStringResult::CanPrint ) {
+                ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, vmAddrConverter, ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
+                    dyld3::MachOAnalyzer::PrintableStringResult methodNameResult;
+                    const char* methodName = ma->getPrintableString(method.nameVMAddr, methodNameResult);
+                    if ( methodNameResult == dyld3::MachOAnalyzer::PrintableStringResult::CanPrint ) {
+                        if ( strcmp(methodName, "load") == 0 ) {
+                            // FIXME: if category is on class in another image, forEachObjCCategory returns null for objcCategory.clsVMAddr, need way to get name
+                            __block const char* catOnClassName = "";
+                            ma->forEachObjCClass(diag, vmAddrConverter, ^(Diagnostics& diag, uint64_t classVMAddr,
+                                                uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
+                                                const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
+                                if ( objcCategory.clsVMAddr == classVMAddr ) {
+                                    dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
+                                    const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
+                                    if ( classNameResult == dyld3::MachOAnalyzer::PrintableStringResult::CanPrint ) {
+                                        catOnClassName = className;
+                                    }
+                                }
+                            });
+                            printf("        0x%08llX  +[%s(%s) %s]\n", methodVMAddr-prefLoadAddress, catOnClassName, categoryName, methodName);
+                        }
+                    }
+                });
+            }
+        });
     }
-    return "!!unknown!!";
 }
 
+
 static const char* pointerFormat(uint16_t format)
 {
     switch (format) {
         case DYLD_CHAINED_PTR_ARM64E:
-            return "authenticated arm64e";
-        case DYLD_CHAINED_PTR_ARM64E_OFFSET:
-            return "authenticated arm64e offset";
+            return "authenticated arm64e, 8-byte stride, target vmadddr";
+        case DYLD_CHAINED_PTR_ARM64E_USERLAND:
+            return "authenticated arm64e, 8-byte stride, target vmoffset";
+        case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
+            return "authenticated arm64e, 4-byte stride, target vmadddr";
+        case DYLD_CHAINED_PTR_ARM64E_KERNEL:
+            return "authenticated arm64e, 4-byte stride, target vmoffset";
         case DYLD_CHAINED_PTR_64:
-            return "generic 64-bit";
+            return "generic 64-bit, 4-byte stride, target vmadddr";
         case DYLD_CHAINED_PTR_64_OFFSET:
-            return "generic 64-bit offset";
+            return "generic 64-bit, 4-byte stride, target vmoffset ";
         case DYLD_CHAINED_PTR_32:
             return "generic 32-bit";
         case DYLD_CHAINED_PTR_32_CACHE:
             return "32-bit for dyld cache";
+        case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
+            return "64-bit for kernel cache";
+        case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
+            return "64-bit for x86_64 kernel cache";
+        case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
+            return "authenticated arm64e, 8-byte stride, target vmoffset, 24-bit bind ordinals";
     }
     return "unknown";
 }
@@ -196,16 +300,20 @@ static void printChainDetails(const dyld3::MachOAnalyzer* ma)
 {
     __block Diagnostics diag;
     ma->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) {
-        ma->forEachFixupInAllChains(diag, starts, true, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
+        ma->forEachFixupInAllChains(diag, starts, true, ^(ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
             uint64_t vmOffset = (uint8_t*)fixupLoc - (uint8_t*)ma;
             switch (segInfo->pointer_format) {
                 case DYLD_CHAINED_PTR_ARM64E:
-                case DYLD_CHAINED_PTR_ARM64E_OFFSET:
+                case DYLD_CHAINED_PTR_ARM64E_KERNEL:
+                case DYLD_CHAINED_PTR_ARM64E_USERLAND:
+                case DYLD_CHAINED_PTR_ARM64E_FIRMWARE:
+                case DYLD_CHAINED_PTR_ARM64E_USERLAND24:
                    if ( fixupLoc->arm64e.authRebase.auth ) {
+                        uint32_t bindOrdinal = (segInfo->pointer_format == DYLD_CHAINED_PTR_ARM64E_USERLAND24) ? fixupLoc->arm64e.authBind24.ordinal : fixupLoc->arm64e.authBind.ordinal;
                         if ( fixupLoc->arm64e.authBind.bind ) {
                              printf("  0x%08llX:  raw: 0x%016llX    auth-bind: (next: %03d, key: %s, addrDiv: %d, diversity: 0x%04X, ordinal: %04X)\n", vmOffset, fixupLoc->raw64,
                                    fixupLoc->arm64e.authBind.next, fixupLoc->arm64e.keyName(),
-                                   fixupLoc->arm64e.authBind.addrDiv, fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.ordinal);
+                                   fixupLoc->arm64e.authBind.addrDiv, fixupLoc->arm64e.authBind.diversity, bindOrdinal);
                         }
                         else {
                             printf("  0x%08llX:  raw: 0x%016llX  auth-rebase: (next: %03d, key: %s, addrDiv: %d, diversity: 0x%04X, target: 0x%08X)\n", vmOffset, fixupLoc->raw64,
@@ -214,9 +322,10 @@ static void printChainDetails(const dyld3::MachOAnalyzer* ma)
                         }
                     }
                     else {
+                        uint32_t bindOrdinal = (segInfo->pointer_format == DYLD_CHAINED_PTR_ARM64E_USERLAND24) ? fixupLoc->arm64e.bind24.ordinal : fixupLoc->arm64e.bind.ordinal;
                         if ( fixupLoc->arm64e.rebase.bind ) {
                             printf("  0x%08llX:  raw: 0x%016llX         bind: (next: %03d, ordinal: %04X, addend: %d)\n", vmOffset, fixupLoc->raw64,
-                                  fixupLoc->arm64e.bind.next, fixupLoc->arm64e.bind.ordinal, fixupLoc->arm64e.bind.addend);
+                                  fixupLoc->arm64e.bind.next, bindOrdinal, fixupLoc->arm64e.bind.addend);
                         }
                         else {
                             printf("  0x%08llX:  raw: 0x%016llX       rebase: (next: %03d, target: 0x%011llX, high8: 0x%02X)\n", vmOffset, fixupLoc->raw64,
@@ -264,9 +373,10 @@ static void printChainDetails(const dyld3::MachOAnalyzer* ma)
 
 struct FixupInfo
 {
-    const char* segName;
+    std::string segName;
     std::string sectName;
     uint64_t    address;
+    dyld3::MachOAnalyzerSet::PointerMetaData pmd;
     const char* type;
     uint64_t    targetValue;
     const char* targetDylib;
@@ -276,6 +386,14 @@ struct FixupInfo
 };
 
 
+struct SymbolicFixupInfo
+{
+    uint64_t    address;
+    const char* kind;
+    std::string target;
+};
+
+
 
 static const char* ordinalName(const dyld3::MachOAnalyzer* ma, int libraryOrdinal)
 {
@@ -326,6 +444,7 @@ private:
     const dyld3::MachOAnalyzer*           _ma;
     uint64_t                              _baseAddress;
     mutable dyld3::MachOFile::SectionInfo _lastSection;
+    mutable char                          _lastSegName[20];
     mutable char                          _lastSectName[20];
 };
 
@@ -346,10 +465,13 @@ bool SectionFinder::isNewSection(uint64_t vmOffset) const
 void SectionFinder::updateLastSection(uint64_t vmOffset) const
 {
     if ( isNewSection(vmOffset) ) {
+        _lastSegName[0] = '\0';
+        _lastSectName[0] = '\0';
         uint64_t vmAddr = _baseAddress + vmOffset;
         _ma->forEachSection(^(const dyld3::MachOFile::SectionInfo& sectInfo, bool malformedSectionRange, bool& sectStop) {
             if ( (sectInfo.sectAddr <= vmAddr) && (vmAddr < sectInfo.sectAddr+sectInfo.sectSize) ) {
                 _lastSection = sectInfo;
+                strcpy(_lastSegName, _lastSection.segInfo.segName);
                 strcpy(_lastSectName, _lastSection.sectName);
                 sectStop = true;
             }
@@ -360,7 +482,7 @@ void SectionFinder::updateLastSection(uint64_t vmOffset) const
 const char* SectionFinder::segmentName(uint64_t vmOffset) const
 {
     updateLastSection(vmOffset);
-    return _lastSection.segInfo.segName;
+    return _lastSegName;
 }
 
 const char* SectionFinder::sectionName(uint64_t vmOffset) const
@@ -370,311 +492,18 @@ const char* SectionFinder::sectionName(uint64_t vmOffset) const
 }
 
 
-
-static void printPreloadChainedFixups(const dyld3::MachOAnalyzer* ma)
-{
-    printf("        segment     section           address       type            (dvrsty   addr  key)  target\n");
-    SectionFinder namer(ma);
-    uint64_t sectionSize;
-    const dyld_chained_starts_offsets* startsSection = (dyld_chained_starts_offsets*)ma->findSectionContent("__TEXT", "__chain_starts", sectionSize);
-    if ( startsSection != nullptr ) {
-        switch (startsSection->pointer_format) {
-            case DYLD_CHAINED_PTR_32_FIRMWARE:
-                for (uint32_t startIndex=0; startIndex < startsSection->starts_count; ++startIndex) {
-                    const dyld_chained_ptr_32_firmware_rebase* p = (dyld_chained_ptr_32_firmware_rebase*)(((uint8_t*)ma)+ startsSection->chain_starts[startIndex]);
-                    bool done = false;
-                    while (!done) {
-                        uint64_t vmOffset = ((uint8_t*)p - (uint8_t*)ma);
-                        printf("        %-12s %-16s 0x%08llX  %16s                        0x%08X\n",
-                                namer.segmentName(vmOffset), namer.sectionName(vmOffset), namer.baseAddress()+vmOffset,
-                                "rebase pointer", p->target);
-                        done = (p->next == 0);
-                        p += p->next;
-                    }
-                }
-        }
-
-    }
-}
-
-
-
-struct FixupTarget
-{
-    uint64_t    value;
-    const char* dylib;
-    const char* symbolName;
-    uint64_t    addend;
-    bool        weakImport;
-};
-
-
-static void printChainedFixups(const dyld3::MachOAnalyzer* ma)
-{
-    // build array of targets
-    __block Diagnostics diag;
-    __block std::vector<FixupTarget> targets;
-    ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
-        FixupTarget target;
-        target.value      = 0;
-        target.dylib      = ordinalName(ma, libOrdinal);
-        target.symbolName = symbolName;
-        target.addend     = addend;
-        target.weakImport = weakImport;
-        targets.push_back(target);
-    });
-    if ( diag.hasError() )
-        return;
-
-    uint64_t baseAddress = ma->preferredLoadAddress();
-
-    printf("        segment      section          address         type          (dvrsty  addr  key)  target\n");
-    SectionFinder namer(ma);
-    ma->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) {
-        ma->forEachFixupInAllChains(diag, starts, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
-            uint64_t vmOffset = (uint8_t*)fixupLoc - (uint8_t*)ma;
-            switch (segInfo->pointer_format) {
-                case DYLD_CHAINED_PTR_ARM64E:
-                   if ( fixupLoc->arm64e.authRebase.auth ) {
-                        if ( fixupLoc->arm64e.authBind.bind ) {
-                            const FixupTarget& bindTarget = targets[fixupLoc->arm64e.authBind.ordinal];
-                            const char* weakImportString  =  bindTarget.weakImport ? " [weak-import]" : "";
-                            if ( bindTarget.addend )
-                                printf("        %-12s %-16s 0x%08llX  %16s  (0x%04X   %d     %s)   %s/%s + 0x%llX%s\n",
-                                       namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), "bind authptr",
-                                       fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv,
-                                       fixupLoc->arm64e.keyName(), bindTarget.dylib, bindTarget.symbolName, bindTarget.addend, weakImportString);
-                            else
-                                printf("        %-12s %-16s 0x%08llX  %16s  (0x%04X   %d     %s)   %s/%s%s\n",
-                                       namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), "bind authptr",
-                                       fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv,
-                                       fixupLoc->arm64e.keyName(), bindTarget.dylib, bindTarget.symbolName, weakImportString);
-                         }
-                        else {
-                            uint64_t targetAddr = fixupLoc->arm64e.authRebase.target + baseAddress;
-                            printf("        %-12s %-16s 0x%08llX  %16s  (0x%04X   %d     %s)   0x%08llX\n",
-                                   namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), "rebase authptr",
-                                   fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv,
-                                   fixupLoc->arm64e.keyName(), targetAddr);
-                        }
-                    }
-                    else {
-                        if ( fixupLoc->arm64e.rebase.bind ) {
-                            const FixupTarget& bindTarget = targets[fixupLoc->arm64e.bind.ordinal];
-                            uint64_t fullAddend = bindTarget.addend + fixupLoc->arm64e.signExtendedAddend();
-                            const char* weakImportString  =  bindTarget.weakImport ? " [weak-import]" : "";
-                            if ( fullAddend )
-                                printf("        %-12s %-16s 0x%08llX  %16s                        %s/%s + 0x%llX%s\n",
-                                        namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
-                                        "bind pointer", bindTarget.dylib, bindTarget.symbolName, fullAddend, weakImportString);
-                            else
-                                printf("        %-12s %-16s 0x%08llX  %16s                        %s/%s%s\n",
-                                        namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
-                                        "bind pointer", bindTarget.dylib, bindTarget.symbolName, weakImportString);
-                        }
-                        else {
-                            uint64_t targetAddr = fixupLoc->arm64e.unpackTarget();
-                            printf("        %-12s %-16s 0x%08llX  %16s                        0x%08llX\n",
-                                    namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
-                                    "rebase pointer", targetAddr);
-                        }
-                    }
-                    break;
-                case DYLD_CHAINED_PTR_ARM64E_OFFSET:
-                   if ( fixupLoc->arm64e.authRebase.auth ) {
-                        if ( fixupLoc->arm64e.authBind.bind ) {
-                            const FixupTarget& bindTarget = targets[fixupLoc->arm64e.authBind.ordinal];
-                            const char* weakImportString  =  bindTarget.weakImport ? " [weak-import]" : "";
-                            if ( bindTarget.addend )
-                                printf("        %-12s %-16s 0x%08llX  %16s  (0x%04X   %d     %s)   %s/%s + 0x%llX%s\n",
-                                       namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), "bind authptr",
-                                       fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv,
-                                       fixupLoc->arm64e.keyName(), bindTarget.dylib, bindTarget.symbolName, bindTarget.addend, weakImportString);
-                            else
-                                printf("        %-12s %-16s 0x%08llX  %16s  (0x%04X   %d     %s)   %s/%s%s\n",
-                                       namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), "bind authptr",
-                                       fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv,
-                                       fixupLoc->arm64e.keyName(), bindTarget.dylib, bindTarget.symbolName, weakImportString);
-                         }
-                        else {
-                            uint64_t targetAddr = fixupLoc->arm64e.authRebase.target + baseAddress;
-                            printf("        %-12s %-16s 0x%08llX  %16s  (0x%04X   %d     %s)   0x%08llX\n",
-                                   namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(), "rebase authptr",
-                                   fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv,
-                                   fixupLoc->arm64e.keyName(), targetAddr);
-                        }
-                    }
-                    else {
-                        if ( fixupLoc->arm64e.rebase.bind ) {
-                            const FixupTarget& bindTarget = targets[fixupLoc->arm64e.bind.ordinal];
-                            uint64_t fullAddend = bindTarget.addend + fixupLoc->arm64e.signExtendedAddend();
-                            const char* weakImportString  =  bindTarget.weakImport ? " [weak-import]" : "";
-                            if ( fullAddend )
-                                printf("        %-12s %-16s 0x%08llX  %16s                        %s/%s + 0x%llX%s\n",
-                                        namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
-                                        "bind pointer", bindTarget.dylib, bindTarget.symbolName, fullAddend, weakImportString);
-                            else
-                                printf("        %-12s %-16s 0x%08llX  %16s                        %s/%s%s\n",
-                                        namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
-                                        "bind pointer", bindTarget.dylib, bindTarget.symbolName, weakImportString);
-                        }
-                        else {
-                            uint64_t targetAddr = fixupLoc->arm64e.unpackTarget() + baseAddress;
-                            printf("        %-12s %-16s 0x%08llX  %16s                        0x%08llX\n",
-                                    namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
-                                    "rebase pointer", targetAddr);
-                        }
-                    }
-                    break;
-                case DYLD_CHAINED_PTR_64:
-                    if ( fixupLoc->generic64.rebase.bind ) {
-                        const FixupTarget& bindTarget = targets[fixupLoc->generic64.bind.ordinal];
-                        uint64_t fullAddend = bindTarget.addend + fixupLoc->generic64.signExtendedAddend();
-                        const char* weakImportString  =  bindTarget.weakImport ? " [weak-import]" : "";
-                        if ( fullAddend )
-                            printf("        %-12s %-16s 0x%08llX  %16s                        %s/%s + 0x%llX%s\n",
-                                   namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
-                                    "bind pointer", bindTarget.dylib, bindTarget.symbolName, fullAddend, weakImportString);
-                        else
-                            printf("        %-12s %-16s 0x%08llX  %16s                        %s/%s%s\n",
-                                    namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
-                                    "bind pointer", bindTarget.dylib, bindTarget.symbolName, weakImportString);
-                    }
-                    else {
-                        uint64_t targetAddr = fixupLoc->generic64.unpackedTarget();
-                        printf("        %-12s %-16s 0x%08llX  %16s                        0x%08llX\n",
-                                namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
-                                "rebase pointer", targetAddr);
-                    }
-                    break;
-                case DYLD_CHAINED_PTR_64_OFFSET:
-                    if ( fixupLoc->generic64.rebase.bind ) {
-                        const FixupTarget& bindTarget = targets[fixupLoc->generic64.bind.ordinal];
-                        uint64_t fullAddend = bindTarget.addend + fixupLoc->generic64.signExtendedAddend();
-                        const char* weakImportString  =  bindTarget.weakImport ? " [weak-import]" : "";
-                        if ( fullAddend )
-                            printf("        %-12s %-16s 0x%08llX  %16s                        %s/%s + 0x%llX%s\n",
-                                   namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
-                                    "bind pointer", bindTarget.dylib, bindTarget.symbolName, fullAddend, weakImportString);
-                        else
-                            printf("        %-12s %-16s 0x%08llX  %16s                        %s/%s%s\n",
-                                    namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
-                                    "bind pointer", bindTarget.dylib, bindTarget.symbolName, weakImportString);
-                    }
-                    else {
-                        uint64_t targetAddr = fixupLoc->generic64.unpackedTarget() + baseAddress;
-                        printf("        %-12s %-16s 0x%08llX  %16s                        0x%08llX\n",
-                                namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
-                                "rebase pointer", targetAddr);
-                    }
-                    break;
-                case DYLD_CHAINED_PTR_32:
-                    if ( fixupLoc->generic32.rebase.bind ) {
-                        const FixupTarget& bindTarget = targets[fixupLoc->generic32.bind.ordinal];
-                        uint32_t fullAddend = (uint32_t)bindTarget.addend + fixupLoc->generic32.bind.addend;
-                        const char* weakImportString  =  bindTarget.weakImport ? " [weak-import]" : "";
-                        if ( fullAddend )
-                            printf("        %-12s %-16s 0x%08llX  %16s                        %s/%s + 0x%X%s\n",
-                                    namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
-                                    "bind pointer", bindTarget.dylib, bindTarget.symbolName, fullAddend, weakImportString);
-                        else
-                            printf("        %-12s %-16s 0x%08llX  %16s                        %s/%s%s\n",
-                                    namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
-                                    "bind pointer", bindTarget.dylib, bindTarget.symbolName, weakImportString);
-                    }
-                    else {
-                        uint32_t targetAddr = fixupLoc->generic32.rebase.target;
-                        printf("        %-12s %-16s 0x%08llX  %16s                        0x%08X\n",
-                                namer.segmentName(vmOffset), namer.sectionName(vmOffset), vmOffset+namer.baseAddress(),
-                                "rebase pointer", targetAddr);
-                    }
-                    break;
-                default:
-                    fprintf(stderr, "unknown pointer type %d\n", segInfo->pointer_format);
-                    break;
-            }
-         });
-    });
-    if ( diag.hasError() )
-        fprintf(stderr, "dyldinfo: %s\n", diag.errorMessage());
-}
-
-static void printOpcodeFixups(const dyld3::MachOAnalyzer* ma)
-{
-    Diagnostics diag;
-    __block std::vector<FixupInfo> fixups;
-    SectionFinder namer(ma);
-    ma->forEachRebase(diag, ^(const char* opcodeName, const dyld3::MachOLoaded::LinkEditInfo& leInfo, const dyld3::MachOFile::SegmentInfo segments[],
-                              bool segIndexSet, uint32_t pointerSize, uint8_t segIndex, uint64_t segOffset, uint8_t type, bool& stop) {
-        const dyld3::MachOFile::SegmentInfo& segment = segments[segIndex];
-        uint64_t locVmAddr = segment.vmAddr + segOffset;
-        uint64_t runtimeOffset = locVmAddr - namer.baseAddress();
-        const uint8_t* loc = ((uint8_t*)ma + runtimeOffset);
-        uint64_t value = (pointerSize == 8) ? *((uint64_t*)(loc)) : *((uint32_t*)(loc));
-        FixupInfo fixup;
-        fixup.segName           = namer.segmentName(runtimeOffset);
-        fixup.sectName          = namer.sectionName(runtimeOffset);
-        fixup.address           = locVmAddr;
-        fixup.type              = rebaseTypeName(type);
-        fixup.targetValue       = value;
-        fixup.targetDylib       = nullptr;
-        fixup.targetSymbolName  = nullptr;
-        fixup.targetAddend      = 0;
-        fixup.targetWeakImport  = false;
-        fixups.push_back(fixup);
-    });
-
-    ma->forEachBind(diag, ^(const char* opcodeName, const dyld3::MachOLoaded::LinkEditInfo& leInfo, const dyld3::MachOFile::SegmentInfo segments[],
-                            bool segIndexSet,  bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
-                            uint32_t pointerSize, uint8_t segIndex, uint64_t segOffset,
-                            uint8_t type, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop) {
-        const dyld3::MachOFile::SegmentInfo& segment = segments[segIndex];
-        uint64_t locVmAddr = segment.vmAddr + segOffset;
-        uint64_t runtimeOffset = locVmAddr - namer.baseAddress();
-        FixupInfo fixup;
-        fixup.segName           = namer.segmentName(runtimeOffset);
-        fixup.sectName          = namer.sectionName(runtimeOffset);
-        fixup.address           = locVmAddr;
-        fixup.type              = bindTypeName(type);
-        fixup.targetValue       = 0;
-        fixup.targetDylib       = ordinalName(ma, libOrdinal);
-        fixup.targetSymbolName  = symbolName;
-        fixup.targetAddend      = addend;
-        fixup.targetWeakImport  = weakImport;
-        fixups.push_back(fixup);
-    },^(const char* symbolName) {
-    },^() { });
-
-
-    std::sort(fixups.begin(), fixups.end(), [](const FixupInfo& l, const FixupInfo& r) {
-        if ( &l == &r )
-            return false;
-        if ( l.address == r.address )
-            return (l.targetSymbolName == nullptr);
-        return ( l.address < r.address );
-    });
-
-    printf("        segment      section          address         type          target\n");
-    for (const FixupInfo& fixup : fixups) {
-        if ( fixup.targetSymbolName == nullptr )
-            printf("        %-12s %-16s 0x%08llX  %16s  0x%08llX\n", fixup.segName, fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetValue);
-        else if ( fixup.targetAddend != 0 )
-            printf("        %-12s %-16s 0x%08llX  %16s  %s/%s + 0x%llX\n", fixup.segName, fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetDylib, fixup.targetSymbolName, fixup.targetAddend);
-        else if ( fixup.targetWeakImport )
-            printf("        %-12s %-16s 0x%08llX  %16s  %s/%s [weak-import]\n", fixup.segName, fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetDylib, fixup.targetSymbolName);
-        else
-            printf("        %-12s %-16s 0x%08llX  %16s  %s/%s\n", fixup.segName, fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetDylib, fixup.targetSymbolName);
-   }
-
-
-}
-
 static inline std::string decimal(int64_t value) {
     char buff[64];
     sprintf(buff, "%lld", value);
     return buff;
 }
 
+static inline std::string hex(int64_t value) {
+    char buff[64];
+    sprintf(buff, "0x%llX", value);
+    return buff;
+}
+
 static std::string rebaseTargetString(const dyld3::MachOAnalyzer* ma, uint64_t vmAddr)
 {
     uint64_t    targetLoadAddr = (uint64_t)ma+vmAddr;
@@ -686,7 +515,10 @@ static std::string rebaseTargetString(const dyld3::MachOAnalyzer* ma, uint64_t v
             return targetSymbolName;
         }
         else {
-            return std::string(targetSymbolName) + std::string("+") + decimal(delta);
+            if ( (delta == 1) && (ma->cputype == CPU_TYPE_ARM) )
+                return std::string(targetSymbolName) + std::string(" [thumb]");
+            else
+                return std::string(targetSymbolName) + std::string("+") + decimal(delta);
         }
     }
     else {
@@ -706,249 +538,224 @@ static std::string rebaseTargetString(const dyld3::MachOAnalyzer* ma, uint64_t v
     }
 }
 
-static void printSymbolicChainedFixups(const dyld3::MachOAnalyzer* ma)
+
+
+typedef dyld3::MachOAnalyzerSet                 MachOAnalyzerSet;
+typedef dyld3::MachOAnalyzerSet::WrappedMachO   WrappedMachO;
+
+struct InfoAnalyzerSet : public MachOAnalyzerSet
 {
-    // build array of targets
-    __block Diagnostics diag;
-    __block std::vector<FixupTarget> targets;
-    ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
-        FixupTarget target;
-        target.value      = 0;
-        target.dylib      = ordinalName(ma, libOrdinal);
-        target.symbolName = symbolName;
-        target.addend     = addend;
-        target.weakImport = weakImport;
-        targets.push_back(target);
-    });
-    if ( diag.hasError() )
-        return;
+    void            mas_forEachImage(void (^handler)(const WrappedMachO& anImage, bool hidden, bool& stop)) const override;
+    void            mas_mainExecutable(WrappedMachO& anImage) const override;
+    void*           mas_dyldCache() const override;
+    bool            wmo_dependent(const WrappedMachO* image, uint32_t depIndex, WrappedMachO& childObj, bool& missingWeakDylib) const override;
+    const char*     wmo_path(const WrappedMachO* image) const override;
+    ExportsTrie     wmo_getExportsTrie(const WrappedMachO* image) const override;
+    bool            wmo_findSymbolFrom(const WrappedMachO* fromWmo, Diagnostics& diag, int libOrdinal, const char* symbolName, bool weakImport,
+                                       bool lazyBind, uint64_t addend, CachePatchHandler ph, FixupTarget& target) const override;
+    bool            wmo_missingSymbolResolver(const WrappedMachO* fromWmo, bool weakImport, bool lazyBind, const char* symbolName, const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const override;
+};
 
-    // walk all fixup chains
-    uint64_t baseAddress = ma->preferredLoadAddress();
-    SectionFinder sectionInfo(ma);
-    __block uint64_t lastSymbolVmOffset = 0;
-    __block bool     lastSymbolIsSectionStart = false;
-    ma->withChainStarts(diag, 0, ^(const dyld_chained_starts_in_image* starts) {
-        ma->forEachFixupInAllChains(diag, starts, false, ^(dyld3::MachOLoaded::ChainedFixupPointerOnDisk* fixupLoc, const dyld_chained_starts_in_segment* segInfo, bool& stop) {
-            uint64_t fixupVmOffset = (uint8_t*)fixupLoc - (uint8_t*)ma;
-            if ( sectionInfo.isNewSection(fixupVmOffset) ) {
-                printf("        0x%08llX %-12s %-16s \n", sectionInfo.currentSectionAddress(), sectionInfo.segmentName(fixupVmOffset), sectionInfo.sectionName(fixupVmOffset));
-                lastSymbolVmOffset = sectionInfo.currentSectionAddress()-sectionInfo.baseAddress();
-                lastSymbolIsSectionStart = true;
-            }
-            const char* symbolName;
-            uint64_t    symbolLoadAddr = 0;
-            if ( ma->findClosestSymbol((uint64_t)fixupLoc, &symbolName, &symbolLoadAddr) ) {
-                uint64_t symbolVmOffset = symbolLoadAddr - (uint64_t)ma;
-                if ( (symbolVmOffset != lastSymbolVmOffset) || lastSymbolIsSectionStart ) {
-                    printf("        %s:\n", symbolName);
-                    lastSymbolVmOffset = symbolVmOffset;
-                    lastSymbolIsSectionStart = false;
-                }
-            }
-            const char* fixupKind = "";
-            std::string fixupTarget;
-            char authInfo[64];
-            switch (segInfo->pointer_format) {
-                case DYLD_CHAINED_PTR_ARM64E:
-                    if ( fixupLoc->arm64e.authRebase.auth ) {
-                        sprintf(authInfo, "(0x%04X   %d     %s)", fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv, fixupLoc->arm64e.keyName());
-                        if ( fixupLoc->arm64e.authBind.bind ) {
-                            const FixupTarget& bindTarget = targets[fixupLoc->arm64e.authBind.ordinal];
-                            fixupKind = "bind authptr";
-                            fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
-                            if ( bindTarget.addend )
-                                fixupTarget += std::string("+") + decimal(bindTarget.addend);
-                            if ( bindTarget.weakImport )
-                                fixupTarget += " [weak-import]";
-                        }
-                        else {
-                            uint64_t targetVmAddr = fixupLoc->arm64e.authRebase.target + baseAddress;
-                            fixupKind = "rebase authptr";
-                            fixupTarget = rebaseTargetString(ma, targetVmAddr);
-                        }
-                    }
-                    else {
-                        authInfo[0] = '\0';
-                        if ( fixupLoc->arm64e.rebase.bind ) {
-                            const FixupTarget& bindTarget = targets[fixupLoc->arm64e.bind.ordinal];
-                            uint64_t fullAddend = bindTarget.addend + fixupLoc->arm64e.signExtendedAddend();
-                            fixupKind = "bind pointer";
-                            fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
-                            if ( fullAddend )
-                                fixupTarget += std::string("+") + decimal(fullAddend);
-                            if ( bindTarget.weakImport )
-                                fixupTarget += " [weak-import]";
-                        }
-                        else {
-                            uint64_t targetVmAddr = fixupLoc->arm64e.unpackTarget();
-                            fixupKind = "rebase pointer";
-                            fixupTarget = rebaseTargetString(ma, targetVmAddr);
-                        }
-                    }
-                    printf("           +0x%04llX  %16s  %30s   %s\n", fixupVmOffset-lastSymbolVmOffset, fixupKind, authInfo, fixupTarget.c_str());
-                    break;
-                case DYLD_CHAINED_PTR_ARM64E_OFFSET:
-                    if ( fixupLoc->arm64e.authRebase.auth ) {
-                        sprintf(authInfo, "(0x%04X   %d     %s)", fixupLoc->arm64e.authBind.diversity, fixupLoc->arm64e.authBind.addrDiv, fixupLoc->arm64e.keyName());
-                        if ( fixupLoc->arm64e.authBind.bind ) {
-                            const FixupTarget& bindTarget = targets[fixupLoc->arm64e.authBind.ordinal];
-                            fixupKind = "bind authptr";
-                            fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
-                            if ( bindTarget.addend )
-                                fixupTarget += std::string("+") + decimal(bindTarget.addend);
-                            if ( bindTarget.weakImport )
-                                fixupTarget += " [weak-import]";
-                        }
-                        else {
-                            uint64_t targetVmAddr = fixupLoc->arm64e.authRebase.target + baseAddress;
-                            fixupKind = "rebase authptr";
-                            fixupTarget = rebaseTargetString(ma, targetVmAddr);
-                        }
-                    }
-                    else {
-                        authInfo[0] = '\0';
-                        if ( fixupLoc->arm64e.rebase.bind ) {
-                            const FixupTarget& bindTarget = targets[fixupLoc->arm64e.bind.ordinal];
-                            uint64_t fullAddend = bindTarget.addend + fixupLoc->arm64e.signExtendedAddend();
-                            fixupKind = "bind pointer";
-                            fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
-                            if ( fullAddend )
-                                fixupTarget += std::string("+") + decimal(fullAddend);
-                            if ( bindTarget.weakImport )
-                                fixupTarget += " [weak-import]";
-                        }
-                        else {
-                            uint64_t targetVmAddr = fixupLoc->arm64e.unpackTarget() + baseAddress;
-                            fixupKind = "rebase pointer";
-                            fixupTarget = rebaseTargetString(ma, targetVmAddr);
-                        }
-                    }
-                    printf("           +0x%04llX  %16s  %30s   %s\n", fixupVmOffset-lastSymbolVmOffset, fixupKind, authInfo, fixupTarget.c_str());
-                    break;
-                case DYLD_CHAINED_PTR_64:
-                    if ( fixupLoc->generic64.rebase.bind ) {
-                        const FixupTarget& bindTarget = targets[fixupLoc->generic64.bind.ordinal];
-                        fixupKind = "bind pointer";
-                        uint64_t fullAddend = bindTarget.addend + fixupLoc->generic64.signExtendedAddend();
-                        fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
-                        if ( fullAddend )
-                            fixupTarget += std::string("+") + decimal(fullAddend);
-                        if ( bindTarget.weakImport )
-                            fixupTarget += " [weak-import]";
-                    }
-                    else {
-                        uint64_t targetVmAddr = fixupLoc->generic64.unpackedTarget();
-                        fixupKind = "rebase pointer";
-                        fixupTarget = rebaseTargetString(ma, targetVmAddr);
-                    }
-                    printf("           +0x%04llX  %16s   %s\n", fixupVmOffset-lastSymbolVmOffset, fixupKind, fixupTarget.c_str());
-                    break;
-                case DYLD_CHAINED_PTR_64_OFFSET:
-                    if ( fixupLoc->generic64.rebase.bind ) {
-                        const FixupTarget& bindTarget = targets[fixupLoc->generic64.bind.ordinal];
-                        fixupKind = "bind pointer";
-                        uint64_t fullAddend = bindTarget.addend + fixupLoc->generic64.signExtendedAddend();
-                        fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
-                        if ( fullAddend )
-                            fixupTarget += std::string("+") + decimal(fullAddend);
-                        if ( bindTarget.weakImport )
-                            fixupTarget += " [weak-import]";
-                    }
-                    else {
-                        uint64_t targetVmAddr = fixupLoc->generic64.unpackedTarget() + baseAddress;
-                        fixupKind = "rebase pointer";
-                        fixupTarget = rebaseTargetString(ma, targetVmAddr);
-                    }
-                    printf("           +0x%04llX  %16s   %s\n", fixupVmOffset-lastSymbolVmOffset, fixupKind, fixupTarget.c_str());
-                    break;
-                case DYLD_CHAINED_PTR_32:
-                    if ( fixupLoc->generic32.rebase.bind ) {
-                        const FixupTarget& bindTarget = targets[fixupLoc->generic32.bind.ordinal];
-                        uint32_t fullAddend = (uint32_t)bindTarget.addend + fixupLoc->generic32.bind.addend;
-                        fixupKind = "bind pointer";
-                        fixupTarget = std::string(bindTarget.dylib) + "/" + bindTarget.symbolName;
-                        if ( fullAddend )
-                            fixupTarget += std::string("+") + decimal(fullAddend);
-                        if ( bindTarget.weakImport )
-                            fixupTarget += " [weak-import]";
-                    }
-                    else {
-                        uint32_t targetAddr = fixupLoc->generic32.rebase.target;
-                        fixupKind = "rebase pointer";
-                        fixupTarget = rebaseTargetString(ma, targetAddr);
-                    }
-                    printf("           +0x%04llX  %16s   %s\n", fixupVmOffset-lastSymbolVmOffset, fixupKind, fixupTarget.c_str());
-                    break;
-                default:
-                    fprintf(stderr, "unknown pointer type %d\n", segInfo->pointer_format);
-                    break;
-            }
-         });
-    });
-    if ( diag.hasError() )
-        fprintf(stderr, "dyldinfo: %s\n", diag.errorMessage());
+bool InfoAnalyzerSet::wmo_dependent(const WrappedMachO* image, uint32_t depIndex, WrappedMachO& childObj, bool& missingWeakDylib) const
+{
+    const char* depPath = image->_mh->dependentDylibLoadPath(depIndex);
+    childObj = WrappedMachO(nullptr, image->_set, (void*)depPath);
+    return true;
 }
 
-struct SymbolicFixupInfo
+const char* InfoAnalyzerSet::wmo_path(const WrappedMachO* image) const
 {
-    uint64_t    address;
-    const char* kind;
-    std::string target;
-};
+    return (const char*)image->_other;
+}
 
-static void printSymbolicOpcodeFixups(const dyld3::MachOAnalyzer* ma)
+MachOAnalyzerSet::ExportsTrie InfoAnalyzerSet::wmo_getExportsTrie(const WrappedMachO* obj) const
 {
+    return { nullptr, nullptr };
+}
+
+bool InfoAnalyzerSet::wmo_findSymbolFrom(const WrappedMachO* obj, Diagnostics& diag, int libOrdinal, const char* symbolName, bool weakImport,
+                                         bool lazyBind, uint64_t addend, CachePatchHandler ph, MachOAnalyzerSet::FixupTarget& target) const
+{
+    target.requestedSymbolName      = symbolName;
+    target.foundSymbolName          = symbolName;
+    target.addend                   = addend;
+    target.kind                     = weakImport ? MachOAnalyzerSet::FixupTarget::Kind::bindMissingSymbol : MachOAnalyzerSet::FixupTarget::Kind::bindToImage;
+    if ( libOrdinal == BIND_SPECIAL_DYLIB_SELF ) {
+        target.foundInImage = WrappedMachO(nullptr, obj->_set, (void*)"self");
+    }
+    else if ( libOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) {
+        target.foundInImage = WrappedMachO(nullptr, obj->_set, (void*)"main-executable");
+    }
+    else if ( libOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) {
+        target.foundInImage = WrappedMachO(nullptr, obj->_set, (void*)"flat-namespace");
+    }
+    else if ( libOrdinal == BIND_SPECIAL_DYLIB_WEAK_LOOKUP ) {
+        target.foundInImage = WrappedMachO(nullptr, obj->_set, (void*)"weak-coalesce");
+    }
+    else {
+        int depIndex = libOrdinal - 1;
+        const char* depPath = obj->_mh->dependentDylibLoadPath(depIndex);
+        const char* lastSlash = strrchr(depPath, '/');
+        if (lastSlash)
+            ++lastSlash;
+        else
+            lastSlash = depPath;
+        target.foundInImage = WrappedMachO(nullptr, obj->_set, (void*)lastSlash);
+    }
+    return true;
+}
+
+void InfoAnalyzerSet::mas_forEachImage(void (^handler)(const WrappedMachO& anImage, bool hidden, bool& stop)) const
+{
+    WrappedMachO anImage(nullptr, this, (void*)"flat-namespace");
+    bool stop = false;
+    handler(anImage, false, stop);
+}
+
+
+bool InfoAnalyzerSet::wmo_missingSymbolResolver(const WrappedMachO* fromWmo, bool weakImport, bool lazyBind, const char* symbolName, const char* expectedInDylibPath, const char* clientPath, FixupTarget& target) const
+{
+    return false;
+}
+
+void InfoAnalyzerSet::mas_mainExecutable(WrappedMachO& anImage) const
+{
+    anImage = WrappedMachO(nullptr, this, (void*)"main-executable");
+}
+
+void*  InfoAnalyzerSet::mas_dyldCache() const
+{
+    return nullptr;
+}
+
+static void printFixups(const dyld3::MachOAnalyzer* ma, const char* path)
+{
+    printf("    -fixups:\n");
     Diagnostics diag;
-    __block std::vector<SymbolicFixupInfo> fixups;
+    __block std::vector<FixupInfo> fixups;
+    uint64_t prefLoadAddr = ma->preferredLoadAddress();
     SectionFinder namer(ma);
-    ma->forEachRebase(diag, ^(const char* opcodeName, const dyld3::MachOLoaded::LinkEditInfo& leInfo, const dyld3::MachOFile::SegmentInfo segments[],
-                              bool segIndexSet, uint32_t pointerSize, uint8_t segIndex, uint64_t segOffset, uint8_t type, bool& stop) {
-        const dyld3::MachOFile::SegmentInfo& segment = segments[segIndex];
-        uint64_t locVmAddr = segment.vmAddr + segOffset;
-        uint64_t runtimeOffset = locVmAddr - namer.baseAddress();
-        const uint8_t* loc = ((uint8_t*)ma + runtimeOffset);
-        uint64_t value = (pointerSize == 8) ? *((uint64_t*)(loc)) : *((uint32_t*)(loc));
-        SymbolicFixupInfo fixup;
-        fixup.address      = locVmAddr;
-        fixup.kind         = rebaseTypeName(type);
-        fixup.target       = rebaseTargetString(ma, value);
+    InfoAnalyzerSet ias;
+    WrappedMachO wm(ma, &ias, (void*)path);
+    wm.forEachFixup(diag, ^(uint64_t fixupLocRuntimeOffset, MachOAnalyzerSet::PointerMetaData pmd, const MachOAnalyzerSet::FixupTarget& target, bool& stop) {
+        FixupInfo fixup;
+        fixup.segName           = namer.segmentName(fixupLocRuntimeOffset);
+        fixup.sectName          = namer.sectionName(fixupLocRuntimeOffset);
+        fixup.address           = prefLoadAddr + fixupLocRuntimeOffset;
+        fixup.pmd               = pmd;
+        fixup.targetWeakImport  = false;
+        switch ( target.kind ) {
+            case MachOAnalyzerSet::FixupTarget::Kind::bindAbsolute:
+                fixup.type      = "absolute";
+                break;
+            case MachOAnalyzerSet::FixupTarget::Kind::rebase:
+                fixup.type      = "rebase";
+                fixup.targetSymbolName  = nullptr;
+                fixup.targetDylib       = nullptr;
+                break;
+            case MachOAnalyzerSet::FixupTarget::Kind::bindToImage:
+                fixup.type      = "bind";
+                fixup.targetSymbolName  = target.requestedSymbolName;
+                fixup.targetDylib       = target.foundInImage.path();
+                break;
+            case MachOAnalyzerSet::FixupTarget::Kind::bindMissingSymbol:
+                fixup.type      = "bind";
+                fixup.targetSymbolName  = target.requestedSymbolName;
+                fixup.targetDylib       = target.foundInImage.path();
+                fixup.targetWeakImport  = true;
+                break;
+        }
+        fixup.targetValue       = target.offsetInImage;
+        fixup.targetAddend      = target.addend;
+        if ( pmd.high8 )
+            fixup.targetAddend += ((uint64_t)pmd.high8 << 56);
         fixups.push_back(fixup);
+    },
+    ^(uint32_t, uint32_t, const MachOAnalyzerSet::FixupTarget&) {
     });
 
-    ma->forEachBind(diag, ^(const char* opcodeName, const dyld3::MachOLoaded::LinkEditInfo& leInfo, const dyld3::MachOFile::SegmentInfo segments[],
-                            bool segIndexSet,  bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
-                            uint32_t pointerSize, uint8_t segIndex, uint64_t segOffset,
-                            uint8_t type, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop) {
-        const dyld3::MachOFile::SegmentInfo& segment = segments[segIndex];
-        uint64_t locVmAddr = segment.vmAddr + segOffset;
+    std::sort(fixups.begin(), fixups.end(), [](const FixupInfo& l, const FixupInfo& r) {
+        if ( &l == &r )
+            return false;
+        if ( l.address == r.address )
+            return (l.targetSymbolName == nullptr);
+        return ( l.address < r.address );
+    });
+
+    printf("        segment      section          address         type          target\n");
+    for (const FixupInfo& fixup : fixups) {
+        char authInfo[128];
+        authInfo[0] = '\0';
+        if ( fixup.pmd.authenticated ) {
+            sprintf(authInfo, " (div=0x%04X ad=%d key=%s)", fixup.pmd.diversity, fixup.pmd.usesAddrDiversity, ChainedFixupPointerOnDisk::Arm64e::keyName(fixup.pmd.key));
+        }
+        if ( fixup.targetSymbolName == nullptr )
+            printf("        %-12s %-16s 0x%08llX  %16s  0x%08llX%s\n", fixup.segName.c_str(), fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetValue, authInfo);
+        else if ( fixup.targetAddend != 0 )
+            printf("        %-12s %-16s 0x%08llX  %16s  %s/%s + 0x%llX%s\n", fixup.segName.c_str(), fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetDylib, fixup.targetSymbolName, fixup.targetAddend, authInfo);
+        else if ( fixup.targetWeakImport )
+            printf("        %-12s %-16s 0x%08llX  %16s  %s/%s [weak-import]%s\n", fixup.segName.c_str(), fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetDylib, fixup.targetSymbolName, authInfo);
+        else
+            printf("        %-12s %-16s 0x%08llX  %16s  %s/%s%s\n", fixup.segName.c_str(), fixup.sectName.c_str(), fixup.address, fixup.type, fixup.targetDylib, fixup.targetSymbolName, authInfo);
+   }
+}
+
+static void printSymbolicFixups(const dyld3::MachOAnalyzer* ma, const char* path)
+{
+    printf("    -symbolic_fixups:\n");
+    Diagnostics diag;
+    __block std::vector<SymbolicFixupInfo> fixups;
+    uint64_t prefLoadAddr = ma->preferredLoadAddress();
+    InfoAnalyzerSet ias;
+    WrappedMachO wm(ma, &ias, (void*)path);
+    wm.forEachFixup(diag, ^(uint64_t fixupLocRuntimeOffset, MachOAnalyzerSet::PointerMetaData pmd, const MachOAnalyzerSet::FixupTarget& target, bool& stop) {
         SymbolicFixupInfo fixup;
-        fixup.address      = locVmAddr;
-        fixup.kind         = bindTypeName(type);
-        fixup.target       = std::string(ordinalName(ma, libOrdinal)) + "/" + symbolName;
-        if ( addend != 0 )
-            fixup.target += std::string("+") + decimal(addend);
-        if ( weakImport )
-            fixup.target += " [weak-import]";
+        fixup.address = prefLoadAddr + fixupLocRuntimeOffset;
+        switch ( target.kind ) {
+            case MachOAnalyzerSet::FixupTarget::Kind::bindAbsolute:
+                fixup.kind         = "absolute";
+                fixup.target       = "";
+                break;
+            case MachOAnalyzerSet::FixupTarget::Kind::rebase:
+                fixup.kind         = "rebase pointer";
+                fixup.target       = rebaseTargetString(ma, target.offsetInImage);
+                break;
+            case MachOAnalyzerSet::FixupTarget::Kind::bindToImage:
+            case MachOAnalyzerSet::FixupTarget::Kind::bindMissingSymbol:
+                fixup.kind         = "bind pointer";
+                fixup.target       = std::string(target.foundInImage.path()) + "/" + target.requestedSymbolName;
+                if ( target.addend != 0 )
+                    fixup.target += std::string("+") + decimal(target.addend);
+                if ( pmd.high8 )
+                    fixup.target += std::string("+") + hex(((uint64_t)pmd.high8 << 56));
+                //if ( target.weakImport )
+                //    fixup.target += " [weak-import]";
+                break;
+        }
+        if ( pmd.authenticated ) {
+            char authInfo[256];
+            sprintf(authInfo, " (div=0x%04X ad=%d key=%s)", pmd.diversity, pmd.usesAddrDiversity, ChainedFixupPointerOnDisk::Arm64e::keyName(pmd.key));
+            fixup.target += authInfo;
+        }
         fixups.push_back(fixup);
-    },^(const char* symbolName) {
-    },^() { });
+    },
+    ^(uint32_t, uint32_t, const MachOAnalyzerSet::FixupTarget&) {
+    });
 
 
     std::sort(fixups.begin(), fixups.end(), [](const SymbolicFixupInfo& l, const SymbolicFixupInfo& r) {
         if ( &l == &r )
-            return false;
+          return false;
         return ( l.address < r.address );
     });
 
     SectionFinder sectionTracker(ma);
     uint64_t lastSymbolVmOffset = 0;
     for (const SymbolicFixupInfo& fixup : fixups) {
-        uint64_t vmOffset = fixup.address;
-        uint64_t vmAddr   = sectionTracker.baseAddress() + vmOffset;
+        uint64_t vmAddr   = fixup.address;
+        uint64_t vmOffset = vmAddr - prefLoadAddr;
         if ( sectionTracker.isNewSection(vmOffset) ) {
             printf("        0x%08llX %-12s %-16s \n", vmAddr, sectionTracker.segmentName(vmOffset), sectionTracker.sectionName(vmOffset));
-            lastSymbolVmOffset = vmOffset;
         }
         const char* symbolName;
         uint64_t    symbolLoadAddr = 0;
@@ -963,36 +770,6 @@ static void printSymbolicOpcodeFixups(const dyld3::MachOAnalyzer* ma)
     }
 }
 
-static void printFixups(const dyld3::MachOAnalyzer* ma)
-{
-    printf("    -fixups:\n");
-    if ( ma->isPreload() || (ma->isStaticExecutable() && !ma->hasChainedFixups()) ) {
-        printPreloadChainedFixups(ma);
-    }
-    else if ( ma->hasChainedFixups() ) {
-        printChainedFixups(ma);
-    }
-    else {
-        printOpcodeFixups(ma);
-    }
-}
-
-
-static void printSymbolicFixups(const dyld3::MachOAnalyzer* ma)
-{
-    printf("    -symbolic_fixups:\n");
-    if ( ma->isPreload() || ma->isStaticExecutable() ) {
-        printPreloadChainedFixups(ma);
-    }
-    else if ( ma->hasChainedFixups() ) {
-        printSymbolicChainedFixups(ma);
-    }
-    else {
-        printSymbolicOpcodeFixups(ma);
-    }
-}
-
-
 
 static void printExports(const dyld3::MachOAnalyzer* ma)
 {
@@ -1049,11 +826,11 @@ static void printExports(const dyld3::MachOAnalyzer* ma)
 
 }
 
-static void printObjC(const dyld3::MachOAnalyzer* ma)
+static void printObjC(const dyld3::MachOAnalyzer* ma, const DyldSharedCache* dyldCache, size_t cacheLen)
 {
     Diagnostics diag;
-    const bool contentRebased = false;
     const uint32_t pointerSize = ma->pointerSize();
+    const dyld3::MachOAnalyzer::VMAddrConverter vmAddrConverter = (ma->inDyldCache() ? dyldCache->makeVMAddrConverter(true) : ma->makeVMAddrConverter(false));
 
     auto printMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
         const char* type = "method";
@@ -1078,6 +855,11 @@ static void printObjC(const dyld3::MachOAnalyzer* ma)
     };
 
     printf("    -objc:\n");
+    // can't inspect ObjC of a live dylib
+    if ( liveMachO(ma, dyldCache, cacheLen) ) {
+        printf("         <<<cannot print objc data on live dylib>>>\n");
+        return;
+    }
     printf("              type       vmaddr   data-vmaddr   name\n");
     auto printClass = ^(Diagnostics& diag, uint64_t classVMAddr,
                         uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
@@ -1105,8 +887,7 @@ static void printObjC(const dyld3::MachOAnalyzer* ma)
                type, classVMAddr, objcClass.dataVMAddr, className);
 
         // Now print the methods on this class
-        ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), contentRebased,
-                              printMethod);
+        ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), vmAddrConverter, printMethod);
     };
     auto printCategory = ^(Diagnostics& diag, uint64_t categoryVMAddr,
                            const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) {
@@ -1131,9 +912,9 @@ static void printObjC(const dyld3::MachOAnalyzer* ma)
                type, categoryVMAddr, categoryName);
 
         // Now print the methods on this category
-        ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, contentRebased,
+        ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, vmAddrConverter,
                               printMethod);
-        ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, contentRebased,
+        ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, vmAddrConverter,
                               printMethod);
     };
     auto printProtocol = ^(Diagnostics& diag, uint64_t protocolVMAddr,
@@ -1159,18 +940,18 @@ static void printObjC(const dyld3::MachOAnalyzer* ma)
                type, protocolVMAddr, protocolName);
 
         // Now print the methods on this protocol
-        ma->forEachObjCMethod(objCProtocol.instanceMethodsVMAddr, contentRebased,
+        ma->forEachObjCMethod(objCProtocol.instanceMethodsVMAddr, vmAddrConverter,
                               printMethod);
-        ma->forEachObjCMethod(objCProtocol.classMethodsVMAddr, contentRebased,
+        ma->forEachObjCMethod(objCProtocol.classMethodsVMAddr, vmAddrConverter,
                               printMethod);
-        ma->forEachObjCMethod(objCProtocol.optionalInstanceMethodsVMAddr, contentRebased,
+        ma->forEachObjCMethod(objCProtocol.optionalInstanceMethodsVMAddr, vmAddrConverter,
                               printMethod);
-        ma->forEachObjCMethod(objCProtocol.optionalClassMethodsVMAddr, contentRebased,
+        ma->forEachObjCMethod(objCProtocol.optionalClassMethodsVMAddr, vmAddrConverter,
                               printMethod);
     };
-    ma->forEachObjCClass(diag, contentRebased, printClass);
-    ma->forEachObjCCategory(diag, contentRebased, printCategory);
-    ma->forEachObjCProtocol(diag, contentRebased, printProtocol);
+    ma->forEachObjCClass(diag, vmAddrConverter, printClass);
+    ma->forEachObjCCategory(diag, vmAddrConverter, printCategory);
+    ma->forEachObjCProtocol(diag, vmAddrConverter, printProtocol);
 }
 
 static void usage()
@@ -1225,10 +1006,14 @@ int main(int argc, const char* argv[])
         return 0;
     }
 
+    size_t cacheLen;
+    const DyldSharedCache* cache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLen);
     for (const char* path : files) {
         Diagnostics diag;
+        bool fromSharedCache = false;
         dyld3::closure::FileSystemPhysical fileSystem;
         dyld3::closure::LoadedFileInfo info;
+        __block std::vector<const char*> archesForFile;
         char realerPath[MAXPATHLEN];
         __block bool printedError = false;
         if (!fileSystem.loadFile(path, info, realerPath, ^(const char* format, ...) {
@@ -1239,11 +1024,27 @@ int main(int argc, const char* argv[])
             va_end(list);
             printedError = true;
         })) {
-            if (!printedError )
+            if ( printedError )
+                return 1;
+            // see if path is in current dyld shared cache
+            info.fileContent = nullptr;
+            if ( cache != nullptr ) {
+                uint32_t imageIndex;
+                if ( cache->hasImagePath(path, imageIndex) ) {
+                    uint64_t mTime;
+                    uint64_t inode;
+                    const mach_header* mh = cache->getIndexedImageEntry(imageIndex, mTime, inode);
+                    info.fileContent = mh;
+                    info.path = path;
+                    fromSharedCache = true;
+                    archesForFile.push_back(cache->archName());
+                }
+            }
+            if ( !fromSharedCache ) {
                 fprintf(stderr, "dyldinfo: %s: file not found\n", path);
-            return 1;
+                return 1;
+            }
         }
-        __block std::vector<const char*> archesForFile;
         __block dyld3::Platform platform = dyld3::Platform::unknown;
         if ( dyld3::FatFile::isFatFile(info.fileContent) ) {
             const dyld3::FatFile* ff = (dyld3::FatFile*)info.fileContent;
@@ -1259,7 +1060,7 @@ int main(int argc, const char* argv[])
                 }
              });
         }
-        else {
+        else if ( !fromSharedCache ) {
             const dyld3::MachOFile* mo = (dyld3::MachOFile*)info.fileContent;
             if ( mo->isMachO(diag, info.sliceLen) ) {
                 archesForFile.push_back(mo->archName());
@@ -1279,7 +1080,8 @@ int main(int argc, const char* argv[])
         }
         char loadedPath[MAXPATHLEN];
         for (const char* sliceArch : archesForFile) {
-            info = dyld3::MachOAnalyzer::load(diag, fileSystem, path, dyld3::GradedArchs::forName(sliceArch), platform, loadedPath);
+            if ( !fromSharedCache )
+                info = dyld3::MachOAnalyzer::load(diag, fileSystem, path, dyld3::GradedArchs::forName(sliceArch), platform, loadedPath);
             if ( diag.hasError() ) {
                 fprintf(stderr, "dyldinfo: %s\n", diag.errorMessage());
                 return 1;
@@ -1306,10 +1108,14 @@ int main(int argc, const char* argv[])
                 }
                 else if ( strcmp(arg, "-dependents") == 0 ) {
                     printDependents(ma);
-                     somethingPrinted = true;
+                    somethingPrinted = true;
+                }
+                else if ( strcmp(arg, "-inits") == 0 ) {
+                    printInitializers(ma, cache, cacheLen);
+                    somethingPrinted = true;
                 }
                 else if ( strcmp(arg, "-fixups") == 0 ) {
-                    printFixups(ma);
+                    printFixups(ma, path);
                     somethingPrinted = true;
                 }
                 else if ( strcmp(arg, "-exports") == 0 ) {
@@ -1325,7 +1131,7 @@ int main(int argc, const char* argv[])
                     somethingPrinted = true;
                 }
                 else if ( strcmp(arg, "-symbolic_fixups") == 0 ) {
-                     printSymbolicFixups(ma);
+                     printSymbolicFixups(ma, path);
                      somethingPrinted = true;
                 }
                 else if ( strcmp(arg, "-opcodes") == 0 ) {
@@ -1337,7 +1143,7 @@ int main(int argc, const char* argv[])
                 else if ( strcmp(arg, "-data_in_code") == 0 ) {
                 }
                 else if ( strcmp(arg, "-objc") == 0 ) {
-                    printObjC(ma);
+                    printObjC(ma, cache, cacheLen);
                     somethingPrinted = true;
                 }
                 else {
@@ -1349,9 +1155,10 @@ int main(int argc, const char* argv[])
                 printPlatforms(ma);
                 printSegments(ma);
                 printDependents(ma);
-                printFixups(ma);
+                printInitializers(ma, cache, cacheLen);
+                printFixups(ma, path);
                 printExports(ma);
-                printObjC(ma);
+                printObjC(ma, cache, cacheLen);
             }
 
         }
diff --git a/dyld3/shared-cache/kernel_collection_builder.cpp b/dyld3/shared-cache/kernel_collection_builder.cpp
new file mode 100644 (file)
index 0000000..ab76b2b
--- /dev/null
@@ -0,0 +1,643 @@
+/* -*- 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 "kernel_collection_builder.h"
+
+#include "AppCacheBuilder.h"
+#include "Diagnostics.h"
+#include "ClosureFileSystemNull.h"
+#include "MachOAppCache.h"
+
+#include <span>
+#include <string>
+#include <vector>
+
+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
+
+static const uint32_t MajorVersion = 1;
+static const uint32_t MinorVersion = 0;
+
+struct KernelCollectionBuilder {
+
+    KernelCollectionBuilder(const BuildOptions_v1* options);
+
+    struct CollectionFile {
+        const uint8_t*  data = nullptr;
+        uint64_t        size = 0;
+        const char*     path = nullptr;
+    };
+
+    struct CacheBuffer {
+        uint8_t*    buffer      = nullptr;
+        uint64_t    bufferSize  = 0;
+    };
+
+    const char*                                 arch = "";
+    BuildOptions_v1                             options;
+    std::list<Diagnostics>                      inputFileDiags;
+    std::vector<AppCacheBuilder::InputDylib>    inputFiles;
+    dyld3::closure::LoadedFileInfo              kernelCollectionFileInfo;
+    dyld3::closure::LoadedFileInfo              pageableCollectionFileInfo;
+    std::vector<AppCacheBuilder::CustomSegment> customSections;
+    CFDictionaryRef                             prelinkInfoExtraData = nullptr;
+
+    std::list<CollectionFileResult_v1>          fileResultsStorage;
+    std::vector<const CollectionFileResult_v1*> fileResults;
+    CFDictionaryRef                             errorsDict           = nullptr;
+
+    std::vector<const char*>    errors;
+    std::vector<std::string>    errorStorage;
+
+    std::list<std::string>      duplicatedStrings;
+    std::vector<CFTypeRef>      typeRefs;
+
+    __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);
+
+        errorStorage.push_back(diag.errorMessage());
+        errors.push_back(errorStorage.back().data());
+    }
+
+    void retain(CFTypeRef v) {
+        CFRetain(v);
+        typeRefs.push_back(v);
+    }
+
+    const char* strdup(CFStringRef str) {
+        size_t length = CFStringGetLength(str);
+        char buffer[length + 1];
+        memset(&buffer[0], 0, length + 1);
+        if ( !CFStringGetCString(str, buffer, length + 1, kCFStringEncodingASCII) ) {
+            error("Could not convert to ASCII");
+            return nullptr;
+        }
+        duplicatedStrings.push_back(buffer);
+        return duplicatedStrings.back().c_str();
+    }
+
+};
+
+// What is the version of this builder dylib.  Can be used to determine what API is available.
+void getVersion(uint32_t *major, uint32_t *minor) {
+    *major = MajorVersion;
+    *minor = MinorVersion;
+}
+
+KernelCollectionBuilder::KernelCollectionBuilder(const BuildOptions_v1* options)
+    : options(*options) {
+    retain(this->options.arch);
+}
+
+// Returns a valid object on success, or NULL on failure.
+__API_AVAILABLE(macos(10.14))
+struct KernelCollectionBuilder* createKernelCollectionBuilder(const struct BuildOptions_v1* options) {
+    KernelCollectionBuilder* builder = new KernelCollectionBuilder(options);
+
+    if (options->version < kMinBuildVersion) {
+        builder->error("Builder version %llu is less than minimum supported version of %llu", options->version, kMinBuildVersion);
+        return builder;
+    }
+    if (options->version > kMaxBuildVersion) {
+        builder->error("Builder version %llu is greater than maximum supported version of %llu", options->version, kMaxBuildVersion);
+        return builder;
+    }
+    if ( options->arch == nullptr ) {
+        builder->error("arch must not be null");
+        return builder;
+    }
+    const char* archName = builder->strdup(options->arch);
+    if ( archName == nullptr ) {
+        // Already generated an error in strdup.
+        return builder;
+    }
+    builder->arch = archName;
+
+    return builder;
+}
+
+static bool loadFileFromData(struct KernelCollectionBuilder* builder,
+                             const CFStringRef path, const CFDataRef data,
+                             dyld3::closure::LoadedFileInfo& fileInfo) {
+    fileInfo.fileContent        = CFDataGetBytePtr(data);
+    fileInfo.fileContentLen     = CFDataGetLength(data);
+    fileInfo.sliceOffset        = 0;
+    fileInfo.sliceLen           = CFDataGetLength(data);
+    fileInfo.isOSBinary         = false;
+    fileInfo.inode              = 0;
+    fileInfo.mtime              = 0;
+    fileInfo.unload             = nullptr;
+    fileInfo.path               = builder->strdup(path);
+
+    Diagnostics diag;
+    dyld3::closure::FileSystemNull fileSystem;
+    const dyld3::GradedArchs& arch = dyld3::GradedArchs::forName(builder->arch);
+    auto loaded = dyld3::MachOAnalyzer::loadFromBuffer(diag, fileSystem, fileInfo.path, arch,
+                                                       dyld3::Platform::unknown, fileInfo);
+    if ( !loaded ) {
+        builder->error("%s", diag.errorMessage().c_str());
+        return false;
+    }
+
+    return true;
+}
+
+// Add a kernel static executable file.  Returns true on success.
+__API_AVAILABLE(macos(10.14))
+bool addKernelFile(struct KernelCollectionBuilder* builder, const CFStringRef path, const CFDataRef data) {
+    builder->retain(path);
+    builder->retain(data);
+
+    dyld3::closure::LoadedFileInfo info;
+    bool loaded = loadFileFromData(builder, path, data, info);
+    if ( !loaded )
+        return false;
+
+    DyldSharedCache::MappedMachO mappedFile(info.path, (const dyld3::MachOAnalyzer *)info.fileContent,
+                                            info.fileContentLen, false, false,
+                                            info.sliceOffset, info.mtime, info.inode);
+
+    AppCacheBuilder::InputDylib input;
+    input.dylib.mappedFile      = mappedFile;
+    input.dylib.loadedFileInfo  = info;
+    input.dylib.inputFile       = nullptr;
+    input.dylibID               = "com.apple.kernel";
+    input.errors                = &builder->inputFileDiags.emplace_back();
+
+    builder->inputFiles.push_back(input);
+    return true;
+}
+
+// Add kext mach-o and plist files.  Returns true on success.
+__API_AVAILABLE(macos(10.14))
+bool addKextDataFile(struct KernelCollectionBuilder* builder, const KextFileData_v1* fileData) {
+    if (fileData->version != 1) {
+        builder->error("addKextDataFile version %llu is less than minimum supported version of %d", fileData->version, 1);
+        return false;
+    }
+
+    builder->retain(fileData->dependencies);
+    builder->retain(fileData->bundleID);
+    builder->retain(fileData->bundlePath);
+    builder->retain(fileData->plist);
+
+    dyld3::closure::LoadedFileInfo info;
+
+    // Code-less kext's don't have a mach-o to load
+    const char* kextpath = nullptr;
+    if ( fileData->kextdata != nullptr ) {
+        builder->retain(fileData->kextpath);
+        builder->retain(fileData->kextdata);
+        bool loaded = loadFileFromData(builder, fileData->kextpath, fileData->kextdata, info);
+        if ( !loaded )
+            return false;
+        kextpath = info.path;
+    } else {
+        kextpath = "codeless";
+    }
+
+    DyldSharedCache::MappedMachO mappedFile(kextpath, (const dyld3::MachOAnalyzer *)info.fileContent,
+                                            info.fileContentLen, false, false,
+                                            info.sliceOffset, info.mtime, info.inode);
+
+    uint64_t numDependencies = CFArrayGetCount(fileData->dependencies);
+
+    AppCacheBuilder::InputDylib input;
+    input.dylib.mappedFile      = mappedFile;
+    input.dylib.loadedFileInfo  = info;
+    input.dylib.inputFile       = nullptr;
+    input.dylibID               = builder->strdup(fileData->bundleID);
+    for (uint64_t i = 0; i != numDependencies; ++i) {
+        CFTypeRef elementRef = CFArrayGetValueAtIndex(fileData->dependencies, i);
+        if ( CFGetTypeID(elementRef) != CFStringGetTypeID() ) {
+            builder->error("Dependency %llu of %s is not a string", i, info.path);
+            return false;
+        }
+        CFStringRef stringRef = (CFStringRef)elementRef;
+        input.dylibDeps.push_back(builder->strdup(stringRef));
+    }
+    input.infoPlist             = fileData->plist;
+    input.errors                = &builder->inputFileDiags.emplace_back();
+    input.bundlePath            = builder->strdup(fileData->bundlePath);
+    switch ( fileData->stripMode ) {
+        case binaryUnknownStripMode:
+            input.stripMode     = CacheBuilder::DylibStripMode::stripNone;
+            break;
+        case binaryStripNone:
+            input.stripMode     = CacheBuilder::DylibStripMode::stripNone;
+            break;
+        case binaryStripExports:
+            input.stripMode     = CacheBuilder::DylibStripMode::stripExports;
+            break;
+        case binaryStripLocals:
+            input.stripMode     = CacheBuilder::DylibStripMode::stripLocals;
+            break;
+        case binaryStripAll:
+            input.stripMode     = CacheBuilder::DylibStripMode::stripAll;
+            break;
+    }
+
+    builder->inputFiles.push_back(input);
+    return true;
+}
+
+// Add interface plist file.  Returns true on success.
+__API_AVAILABLE(macos(10.14))
+bool addInterfaceFile(struct KernelCollectionBuilder* builder, const CFStringRef path, const CFDataRef data) {
+    builder->retain(path);
+    builder->retain(data);
+
+    assert(0);
+}
+
+// Add collection file.  Returns true on success.
+__API_AVAILABLE(macos(10.14))
+bool addCollectionFile(struct KernelCollectionBuilder* builder, const CFStringRef path, const CFDataRef data,
+                       CollectionKind kind) {
+    dyld3::closure::LoadedFileInfo* fileInfo = nullptr;
+    if ( kind == baseKC ) {
+        if ( (builder->options.collectionKind != auxKC) && (builder->options.collectionKind != pageableKC) ) {
+            builder->error("Invalid collection file for build");
+            return false;
+        }
+        fileInfo = &builder->kernelCollectionFileInfo;
+    } else if ( kind == pageableKC ) {
+        if ( builder->options.collectionKind != auxKC ) {
+            builder->error("Invalid collection file for build");
+            return false;
+        }
+        if ( builder->pageableCollectionFileInfo.fileContent != nullptr ) {
+            builder->error("Already have collection file");
+            return false;
+        }
+        fileInfo = &builder->pageableCollectionFileInfo;
+    } else {
+        builder->error("Unsupported collection kind");
+        return false;
+    }
+    if ( fileInfo->fileContent != nullptr ) {
+        builder->error("Already have collection file");
+        return false;
+    }
+
+    builder->retain(path);
+    builder->retain(data);
+
+    bool loaded = loadFileFromData(builder, path, data, *fileInfo);
+    if ( !loaded )
+        return false;
+
+    const dyld3::MachOFile* mf = (const dyld3::MachOFile*)fileInfo->fileContent;
+    if ( !mf->isFileSet() ) {
+        builder->error("kernel collection is not a cache file: %s\n", builder->strdup(path));
+        return false;
+    }
+
+    return true;
+}
+
+// Add data to the given segment of the final file.  Note the section can be null (or "") if desired
+__API_AVAILABLE(macos(10.14))
+bool addSegmentData(struct KernelCollectionBuilder* builder, const CFStringRef segmentName,
+                    const CFStringRef sectionName, const CFDataRef data) {
+    // Check the segment name
+    if ( segmentName == nullptr ) {
+        builder->error("Segment data name must be non-null");
+        return false;
+    }
+    CFIndex segmentNameLength = CFStringGetLength(segmentName);
+    if ( (segmentNameLength == 0) || (segmentNameLength > 16) ) {
+        builder->error("Segment data name must not be empty or > 16 characters");
+        return false;
+    }
+
+    AppCacheBuilder::CustomSegment::CustomSection section;
+
+    // Check the section name
+    if ( sectionName != nullptr ) {
+        CFIndex sectionNameLength = CFStringGetLength(sectionName);
+        if ( sectionNameLength != 0 ) {
+            if ( sectionNameLength > 16 ) {
+                builder->error("Section data name must not be empty or > 16 characters");
+                return false;
+            }
+            section.sectionName = builder->strdup(sectionName);
+        }
+    }
+
+    // Check the data
+    if ( data == nullptr ) {
+        builder->error("Segment data payload must be non-null");
+        return false;
+    }
+    const uint8_t* dataStart = CFDataGetBytePtr(data);
+    const uint64_t dataLength = CFDataGetLength(data);
+    if ( dataLength == 0 ) {
+        builder->error("Segment data payload must not be empty");
+        return false;
+    }
+
+    builder->retain(data);
+    section.data.insert(section.data.end(), dataStart, dataStart + dataLength);
+
+    AppCacheBuilder::CustomSegment segment;
+    segment.segmentName = builder->strdup(segmentName);
+    segment.sections.push_back(section);
+    builder->customSections.push_back(segment);
+    return true;
+}
+
+// Add data to the given segment of the final file.  Note the section can be null (or "") if desired
+__API_AVAILABLE(macos(10.14))
+bool addPrelinkInfo(struct KernelCollectionBuilder* builder, const CFDictionaryRef extraData) {
+    if ( builder->prelinkInfoExtraData != nullptr ) {
+        builder->error("Prelink info data has already been set by an earlier call");
+        return false;
+    }
+    // Check the data
+    if ( extraData == nullptr ) {
+        builder->error("Prelink info data payload must be non-null");
+        return false;
+    }
+    if ( CFDictionaryGetCount(extraData) == 0 ) {
+        builder->error("Prelink info data payload must not be empty");
+        return false;
+    }
+    builder->retain(extraData);
+    builder->prelinkInfoExtraData = extraData;
+    return true;
+}
+
+// Set a handler to be called at various points during the build to notify the user of progress.
+__API_AVAILABLE(macos(10.14))
+void setProgressCallback(const ProgressCallback callback) {
+    assert(0);
+}
+
+static AppCacheBuilder::Options::AppCacheKind cacheKind(CollectionKind kind) {
+    switch (kind) {
+        case unknownKC:
+            return AppCacheBuilder::Options::AppCacheKind::none;
+        case baseKC:
+            return AppCacheBuilder::Options::AppCacheKind::kernel;
+        case auxKC:
+            return AppCacheBuilder::Options::AppCacheKind::auxKC;
+        case pageableKC:
+            return AppCacheBuilder::Options::AppCacheKind::pageableKC;
+    }
+}
+
+static AppCacheBuilder::Options::StripMode stripMode(StripMode mode) {
+    switch (mode) {
+        case unknownStripMode:
+            return AppCacheBuilder::Options::StripMode::none;
+        case stripNone:
+            return AppCacheBuilder::Options::StripMode::none;
+        case stripAll:
+            return AppCacheBuilder::Options::StripMode::all;
+        case stripAllKexts:
+            return AppCacheBuilder::Options::StripMode::allExceptKernel;
+    }
+}
+
+static void generatePerKextErrors(struct KernelCollectionBuilder* builder) {
+    CFMutableDictionaryRef errorsDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
+    builder->typeRefs.push_back(errorsDict);
+
+    for (const AppCacheBuilder::InputDylib& file : builder->inputFiles) {
+        if ( !file.errors->hasError() )
+            continue;
+
+        CFStringRef bundleID = CFStringCreateWithCString(kCFAllocatorDefault, file.dylibID.c_str(),
+                                                            kCFStringEncodingASCII);
+        builder->typeRefs.push_back(bundleID);
+
+        CFMutableArrayRef errorsArray = CFArrayCreateMutable(kCFAllocatorDefault, 1, nullptr);
+        builder->typeRefs.push_back(errorsArray);
+
+        CFStringRef errorString = CFStringCreateWithCString(kCFAllocatorDefault, file.errors->errorMessage().c_str(),
+                                                            kCFStringEncodingASCII);
+        builder->typeRefs.push_back(errorString);
+
+        CFArrayAppendValue(errorsArray, errorString);
+
+        // Add this bundle to the dictionary
+        CFDictionarySetValue(errorsDict, bundleID, errorsArray);
+
+        // FIXME: Remove this once the kernel linker has adopted the new API to get the per-kext errors
+        // For now also put the per-kext errors on the main error list
+        builder->error("Could not use '%s' because: %s", file.dylibID.c_str(), file.errors->errorMessage().c_str());
+    }
+
+    if ( CFDictionaryGetCount(errorsDict) != 0 ) {
+        builder->errorsDict = errorsDict;
+    }
+}
+
+// Returns true on success.
+__API_AVAILABLE(macos(10.14))
+bool runKernelCollectionBuilder(struct KernelCollectionBuilder* builder) {
+
+    // Make sure specificed bundle-id's are not already in the caches we
+    // are linking to
+    __block std::set<std::string_view> existingBundles;
+    if ( (builder->options.collectionKind == auxKC) || (builder->options.collectionKind == pageableKC) ) {
+        if ( builder->kernelCollectionFileInfo.fileContent == nullptr ) {
+            builder->error("Cannot build pageableKC/auxKC without baseKC");
+            return false;
+        }
+        Diagnostics diag;
+
+        // Check the base KC
+        const dyld3::MachOAppCache* kernelCacheMA = (const dyld3::MachOAppCache*)builder->kernelCollectionFileInfo.fileContent;
+        kernelCacheMA->forEachDylib(diag, ^(const dyld3::MachOAnalyzer *ma, const char *name, bool &stop) {
+            existingBundles.insert(name);
+        });
+
+        // Check the pageableKC if we have one
+        const dyld3::MachOAppCache* pageableCacheMA = (const dyld3::MachOAppCache*)builder->kernelCollectionFileInfo.fileContent;
+        if ( pageableCacheMA != nullptr ) {
+            pageableCacheMA->forEachDylib(diag, ^(const dyld3::MachOAnalyzer *ma, const char *name, bool &stop) {
+                existingBundles.insert(name);
+            });
+        }
+
+        bool foundBadBundle = false;
+        for (const AppCacheBuilder::InputDylib& input : builder->inputFiles) {
+            if ( existingBundles.find(input.dylibID) != existingBundles.end() ) {
+                builder->error("kernel collection already contains bundle-id: %s\n", input.dylibID.c_str());
+                foundBadBundle = true;
+            }
+        }
+        if ( foundBadBundle )
+            return false;
+    }
+
+    dispatch_apply(builder->inputFiles.size(), DISPATCH_APPLY_AUTO, ^(size_t index) {
+        const AppCacheBuilder::InputDylib& input = builder->inputFiles[index];
+        auto errorHandler = ^(const char* msg) {
+            input.errors->error("cannot be placed in kernel collection because: %s", msg);
+        };
+        const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)input.dylib.loadedFileInfo.fileContent;
+        // Skip codeless kexts
+        if ( ma == nullptr )
+            return;
+        if (!ma->canBePlacedInKernelCollection(input.dylib.loadedFileInfo.path, errorHandler)) {
+            assert(input.errors->hasError());
+        }
+    });
+    for (const AppCacheBuilder::InputDylib& input : builder->inputFiles) {
+        if ( input.errors->hasError() ) {
+            builder->error("One or more binaries has an error which prevented linking.  See other errors.");
+            generatePerKextErrors(builder);
+            return false;
+        }
+    }
+
+    DyldSharedCache::CreateOptions builderOptions = {};
+    std::string runtimePath = "";
+    builderOptions.outputFilePath = runtimePath;
+    builderOptions.outputMapFilePath = builderOptions.outputFilePath + ".json";
+    builderOptions.archs = &dyld3::GradedArchs::forName(builder->arch);
+    builderOptions.platform = dyld3::Platform::unknown;
+    builderOptions.localSymbolMode = DyldSharedCache::LocalSymbolsMode::keep;
+    builderOptions.optimizeStubs = true;
+    builderOptions.optimizeDyldDlopens = false;
+    builderOptions.optimizeDyldLaunches = false;
+    builderOptions.codeSigningDigestMode = DyldSharedCache::CodeSigningDigestMode::SHA256only;
+    builderOptions.dylibsRemovedDuringMastering = true;
+    builderOptions.inodesAreSameAsRuntime = false;
+    builderOptions.cacheSupportsASLR = true;
+    builderOptions.forSimulator = false;
+    builderOptions.isLocallyBuiltCache = true;
+    builderOptions.verbose = builder->options.verboseDiagnostics;
+    builderOptions.evictLeafDylibsOnOverflow = true;
+    builderOptions.loggingPrefix = "";
+    builderOptions.dylibOrdering = {};
+    builderOptions.dirtyDataSegmentOrdering = {};
+    builderOptions.objcOptimizations = {};
+
+    AppCacheBuilder::Options appCacheOptions;
+    appCacheOptions.cacheKind = cacheKind(builder->options.collectionKind);
+    appCacheOptions.stripMode = stripMode(builder->options.stripMode);
+
+    const dyld3::closure::FileSystemNull builderFileSystem;
+
+    AppCacheBuilder cacheBuilder(builderOptions, appCacheOptions, builderFileSystem);
+    if ( builder->kernelCollectionFileInfo.fileContent != nullptr ) {
+        const dyld3::MachOAppCache* appCacheMA = (const dyld3::MachOAppCache*)builder->kernelCollectionFileInfo.fileContent;
+        cacheBuilder.setExistingKernelCollection(appCacheMA);
+    }
+    if ( builder->pageableCollectionFileInfo.fileContent != nullptr ) {
+        const dyld3::MachOAppCache* appCacheMA = (const dyld3::MachOAppCache*)builder->pageableCollectionFileInfo.fileContent;
+        cacheBuilder.setExistingPageableKernelCollection(appCacheMA);
+    }
+
+    // Add custom sections
+    for (const AppCacheBuilder::CustomSegment& segment : builder->customSections) {
+        if ( !cacheBuilder.addCustomSection(segment.segmentName, segment.sections.front()) ) {
+            builder->error("%s", cacheBuilder.errorMessage().c_str());
+            return false;
+        }
+    }
+
+    // Add prelink info data
+    if ( builder->prelinkInfoExtraData != nullptr ) {
+        cacheBuilder.setExtraPrelinkInfo(builder->prelinkInfoExtraData);
+    }
+
+    cacheBuilder.buildAppCache(builder->inputFiles);
+    if ( !cacheBuilder.errorMessage().empty() ) {
+        builder->error("%s", cacheBuilder.errorMessage().c_str());
+        generatePerKextErrors(builder);
+        return false;
+    }
+
+    uint8_t* cacheBuffer    = nullptr;
+    uint64_t cacheSize      = 0;
+    cacheBuilder.writeBuffer(cacheBuffer, cacheSize);
+
+    CFDataRef dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, cacheBuffer, cacheSize, kCFAllocatorDefault);
+    builder->retain(dataRef);
+    CFRelease(dataRef);
+
+    CFArrayRef warningsArrayRef = CFArrayCreate(kCFAllocatorDefault, nullptr, 0, nullptr);
+    builder->retain(warningsArrayRef);
+    CFRelease(warningsArrayRef);
+
+    CollectionFileResult_v1 fileResult = { 1, kernelCollection, dataRef, warningsArrayRef };
+
+    builder->fileResultsStorage.push_back(fileResult);
+    builder->fileResults.push_back(&builder->fileResultsStorage.back());
+
+    return true;
+}
+
+// Gets the list of errors we have produced.  These may be from incorrect input values, or failures in the build itself
+__API_AVAILABLE(macos(10.14))
+const char* const* getErrors(const struct KernelCollectionBuilder* builder, uint64_t* errorCount) {
+    if (builder->errors.empty())
+        return nullptr;
+    *errorCount = builder->errors.size();
+    return builder->errors.data();
+}
+
+__API_AVAILABLE(macos(10.14))
+CFDictionaryRef getKextErrors(const struct KernelCollectionBuilder* builder) {
+    return builder->errorsDict;
+}
+
+// Returns an array of the resulting files.  These may be new collections, or other files required later
+__API_AVAILABLE(macos(10.14))
+const struct CollectionFileResult_v1* const* getCollectionFileResults(struct KernelCollectionBuilder* builder, uint64_t* resultCount) {
+    if ( builder->fileResults.empty() ) {
+        *resultCount = 0;
+        return nullptr;
+    }
+
+    *resultCount = builder->fileResults.size();
+    return builder->fileResults.data();
+}
+
+__API_AVAILABLE(macos(10.14))
+void destroyKernelCollectionBuilder(struct KernelCollectionBuilder* builder) {
+    for (CFTypeRef ref : builder->typeRefs)
+        CFRelease(ref);
+    dyld3::closure::FileSystemNull fileSystem;
+    for (const AppCacheBuilder::InputDylib& inputFile : builder->inputFiles) {
+        fileSystem.unloadFile(inputFile.dylib.loadedFileInfo);
+    }
+    if ( builder->kernelCollectionFileInfo.fileContent != nullptr) {
+        fileSystem.unloadFile(builder->kernelCollectionFileInfo);
+    }
+    if ( builder->pageableCollectionFileInfo.fileContent != nullptr) {
+        fileSystem.unloadFile(builder->pageableCollectionFileInfo);
+    }
+    delete builder;
+}
diff --git a/dyld3/shared-cache/kernel_collection_builder.h b/dyld3/shared-cache/kernel_collection_builder.h
new file mode 100644 (file)
index 0000000..1139e60
--- /dev/null
@@ -0,0 +1,179 @@
+/* -*- 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 kernel_collection_builder_h
+#define kernel_collection_builder_h
+
+#include <Availability.h>
+
+#include <CoreFoundation/CFArray.h>
+#include <CoreFoundation/CFData.h>
+#include <CoreFoundation/CFDictionary.h>
+#include <CoreFoundation/CFString.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef CF_ENUM(uint32_t, CollectionKind) {
+    unknownKC   = 0,
+    baseKC      = 1,
+    auxKC       = 2,
+    pageableKC  = 3,
+};
+
+typedef CF_ENUM(uint32_t, FileResultKind) {
+    unknownFileResult   = 0,
+    kernelCollection    = 1,
+};
+
+typedef CF_ENUM(uint32_t, StripMode) {
+    unknownStripMode     = 0,
+    stripNone            = 1, // Don't strip any symbols
+    stripAll             = 2, // Strip all symbols from all binaries
+    stripAllKexts        = 3, // Don't strip xnu, but strip everything else
+};
+
+typedef CF_ENUM(uint32_t, BinaryStripMode) {
+    binaryUnknownStripMode     = 0,
+    binaryStripNone            = 1, // Don't strip any symbols
+    binaryStripExports         = 2, // Strip all the exports, but leave the local symbols
+    binaryStripLocals          = 3, // Strip all the locals, but leave the exported symbols
+    binaryStripAll             = 4, // Strip all symbols
+};
+
+typedef CF_ENUM(uint32_t, ProgressKind) {
+    unknownProgress     = 0,
+    layout              = 1,
+    generateHeader      = 2,
+    copyInputs          = 3,
+    applySplitSeg       = 4,
+    generatePrelinkInfo = 5,
+    processFixups       = 6,
+    optimizeStubs       = 7,
+    optimizeLinkedit    = 8,
+    writeFixups         = 9,
+    emitFile            = 10
+};
+
+struct BuildOptions_v1
+{
+    uint64_t                                    version;                        // Future proofing, set to 1
+    CollectionKind                              collectionKind;
+    StripMode                                   stripMode;
+// Valid archs are one of: "arm64", "arm64e", "x86_64", "x86_64h"
+    const CFStringRef                           arch;
+    bool                                        verboseDiagnostics;
+};
+
+
+struct CollectionFileResult_v1
+{
+    uint64_t                                    version;            // Future proofing, set to 1
+    FileResultKind                              fileKind;
+    const CFDataRef                             data;               // Owned by the cache builder.  Destroyed by destroyKernelCollectionBuilder
+    const CFArrayRef                            warnings;           // should this be per-result?
+};
+
+struct KextFileData_v1
+{
+    uint64_t                                    version;            // Future proofing, set to 1
+    const CFStringRef                           kextpath;
+    const CFDataRef                             kextdata;
+    const CFArrayRef                            dependencies;
+    const CFStringRef                           bundleID;
+    const CFStringRef                           bundlePath;
+    CFDictionaryRef                             plist;
+    BinaryStripMode                             stripMode;
+};
+
+typedef void (*ProgressCallback)(const char* message, ProgressKind kind);
+
+struct KernelCollectionBuilder;
+
+// What is the version of this builder dylib.  Can be used to determine what API is available.
+__API_AVAILABLE(macos(10.14))
+void getVersion(uint32_t *major, uint32_t *minor);
+
+// Returns a valid object on success, or NULL on failure.
+__API_AVAILABLE(macos(10.14))
+struct KernelCollectionBuilder* createKernelCollectionBuilder(const struct BuildOptions_v1* options);
+
+// Add a kernel static executable file.  Returns true on success.
+__API_AVAILABLE(macos(10.14))
+bool addKernelFile(struct KernelCollectionBuilder* builder, const CFStringRef path, const CFDataRef data);
+
+// Add kext mach-o and plist files.  Returns true on success.
+__API_AVAILABLE(macos(10.14))
+bool addKextDataFile(struct KernelCollectionBuilder* builder, const struct KextFileData_v1* fileData);
+
+// Add interface plist file.  Returns true on success.
+__API_AVAILABLE(macos(10.14))
+bool addInterfaceFile(struct KernelCollectionBuilder* builder, const CFStringRef path, const CFDataRef data);
+
+// Add collection file.  Returns true on success.
+__API_AVAILABLE(macos(10.14))
+bool addCollectionFile(struct KernelCollectionBuilder* builder, const CFStringRef path, const CFDataRef data, CollectionKind kind);
+
+// Add data to the given segment of the final file.  Note the section can be null (or "") if desired
+__API_AVAILABLE(macos(10.14))
+bool addSegmentData(struct KernelCollectionBuilder* builder, const CFStringRef segmentName, const CFStringRef sectionName, const CFDataRef data);
+
+// Add entries to the prelink info dictionary.  Note this can only be used once at this time.
+__API_AVAILABLE(macos(10.14))
+bool addPrelinkInfo(struct KernelCollectionBuilder* builder, const CFDictionaryRef extraData);
+
+// Set a handler to be called at various points during the build to notify the user of progress.
+__API_AVAILABLE(macos(10.14))
+void setProgressCallback(const ProgressCallback callback);
+
+// Returns true on success.
+__API_AVAILABLE(macos(10.14))
+bool runKernelCollectionBuilder(struct KernelCollectionBuilder* builder);
+
+// Gets the list of errors we have produced.  These may be from incorrect input values, or failures in the build itself
+__API_AVAILABLE(macos(10.14))
+const char* const* getErrors(const struct KernelCollectionBuilder* builder, uint64_t* errorCount);
+
+// Gets the errors on each kext.  This returns null if there are no such errors.
+// A non-null result is a dictionary where the keys are bundle-ids and the values are
+// arrays of strings represeting error messages.
+__API_AVAILABLE(macos(10.14))
+CFDictionaryRef getKextErrors(const struct KernelCollectionBuilder* builder);
+
+// Returns an array of the resulting files.  These may be new collections, or other files required later
+__API_AVAILABLE(macos(10.14))
+const struct CollectionFileResult_v1* const* getCollectionFileResults(struct KernelCollectionBuilder* builder, uint64_t* resultCount);
+
+__API_AVAILABLE(macos(10.14))
+void destroyKernelCollectionBuilder(struct KernelCollectionBuilder* builder);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* kernel_collection_builder_h */
index c01339324e1372dae2edfaa149d212bb094b7c1a..5e58cbf58eb0677e01009fcd4dd74c08135047aa 100644 (file)
 #include "SharedCacheBuilder.h"
 #include "ClosureFileSystem.h"
 #include "FileUtils.h"
+#include "JSONReader.h"
 #include <pthread.h>
 #include <memory>
 #include <vector>
 #include <map>
 #include <sys/stat.h>
 
+#if __has_include(<Cambria/SharedCache.h>)
+#include <Cambria/SharedCache.h>
+asm(".linker_option \"-lcambria_sharedcache\"");
+asm(".linker_option \"-lcodedirectory_static\"");
+#endif
+
 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
+static const uint64_t kMaxBuildVersion = 2; //The maximum version BuildOptions struct we can support
 
 static const uint32_t MajorVersion = 1;
-static const uint32_t MinorVersion = 0;
+static const uint32_t MinorVersion = 2;
 
 namespace dyld3 {
 namespace closure {
@@ -97,7 +104,7 @@ public:
         info.fileContentLen             = fileInfo.length;
         info.sliceOffset                = 0;
         info.sliceLen                   = fileInfo.length;
-        info.isSipProtected             = false;
+        info.isOSBinary                 = true;
         info.inode                      = fileInfo.inode;
         info.mtime                      = fileInfo.mtime;
         info.unload                     = nullptr;
@@ -203,6 +210,8 @@ struct BuildInstance {
     uint8_t*                                        cacheData       = nullptr;
     uint64_t                                        cacheSize       = 0;
     std::string                                     jsonMap;
+    std::string                                     macOSMap;       // For compatibility with update_dyld_shared_cache's .map file
+    std::string                                     macOSMapPath;   // Owns the string for the path
     std::string                                     cdHash;         // Owns the data for the cdHash
     std::string                                     cdHashType;     // Owns the data for the cdHashType
     std::string                                     uuid;           // Owns the data for the uuid
@@ -214,6 +223,14 @@ struct BuildFileResult {
     uint64_t                                    size;
 };
 
+struct TranslationResult {
+    const uint8_t*   data;
+    size_t           size;
+    std::string      cdHash;
+    std::string      path;
+    bool             bufferWasMalloced;
+};
+
 struct MRMSharedCacheBuilder {
     MRMSharedCacheBuilder(const BuildOptions_v1* options);
     const BuildOptions_v1*          options;
@@ -221,24 +238,32 @@ struct MRMSharedCacheBuilder {
 
     std::string dylibOrderFileData;
     std::string dirtyDataOrderFileData;
+    void* objcOptimizationsFileData;
+    size_t objcOptimizationsFileLength;
 
     // 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 paths in all of the caches
     // We keep this here to own the std::string path data
-    std::map<std::string, uint32_t> dylibsInCaches;
+    std::map<std::string, std::unordered_set<const BuildInstance*>> dylibsInCaches;
 
     // The results from all of the builders
     // We keep this in a vector to own the data.
-    std::vector<FileResult*>    fileResults;
-    std::vector<FileResult>     fileResultStorage;
+    std::vector<FileResult*>                     fileResults;
+    std::vector<FileResult>                      fileResultStorage;
+    std::vector<std::pair<uint64_t, bool>>       fileResultBuffers;
 
     // The results from all of the builders
     // We keep this in a vector to own the data.
     std::vector<CacheResult*>    cacheResults;
     std::vector<CacheResult>     cacheResultStorage;
 
+#if __has_include(<Cambria/SharedCache.h>)
+    // Storage for translated shared caches
+    std::vector<TranslationResult> translationResults;
+#endif
+
     // The files to remove.  These are in every copy of the caches we built
     std::vector<const char*> filesToRemove;
 
@@ -273,7 +298,12 @@ struct MRMSharedCacheBuilder {
     }
 };
 
-MRMSharedCacheBuilder::MRMSharedCacheBuilder(const BuildOptions_v1* options) : options(options), lock(PTHREAD_MUTEX_INITIALIZER) {
+MRMSharedCacheBuilder::MRMSharedCacheBuilder(const BuildOptions_v1* options)
+: options(options)
+, lock(PTHREAD_MUTEX_INITIALIZER)
+, objcOptimizationsFileData(nullptr)
+, objcOptimizationsFileLength(0)
+{
 
 }
 
@@ -294,6 +324,7 @@ void validiateBuildOptions(const BuildOptions_v1* options, MRMSharedCacheBuilder
         case Disposition::Unknown:
         case Disposition::InternalDevelopment:
         case Disposition::Customer:
+        case Disposition::InternalMinDevelopment:
             break;
         default:
             builder.error("unknown disposition value");
@@ -373,6 +404,11 @@ bool addFile(struct MRMSharedCacheBuilder* builder, const char* path, uint8_t* d
                 builder->dirtyDataOrderFileData = std::string((char*)data, size);
                 success = true;
                 return;
+            case ObjCOptimizationsFile:
+                builder->objcOptimizationsFileData = data;
+                builder->objcOptimizationsFileLength = size;
+                success = true;
+                return;
             default:
                 builder->error("unknown file flags value");
                 break;
@@ -415,22 +451,44 @@ bool addSymlink(struct MRMSharedCacheBuilder* builder, const char* fromPath, con
     return success;
 }
 
-static bool platformExcludeLocalSymbols(Platform platform) {
+static DyldSharedCache::LocalSymbolsMode platformExcludeLocalSymbols(Platform platform) {
     switch (platform) {
         case Platform::unknown:
         case Platform::macOS:
-            return false;
+            return DyldSharedCache::LocalSymbolsMode::keep;
         case Platform::iOS:
         case Platform::tvOS:
         case Platform::watchOS:
         case Platform::bridgeOS:
-            return true;
+            return DyldSharedCache::LocalSymbolsMode::unmap;
         case Platform::iOSMac:
         case Platform::iOS_simulator:
         case Platform::tvOS_simulator:
         case Platform::watchOS_simulator:
-            return false;
+            return DyldSharedCache::LocalSymbolsMode::keep;
+    }
+}
+
+static DyldSharedCache::LocalSymbolsMode excludeLocalSymbols(const BuildOptions_v1* options) {
+    if ( options->version >= 2 ) {
+        const BuildOptions_v2* v2 = (const BuildOptions_v2*)options;
+        if ( v2->optimizeForSize )
+            return DyldSharedCache::LocalSymbolsMode::strip;
     }
+
+    // Old build options always use the platform default
+    return platformExcludeLocalSymbols(options->platform);
+}
+
+static bool optimizeDyldDlopens(const BuildOptions_v1* options) {
+    // Old builds always default to dyld3 optimisations
+    if ( options->version < 2 ) {
+        return true;
+    }
+
+    // If we want to optimize for size instead of speed, then disable dyld3 dlopen closures
+    const BuildOptions_v2* v2 = (const BuildOptions_v2*)options;
+    return !v2->optimizeForSize;
 }
 
 static DyldSharedCache::CodeSigningDigestMode platformCodeSigningDigestMode(Platform platform) {
@@ -481,6 +539,12 @@ static const char* dispositionName(Disposition disposition) {
     }
 }
 
+// This is a JSON file containing the list of classes for which
+// we should try to build IMP caches.
+dyld3::json::Node parseObjcOptimizationsFile(Diagnostics& diags, const void* data, size_t length) {
+    return dyld3::json::readJSON(diags, data, length);
+}
+
 bool runSharedCacheBuilder(struct MRMSharedCacheBuilder* builder) {
     __block bool success = false;
     builder->runSync(^() {
@@ -493,7 +557,7 @@ bool runSharedCacheBuilder(struct MRMSharedCacheBuilder* builder) {
             builder->error("Cannot run builder with no files");
         }
 
-        Diagnostics diag;
+        __block 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());
@@ -504,42 +568,49 @@ bool runSharedCacheBuilder(struct MRMSharedCacheBuilder* builder) {
             return;
         }
 
-        __block std::vector<CacheBuilder::InputFile> inputFiles;
+        __block std::vector<SharedCacheBuilder::InputFile> inputFiles;
         builder->fileSystem.forEachFileInfo(^(const char* path, FileFlags fileFlags) {
-            CacheBuilder::InputFile::State state = CacheBuilder::InputFile::Unset;
+            SharedCacheBuilder::InputFile::State state = SharedCacheBuilder::InputFile::Unset;
             switch (fileFlags) {
                 case FileFlags::NoFlags:
-                    state = CacheBuilder::InputFile::Unset;
+                    state = SharedCacheBuilder::InputFile::Unset;
                     break;
                 case FileFlags::MustBeInCache:
-                    state = CacheBuilder::InputFile::MustBeIncluded;
+                    state = SharedCacheBuilder::InputFile::MustBeIncluded;
                     break;
                 case FileFlags::ShouldBeExcludedFromCacheIfUnusedLeaf:
-                    state = CacheBuilder::InputFile::MustBeExcludedIfUnused;
+                    state = SharedCacheBuilder::InputFile::MustBeExcludedIfUnused;
                     break;
                 case FileFlags::RequiredClosure:
-                    state = CacheBuilder::InputFile::MustBeIncluded;
+                    state = SharedCacheBuilder::InputFile::MustBeIncluded;
                     break;
                 case FileFlags::DylibOrderFile:
                 case FileFlags::DirtyDataOrderFile:
+                case FileFlags::ObjCOptimizationsFile:
                     builder->error("Order files should not be in the file system");
                     return;
             }
-            inputFiles.emplace_back((CacheBuilder::InputFile){ path, state });
+            inputFiles.emplace_back((SharedCacheBuilder::InputFile){ path, state });
         });
 
         auto addCacheConfiguration = ^(bool isOptimized) {
             for (uint64_t i = 0; i != builder->options->numArchs; ++i) {
+                // HACK: Skip i386 for macOS
+                if ( (builder->options->platform == Platform::macOS) && (strcmp(builder->options->archs[i], "i386") == 0 ) )
+                    continue;
                 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/";
+                if ( builder->options->platform == Platform::macOS )
+                    cacheSuffix = "";
+                std::string runtimePath = (builder->options->platform == Platform::macOS) ? MACOSX_MRM_DYLD_SHARED_CACHE_DIR : IPHONE_DYLD_SHARED_CACHE_DIR;
                 options->outputFilePath = runtimePath + "dyld_shared_cache_" + builder->options->archs[i] + cacheSuffix;
                 options->outputMapFilePath = options->outputFilePath + ".json";
                 options->archs = &dyld3::GradedArchs::forName(builder->options->archs[i]);
                 options->platform = (dyld3::Platform)builder->options->platform;
-                options->excludeLocalSymbols = platformExcludeLocalSymbols(builder->options->platform);
+                options->localSymbolMode = excludeLocalSymbols(builder->options);
                 options->optimizeStubs = isOptimized;
-                options->optimizeObjC = true;
+                options->optimizeDyldDlopens = optimizeDyldDlopens(builder->options);
+                options->optimizeDyldLaunches = true;
                 options->codeSigningDigestMode = platformCodeSigningDigestMode(builder->options->platform);
                 options->dylibsRemovedDuringMastering = true;
                 options->inodesAreSameAsRuntime = false;
@@ -551,6 +622,7 @@ bool runSharedCacheBuilder(struct MRMSharedCacheBuilder* builder) {
                 options->loggingPrefix = std::string(builder->options->deviceName) + dispositionName(builder->options->disposition) + "." + builder->options->archs[i] + cacheSuffix;
                 options->dylibOrdering = parseOrderFile(builder->dylibOrderFileData);
                 options->dirtyDataSegmentOrdering = parseOrderFile(builder->dirtyDataOrderFileData);
+                options->objcOptimizations = parseObjcOptimizationsFile(diag, builder->objcOptimizationsFileData, builder->objcOptimizationsFileLength);
 
                 auto cacheBuilder = std::make_unique<SharedCacheBuilder>(*options.get(), builder->fileSystem);
                 builder->builders.emplace_back((BuildInstance) { std::move(options), std::move(cacheBuilder), inputFiles });
@@ -561,8 +633,13 @@ bool runSharedCacheBuilder(struct MRMSharedCacheBuilder* builder) {
         switch (builder->options->disposition) {
             case Disposition::Unknown:
             case Disposition::InternalDevelopment:
-                addCacheConfiguration(false);
-                addCacheConfiguration(true);
+                // HACK: MRM for the mac should only get development, even if it requested both
+                if (builder->options->platform == Platform::macOS) {
+                    addCacheConfiguration(false);
+                } else {
+                    addCacheConfiguration(false);
+                    addCacheConfiguration(true);
+                }
                 break;
             case Disposition::Customer:
                 addCacheConfiguration(true);
@@ -599,7 +676,12 @@ bool runSharedCacheBuilder(struct MRMSharedCacheBuilder* builder) {
 
             if (cacheBuilder->errorMessage().empty()) {
                 cacheBuilder->writeBuffer(buildInstance.cacheData, buildInstance.cacheSize);
-                buildInstance.jsonMap = cacheBuilder->getMapFileBuffer(builder->options->deviceName);
+                buildInstance.jsonMap = cacheBuilder->getMapFileJSONBuffer(builder->options->deviceName);
+                if ( buildInstance.options->platform == dyld3::Platform::macOS ) {
+                    // For compatibility with update_dyld_shared_cache, put a .map file next to the shared cache
+                    buildInstance.macOSMap = cacheBuilder->getMapFileBuffer();
+                    buildInstance.macOSMapPath = buildInstance.options->outputFilePath + ".map";
+                }
                 buildInstance.cdHash = cacheBuilder->cdHashFirst();
                 buildInstance.uuid = cacheBuilder->uuid();
                 switch (buildInstance.options->codeSigningDigestMode) {
@@ -613,14 +695,110 @@ bool runSharedCacheBuilder(struct MRMSharedCacheBuilder* builder) {
                         buildInstance.cdHashType = "sha1";
                         break;
                 }
+
+                // Track the dylibs which were included in this cache
+                cacheBuilder->forEachCacheDylib(^(const std::string &path) {
+                    builder->dylibsInCaches[path.c_str()].insert(&buildInstance);
+                });
+                cacheBuilder->forEachCacheSymlink(^(const std::string &path) {
+                    builder->dylibsInCaches[path.c_str()].insert(&buildInstance);
+                });
             }
+            // Free the cache builder now so that we don't keep too much memory resident
+            cacheBuilder->deleteBuffer();
+            buildInstance.builder.reset();
         }
 
+#if __has_include(<Cambria/SharedCache.h>)
+        // Only build the cambria caches if we have x86_64(h) and at least one other arch
+        bool gotX86Cache    = false;
+        bool gotOtherCache  = false;
+        for (uint64_t i = 0; i != builder->options->numArchs; ++i) {
+            const char* arch = builder->options->archs[i];
+            if ( !strcmp(arch, "x86_64") || !strcmp(arch, "x86_64h") ) {
+                gotX86Cache = true;
+                continue;
+            }
+            gotOtherCache = true;
+        }
+
+        if ( (builder->options->platform == Platform::macOS) && gotX86Cache && gotOtherCache ) {
+            // Now create an Aot shared cache for any applicable caches
+            for (const auto& buildInstance : builder->builders) {
+                // Only convert the x86_64 cache
+                if ( buildInstance.options->archs != &dyld3::GradedArchs::x86_64)
+                    continue;
+
+                // Skip failed caches
+                if (!buildInstance.errors.empty())
+                    continue;
+
+                const uint8_t* aotCacheData;
+                size_t aotCacheSize;
+                std::string cdHashStr;
+
+                // Translate this shared cache file.
+                cambria::TranslationOptions options;
+
+#if !defined(RC_HIDE_J274) || !RC_HIDE_J274
+                // <rdar://problem/63767839> Don't generate a Tonga shared cache translation in the seed trains
+                cambria::translate_shared_cache(options, buildInstance.cacheData, buildInstance.cacheSize,
+                                                &aotCacheData, &aotCacheSize, &cdHashStr);
+
+                builder->translationResults.push_back({
+                    .data = aotCacheData,
+                    .size = aotCacheSize,
+                    .cdHash = cdHashStr,
+                    .path = std::string(cambria::kAotSharedCachePath),
+                    .bufferWasMalloced = false
+                });
+#endif
+
+                // Now translate again for aruba based systems
+                options.is_aruba_shared_cache = true;
+                cambria::translate_shared_cache(options, buildInstance.cacheData, buildInstance.cacheSize,
+                                                &aotCacheData, &aotCacheSize, &cdHashStr);
+
+                bool bufferWasMalloced = false;
+#if !defined(RC_HIDE_J274) || !RC_HIDE_J274
+                // For GM we don't need to ship the Aruba AOT any more.  It will be converted on the fly
+                // for the DTK.  We'll just put a 0-byte placeholder instead and record the cdHash
+                vm_deallocate(mach_task_self(), (vm_address_t)aotCacheData, aotCacheSize);
+                aotCacheData = (uint8_t*)calloc(1, 1);
+                aotCacheSize = 0;
+                bufferWasMalloced = true;
+#endif
+
+                builder->translationResults.push_back({
+                    .data = aotCacheData,
+                    .size = aotCacheSize,
+                    .cdHash = cdHashStr,
+                    .path = std::string(cambria::kAotSharedCachePathAruba),
+                    .bufferWasMalloced = bufferWasMalloced
+                });
+           }
+
+            // Convert translation results into File results
+            for (auto& translationResult : builder->translationResults) {
+                FileResult cacheFileResult;
+                cacheFileResult.version          = 1;
+                cacheFileResult.behavior         = AddFile;
+                cacheFileResult.path             = translationResult.path.c_str();
+                cacheFileResult.data             = translationResult.data;
+                cacheFileResult.size             = translationResult.size;
+                cacheFileResult.hashArch         = "x86_64";
+                cacheFileResult.hashType         = "sha256";
+                cacheFileResult.hash             = translationResult.cdHash.c_str();
+
+                builder->fileResultBuffers.push_back({ builder->fileResultStorage.size(), translationResult.bufferWasMalloced });
+                builder->fileResultStorage.emplace_back(cacheFileResult);
+            }
+        }
+#endif
+
         // Now that we have run all of the builds, collect the results
         // First push file results for each of the shared caches we built
         for (auto& buildInstance : builder->builders) {
-            SharedCacheBuilder* cacheBuilder = buildInstance.builder.get();
-
             CacheResult cacheBuildResult;
             cacheBuildResult.version                = 1;
             cacheBuildResult.loggingPrefix          = buildInstance.options->loggingPrefix.c_str();
@@ -634,7 +812,7 @@ bool runSharedCacheBuilder(struct MRMSharedCacheBuilder* builder) {
 
             builder->cacheResultStorage.emplace_back(cacheBuildResult);
 
-            if (!cacheBuilder->errorMessage().empty())
+            if (!buildInstance.errors.empty())
                 continue;
 
             FileResult cacheFileResult;
@@ -647,11 +825,23 @@ bool runSharedCacheBuilder(struct MRMSharedCacheBuilder* builder) {
             cacheFileResult.hashType         = buildInstance.cdHashType.c_str();
             cacheFileResult.hash             = buildInstance.cdHash.c_str();
 
+            builder->fileResultBuffers.push_back({ builder->fileResultStorage.size(), true });
             builder->fileResultStorage.emplace_back(cacheFileResult);
 
-            cacheBuilder->forEachCacheDylib(^(const std::string &path) {
-                ++builder->dylibsInCaches[path.c_str()];
-            });
+            // Add a file result for the .map file
+            if ( !buildInstance.macOSMap.empty() ) {
+                FileResult cacheFileResult;
+                cacheFileResult.version          = 1;
+                cacheFileResult.path             = buildInstance.macOSMapPath.c_str();
+                cacheFileResult.behavior         = AddFile;
+                cacheFileResult.data             = (const uint8_t*)buildInstance.macOSMap.data();
+                cacheFileResult.size             = buildInstance.macOSMap.size();
+                cacheFileResult.hashArch         = buildInstance.options->archs->name();
+                cacheFileResult.hashType         = buildInstance.cdHashType.c_str();
+                cacheFileResult.hash             = buildInstance.cdHash.c_str();
+
+                builder->fileResultStorage.emplace_back(cacheFileResult);
+            }
         }
 
         // Copy from the storage to the vector we can return to the API.
@@ -664,15 +854,67 @@ bool runSharedCacheBuilder(struct MRMSharedCacheBuilder* builder) {
         // 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 : builder->dylibsInCaches) {
-            if (dylibAndCount.second == numCaches) {
-                builder->filesToRemove.push_back(dylibAndCount.first.c_str());
+            const char* pathToRemove = dylibAndCount.first.c_str();
+
+            if ( builder->options->platform == Platform::macOS ) {
+                // macOS has to leave the simulator support binaries on disk
+                if ( strcmp(pathToRemove, "/usr/lib/system/libsystem_kernel.dylib") == 0 )
+                    continue;
+                if ( strcmp(pathToRemove, "/usr/lib/system/libsystem_platform.dylib") == 0 )
+                    continue;
+                if ( strcmp(pathToRemove, "/usr/lib/system/libsystem_pthread.dylib") == 0 )
+                    continue;
+            }
+
+            if (dylibAndCount.second.size() == numCaches) {
+                builder->filesToRemove.push_back(pathToRemove);
+            } else {
+                // File is not in every cache, so likely has perhaps only x86_64h slice
+                // but we built both x86_64 and x86_64h caches.
+                // We may still delete it if its in all caches it's eligible for, ie, we
+                // assume the cache builder knows about all possible arch's on the system and
+                // can delete anything it knows can't run
+                bool canDeletePath = true;
+                for (auto& buildInstance : builder->builders) {
+                    if ( dylibAndCount.second.count(&buildInstance) != 0 )
+                        continue;
+                    // This builder didn't get this image.  See if the image was ineligible
+                    // based on slide, ie, that dyld at runtime couldn't load this anyway, so
+                    // so removing it from disk won't hurt
+                    Diagnostics loaderDiag;
+                    const dyld3::GradedArchs* archs = buildInstance.options->archs;
+                    dyld3::Platform platform = buildInstance.options->platform;
+                    char realerPath[MAXPATHLEN];
+                    dyld3::closure::LoadedFileInfo fileInfo = dyld3::MachOAnalyzer::load(loaderDiag, builder->fileSystem,
+                                                                                         pathToRemove, *archs, platform, realerPath);
+                    if ( (platform == dyld3::Platform::macOS) && loaderDiag.hasError() ) {
+                        // Try again with iOSMac
+                        loaderDiag.clearError();
+                        fileInfo = dyld3::MachOAnalyzer::load(loaderDiag, builder->fileSystem,
+                                                              pathToRemove, *archs, dyld3::Platform::iOSMac, realerPath);
+                    }
+
+                    // We don't need the file content now, as we only needed to know if this file could be loaded
+                    builder->fileSystem.unloadFile(fileInfo);
+
+                    if ( loaderDiag.hasError() || (fileInfo.fileContent == nullptr) ) {
+                        // This arch/platform combination couldn't load this path, so we can remove it
+                        continue;
+                    }
+
+                    // This arch was compatible, so the dylib was rejected from this cache for some other reason, eg,
+                    // cache overflow.  We need to keep it on-disk
+                    canDeletePath = false;
+                    break;
+                }
+                if ( canDeletePath )
+                    builder->filesToRemove.push_back(pathToRemove);
             }
         }
 
         // Quit if we had any errors.
         for (auto& buildInstance : builder->builders) {
-            CacheBuilder* cacheBuilder = buildInstance.builder.get();
-            if (!cacheBuilder->errorMessage().empty())
+            if (!buildInstance.errors.empty())
                 return;
         }
 
@@ -711,12 +953,13 @@ const char* const* getFilesToRemove(const struct MRMSharedCacheBuilder* builder,
 }
 
 void destroySharedCacheBuilder(struct MRMSharedCacheBuilder* builder) {
-    for (auto& buildInstance : builder->builders) {
-        SharedCacheBuilder* cacheBuilder = buildInstance.builder.get();
-        cacheBuilder->deleteBuffer();
-    }
-    for (auto &fileResult : builder->fileResultStorage) {
-        free((void*)fileResult.data);
+    for (auto &indexAndIsDataMalloced : builder->fileResultBuffers) {
+        FileResult& fileResult = builder->fileResultStorage[indexAndIsDataMalloced.first];
+        if (indexAndIsDataMalloced.second) {
+            free((void*)fileResult.data);
+        } else {
+            vm_deallocate(mach_task_self(), (vm_address_t)fileResult.data, fileResult.size);
+        }
         fileResult.data = nullptr;
     }
     delete builder;
index 983b1ca058e17ec6d9be9efd55f7ce4f4cda3b18..e566fc02a000511ea3df1144e1795b7aeed869e2 100644 (file)
@@ -42,7 +42,7 @@ enum Platform {
     tvOS                = 3,    // PLATFORM_TVOS
     watchOS             = 4,    // PLATFORM_WATCHOS
     bridgeOS            = 5,    // PLATFORM_BRIDGEOS
-    iOSMac              = 6,    // PLATFORM_IOSMAC
+    iOSMac              = 6,    // PLATFORM_MACCATALYST
     iOS_simulator       = 7,    // PLATFORM_IOSIMULATOR
     tvOS_simulator      = 8,    // PLATFORM_TVOSSIMULATOR
     watchOS_simulator   = 9     // PLATFORM_WATCHOSSIMULATOR
@@ -66,7 +66,8 @@ enum FileFlags
 
     // These are for the order files
     DylibOrderFile                              = 100,
-    DirtyDataOrderFile                          = 101
+    DirtyDataOrderFile                          = 101,
+    ObjCOptimizationsFile                       = 102,
 };
 
 struct BuildOptions_v1
@@ -82,6 +83,22 @@ struct BuildOptions_v1
     bool                                        isLocallyBuiltCache;
 };
 
+// This is available when getVersion() returns 1.2 or higher
+struct BuildOptions_v2
+{
+    uint64_t                                    version;                        // Future proofing, set to 2
+    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;
+    // Added in v2
+    bool                                        optimizeForSize;
+};
+
 enum FileBehavior
 {
     AddFile                                     = 0,        // New file: uid, gid, mode, data, cdhash fields must be set
index df0644d82379cb550e1c4f842f6235a1b593ea97..dd5801f95f81682bd3b7739c2aeee630eb0e4e77 100644 (file)
@@ -25,9 +25,6 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/mman.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 <assert.h>
 #include <sys/uio.h>
 #include <unistd.h>
-#include <sys/param.h>
-#include <sys/sysctl.h>
-#include <sys/resource.h>
-#include <dirent.h>
-#include <rootless.h>
-#include <dscsym.h>
-#include <dispatch/dispatch.h>
-#include <pthread/pthread.h>
-#include <Bom/Bom.h>
-#include <CoreFoundation/CoreFoundation.h>
 
-#include <algorithm>
-#include <vector>
-#include <unordered_set>
-#include <unordered_set>
-#include <iostream>
-#include <fstream>
-
-#include "FileUtils.h"
-#include "StringUtils.h"
-#include "DyldSharedCache.h"
-#include "MachOFile.h"
-#include "MachOAnalyzer.h"
-#include "ClosureFileSystemPhysical.h"
-
-struct MappedMachOsByCategory
-{
-    const dyld3::GradedArchs&                   archs;
-    std::vector<DyldSharedCache::MappedMachO>   dylibsForCache;
-    std::vector<DyldSharedCache::MappedMachO>   otherDylibsAndBundles;
-    std::vector<DyldSharedCache::MappedMachO>   mainExecutables;
-    std::unordered_set<std::string>             badZippered;
-};
-
-static const char* sAllowedPrefixes[] = {
-    "/bin/",
-    "/sbin/",
-    "/usr/",
-    "/System/",
-    "/Library/Apple/System/",
-    "/Library/Apple/usr/",
-    "/System/Applications/App Store.app/",
-    "/System/Applications/Automator.app/",
-    "/System/Applications/Calculator.app/",
-    "/System/Applications/Calendar.app/",
-    "/System/Applications/Chess.app/",
-    "/System/Applications/Contacts.app/",
-    "/System/Applications/Dashboard.app/",
-    "/System/Applications/Dictionary.app/",
-    "/System/Applications/FaceTime.app/",
-    "/System/Applications/Font Book.app/",
-    "/System/Applications/Image Capture.app/",
-    "/System/Applications/Launchpad.app/",
-    "/System/Applications/Mail.app/",
-    "/System/Applications/Maps.app/",
-    "/System/Applications/Messages.app/",
-    "/System/Applications/Mission Control.app/",
-    "/System/Applications/Notes.app/",
-    "/System/Applications/Photo Booth.app/",
-    "/System/Applications/Preview.app/",
-    "/System/Applications/QuickTime Player.app/",
-    "/System/Applications/Reminders.app/",
-    "/Applications/Safari.app/",
-    "/System/Applications/Siri.app/",
-    "/System/Applications/Stickies.app/",
-    "/System/Applications/System Preferences.app/",
-    "/System/Applications/TextEdit.app/",
-    "/System/Applications/Time Machine.app/",
-    "/System/Applications/iBooks.app/",
-    "/System/Applications/iTunes.app/",
-    "/System/Applications/Utilities/Activity Monitor.app",
-    "/System/Applications/Utilities/AirPort Utility.app",
-    "/System/Applications/Utilities/Audio MIDI Setup.app",
-    "/System/Applications/Utilities/Bluetooth File Exchange.app",
-    "/System/Applications/Utilities/Boot Camp Assistant.app",
-    "/System/Applications/Utilities/ColorSync Utility.app",
-    "/System/Applications/Utilities/Console.app",
-    "/System/Applications/Utilities/Digital Color Meter.app",
-    "/System/Applications/Utilities/Disk Utility.app",
-    "/System/Applications/Utilities/Grab.app",
-    "/System/Applications/Utilities/Grapher.app",
-    "/System/Applications/Utilities/Keychain Access.app",
-    "/System/Applications/Utilities/Migration Assistant.app",
-    "/System/Applications/Utilities/Script Editor.app",
-    "/System/Applications/Utilities/System Information.app",
-    "/System/Applications/Utilities/Terminal.app",
-    "/System/Applications/Utilities/VoiceOver Utility.app",
-    "/Library/CoreMediaIO/Plug-Ins/DAL/"                // temp until plugins moved or closured working
-};
-
-static const char* sDontUsePrefixes[] = {
-    "/usr/share",
-    "/usr/local/",
-    "/System/Library/Assets",
-    "/System/Library/StagedFrameworks",
-    "/Library/Apple/System/Library/StagedFrameworks",
-    "/System/Library/Kernels/",
-    "/bin/zsh",                             // until <rdar://31026756> is fixed
-    "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Support/mdworker", // these load third party plugins
-    "/usr/bin/mdimport", // these load third party plugins
-};
-
-
-static bool verbose = false;
-
-
-
-
-static bool addIfMachO(const dyld3::closure::FileSystem& fileSystem, const std::string& runtimePath, const struct stat& statBuf,
-                       bool requireSIP, dev_t rootFS, 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;
-    if ( startsWith(runtimePath, "/usr/lib/system/introspection/") )
-        return false;
-
-#if !BUILDING_UPDATE_OTHER_DYLD_CACHE_BUILDER
-    // Only use files on the same volume as the boot volume
-    if (statBuf.st_dev != rootFS) {
-        if ( verbose )
-            fprintf(stderr, "update_dyld_shared_cache: warning: skipping overlay file '%s' which is not on the root volume\n", runtimePath.c_str());
-       return false;
-    }
-#endif
-
-    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;
-    for (MappedMachOsByCategory& file : files) {
-        Diagnostics diag;
-        char realerPath[MAXPATHLEN];
-        dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, runtimePath.c_str(), file.archs, dyld3::Platform::macOS, realerPath);
-        if (diag.hasError() ) {
-            // Try again with iOSMac
-            diag.clearError();
-            loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, runtimePath.c_str(), file.archs, dyld3::Platform::iOSMac, realerPath);
-        }
-        const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
-        if ( ma != nullptr ) {
-            bool           issetuid       = false;
-            const uint64_t sliceLen       = loadedFileInfo.sliceLen;
-            const bool     isSipProtected = loadedFileInfo.isSipProtected;
-            if ( ma->isDynamicExecutable() ) {
-                // When SIP enabled, only build closures for SIP protected programs
-                if ( !requireSIP || isSipProtected ) {
-                    //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, sliceLen, issetuid, isSipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino);
-                }
-            }
-            else if ( ma->canBePlacedInDyldCache(runtimePath.c_str(), ^(const char* msg) {
-                if (verbose)
-                    fprintf(stderr, "update_dyld_shared_cache: warning dylib located at '%s' cannot be placed in cache because: %s\n", runtimePath.c_str(), msg);
-            }) ) {
-                // when SIP is enabled, only dylib protected by SIP can go in cache
-                if ( !requireSIP || isSipProtected )
-                    file.dylibsForCache.emplace_back(runtimePath, ma, sliceLen, issetuid, isSipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino);
-                else if ( ma->canHavePrecomputedDlopenClosure(runtimePath.c_str(), warningHandler) )
-                    file.otherDylibsAndBundles.emplace_back(runtimePath, ma, sliceLen, issetuid, isSipProtected, loadedFileInfo.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 ( dyld3::MachOFile::isSharedCacheEligiblePath(runtimePath.c_str()) )
-                            fprintf(stderr, "update_dyld_shared_cache: warning @rpath install name for system framework: %s\n", runtimePath.c_str());
-                    }
-                }
-                if ( ma->canHavePrecomputedDlopenClosure(runtimePath.c_str(), warningHandler) ) {
-                    // Only add a dlopen closure for objc trampolines.  The rest should have been shared cache eligible.
-                    bool addClosure = false;
-                    if ( ma->isDylib() ) {
-                        std::string installName = ma->installName();
-                        addClosure = installName == "/usr/lib/libobjc-trampolines.dylib";
-                    } else {
-                        addClosure = true;
-                    }
-                    if (addClosure)
-                        file.otherDylibsAndBundles.emplace_back(runtimePath, ma, sliceLen, issetuid, isSipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino);
-                }
-            }
-            result = true;
-        }
-    }
-
-    return result;
-}
-
-static void findAllFiles(const dyld3::closure::FileSystem& fileSystem, const std::vector<std::string>& pathPrefixes,
-                         bool requireSIP, dev_t rootFS, std::vector<MappedMachOsByCategory>& files)
-{
-    std::unordered_set<std::string> skipDirs;
-    for (const char* s : sDontUsePrefixes)
-        skipDirs.insert(s);
-
-    __block std::unordered_set<std::string> alreadyUsed;
-    bool multiplePrefixes = (pathPrefixes.size() > 1);
-    for (const std::string& prefix : pathPrefixes) {
-        // get all files from overlay for this search dir
-        for (const char* searchDir : sAllowedPrefixes ) {
-            iterateDirectoryTree(prefix, searchDir, ^(const std::string& dirPath) { return (skipDirs.count(dirPath) != 0); }, ^(const std::string& path, const struct stat& statBuf) {
-                // ignore files that don't have 'x' bit set (all runnable mach-o files do)
-                const bool hasXBit = ((statBuf.st_mode & S_IXOTH) == S_IXOTH);
-                if ( !hasXBit && !endsWith(path, ".dylib") )
-                    return;
-
-                // ignore files too small
-                if ( statBuf.st_size < 0x3000 )
-                    return;
-
-                // don't add paths already found using previous prefix
-                if ( multiplePrefixes && (alreadyUsed.count(path) != 0) )
-                    return;
-
-                // if the file is mach-o, add to list
-                if ( addIfMachO(fileSystem, path, statBuf, requireSIP, rootFS, files) ) {
-                    if ( multiplePrefixes )
-                        alreadyUsed.insert(path);
-                }
-            });
-        }
-    }
-}
-
-static const char* sReceiptLocations[] = {
-    "/System/Library/Receipts",
-    "/Library/Apple/System/Library/Receipts"
-};
-
-static void findOSFilesViaBOMS(const dyld3::closure::FileSystem& fileSystem, const std::vector<std::string>& pathPrefixes,
-                               bool requireSIP, dev_t rootFS, std::vector<MappedMachOsByCategory>& files)
-{
-    __block std::unordered_set<std::string> runtimePathsFound;
-    __block bool foundUsableBom = false;
-    for (const std::string& prefix : pathPrefixes) {
-        for (const char* dirToIterate : sReceiptLocations ) {
-            iterateDirectoryTree(prefix, dirToIterate, ^(const std::string&) { return false; }, ^(const std::string& path, const struct stat& statBuf) {
-                if ( !contains(path, "com.apple.pkg.") )
-                    return;
-                if ( !endsWith(path, ".bom") )
-                    return;
-                std::string fullPath = prefix + path;
-                BOMBom bom = BOMBomOpenWithSys(fullPath.c_str(), false, NULL);
-                if ( bom == nullptr )
-                    return;
-                BOMFSObject rootFso = BOMBomGetRootFSObject(bom);
-                if ( rootFso == nullptr ) {
-                    BOMBomFree(bom);
-                    return;
-                }
-                BOMBomEnumerator e = BOMBomEnumeratorNew(bom, rootFso);
-                if ( e == nullptr ) {
-                    fprintf(stderr, "Can't get enumerator for BOM root FSObject\n");
-                    return;
-                }
-                BOMFSObjectFree(rootFso);
-                //fprintf(stderr, "using BOM %s\n", path.c_str());
-                foundUsableBom = true;
-                while (BOMFSObject fso = BOMBomEnumeratorNext(e)) {
-                    if ( BOMFSObjectIsBinaryObject(fso) ) {
-                        const char* runPath = BOMFSObjectPathName(fso);
-                        if ( (runPath[0] == '.') && (runPath[1] == '/') )
-                            ++runPath;
-                        // <rdar://problem/48748330> update_dyld_shared_cache needs to fold away /S/L/Templates/Data
-                        if (strncmp(runPath, "/System/Library/Templates/Data/", 31) == 0 )
-                            runPath = &runPath[30];
-                        if ( runtimePathsFound.count(runPath) == 0 ) {
-                            // only add files from sAllowedPrefixes and not in sDontUsePrefixes
-                            bool inSearchDir = false;
-                            for (const char* searchDir : sAllowedPrefixes ) {
-                                if ( strncmp(searchDir, runPath, strlen(searchDir)) == 0 )  {
-                                    inSearchDir = true;
-                                    break;
-                                }
-                            }
-                            if ( inSearchDir ) {
-                                bool inSkipDir = false;
-                                for (const char* skipDir : sDontUsePrefixes) {
-                                    if ( strncmp(skipDir, runPath, strlen(skipDir)) == 0 )  {
-                                        inSkipDir = true;
-                                        break;
-                                    }
-                                }
-                                if ( !inSkipDir ) {
-                                    for (const std::string& prefix2 : pathPrefixes) {
-                                        struct stat statBuf2;
-                                        std::string fullPath2 = prefix2 + runPath;
-                                        if ( stat(fullPath2.c_str(), &statBuf2) == 0 ) {
-                                            if ( addIfMachO(fileSystem, runPath, statBuf2, requireSIP, rootFS, files) ) {
-                                                runtimePathsFound.insert(runPath);
-                                                break;
-                                            }
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-                    BOMFSObjectFree(fso);
-                }
-
-                BOMBomEnumeratorFree(e);
-                BOMBomFree(bom);
-            });
-        }
-    }
-
-    if (!foundUsableBom)
-        fprintf(stderr, "update_dyld_shared_cache: warning: No usable BOM files were found in '/System/Library/Receipts'\n");
-}
-
-
-static bool dontCache(const std::string& volumePrefix, const std::string& archName,
-                      const std::unordered_set<std::string>& pathsWithDuplicateInstallName,
-                      const std::unordered_set<std::string>& badZippered,
-                      const DyldSharedCache::MappedMachO& aFile, bool warn,
-                      const std::unordered_set<std::string>& skipDylibs)
-{
-    if ( skipDylibs.count(aFile.runtimePath) )
-        return true;
-    if ( startsWith(aFile.runtimePath, "/usr/lib/system/introspection/") )
-        return true;
-    if ( startsWith(aFile.runtimePath, "/System/Library/QuickTime/") )
-        return true;
-    if ( startsWith(aFile.runtimePath, "/System/Library/Tcl/") )
-        return true;
-    if ( startsWith(aFile.runtimePath, "/System/Library/Perl/") )
-        return true;
-    if ( startsWith(aFile.runtimePath, "/System/Library/MonitorPanels/") )
-        return true;
-    if ( startsWith(aFile.runtimePath, "/System/Library/Accessibility/") )
-        return true;
-    if ( startsWith(aFile.runtimePath, "/usr/local/") )
-        return true;
-
-    // anything inside a .app bundle is specific to app, so should not be in shared cache
-    if ( aFile.runtimePath.find(".app/") != std::string::npos )
-        return true;
-
-    if ( archName == "i386" ) {
-        if ( startsWith(aFile.runtimePath, "/System/Library/CoreServices/") )
-            return true;
-        if ( startsWith(aFile.runtimePath, "/System/Library/Extensions/") )
-            return true;
-    }
-
-    if ( aFile.runtimePath.find("//") != std::string::npos ) {
-        if (warn) fprintf(stderr, "update_dyld_shared_cache: warning: %s skipping because of bad install name %s\n", archName.c_str(), aFile.runtimePath.c_str());
-        return true;
-    }
-
-    const char* installName = aFile.mh->installName();
-    if ( (pathsWithDuplicateInstallName.count(aFile.runtimePath) != 0) && (aFile.runtimePath != installName) ) {
-        // <rdar://problem/46431467> if a dylib moves and a symlink is installed into its place, bom iterator will see both and issue a warning
-        struct stat statBuf;
-        bool isSymLink = ( (lstat(aFile.runtimePath.c_str(), &statBuf) == 0) && S_ISLNK(statBuf.st_mode) );
-        if (!isSymLink && warn) fprintf(stderr, "update_dyld_shared_cache: warning: %s skipping because of duplicate install name %s\n", archName.c_str(), aFile.runtimePath.c_str());
-        return true;
-    }
-
-    if (badZippered.count(aFile.runtimePath)) {
-        return true;
-    }
-
-    if ( aFile.runtimePath != installName ) {
-        // see if install name is a symlink to actual path
-        std::string fullInstall = volumePrefix + installName;
-        char resolvedPath[PATH_MAX];
-        if ( realpath(fullInstall.c_str(), resolvedPath) != NULL ) {
-            std::string resolvedSymlink = resolvedPath;
-            if ( !volumePrefix.empty() ) {
-                resolvedSymlink = resolvedSymlink.substr(volumePrefix.size());
-            }
-            if ( aFile.runtimePath == resolvedSymlink ) {
-                return false;
-            }
-        }
-        // <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;
-    }
-    return false;
-}
-
-static void pruneCachedDylibs(const std::string& volumePrefix, const std::unordered_set<std::string>& skipDylibs,
-                              MappedMachOsByCategory& fileSet, bool warn)
-{
-    std::unordered_set<std::string> pathsWithDuplicateInstallName;
-
-    std::unordered_map<std::string, std::string> installNameToFirstPath;
-    for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) {
-        const char* installName = aFile.mh->installName();
-        auto pos = installNameToFirstPath.find(installName);
-        if ( pos == installNameToFirstPath.end() ) {
-            installNameToFirstPath[installName] = aFile.runtimePath;
-        }
-        else {
-            pathsWithDuplicateInstallName.insert(aFile.runtimePath);
-            pathsWithDuplicateInstallName.insert(installNameToFirstPath[installName]);
-        }
-    }
-
-    std::unordered_map<std::string, std::string> macOSPathToTwinPath;
-    for (const auto& entry : installNameToFirstPath) {
-        if ( startsWith(entry.first, "/System/iOSSupport/") ) {
-            std::string tail = entry.first.substr(18);
-            if ( installNameToFirstPath.count(tail) != 0 ) {
-                macOSPathToTwinPath.insert({ tail, entry.first });
-            }
-        }
-    }
-
-    for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) {
-        if ( aFile.mh->isZippered() ) {
-            aFile.mh->forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) {
-                auto macOSAndTwinPath = macOSPathToTwinPath.find(loadPath);
-                if ( macOSAndTwinPath != macOSPathToTwinPath.end() ) {
-                    if ( warn ) {
-                        fprintf(stderr, "update_dyld_shared_cache: warning: evicting UIKitForMac binary: %s as it is linked by zippered binary %s\n",
-                                macOSAndTwinPath->second.c_str(), aFile.runtimePath.c_str());
-                    }
-                    fileSet.badZippered.insert(macOSAndTwinPath->second);
-                }
-            });
-        }
-    }
-
-    for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) {
-        if ( dontCache(volumePrefix, fileSet.archs.name(), pathsWithDuplicateInstallName, fileSet.badZippered, aFile, true, skipDylibs) ){
-            // <rdar://problem/46423929> don't build dlopen closures for symlinks to something in the dyld cache
-            if ( pathsWithDuplicateInstallName.count(aFile.runtimePath) == 0 )
-                fileSet.otherDylibsAndBundles.push_back(aFile);
-        }
-    }
-    fileSet.dylibsForCache.erase(std::remove_if(fileSet.dylibsForCache.begin(), fileSet.dylibsForCache.end(),
-        [&](const DyldSharedCache::MappedMachO& aFile) { return dontCache(volumePrefix, fileSet.archs.name(), pathsWithDuplicateInstallName, fileSet.badZippered, aFile, false, skipDylibs); }),
-        fileSet.dylibsForCache.end());
-}
-
-static void pruneOtherDylibs(const std::string& volumePrefix, MappedMachOsByCategory& fileSet)
-{
-    // other OS dylibs should not contain dylibs that are embedded in some .app bundle
-    fileSet.otherDylibsAndBundles.erase(std::remove_if(fileSet.otherDylibsAndBundles.begin(), fileSet.otherDylibsAndBundles.end(),
-                                                       [&](const DyldSharedCache::MappedMachO& aFile) { return (aFile.runtimePath.find(".app/") != std::string::npos); }),
-                                        fileSet.otherDylibsAndBundles.end());
-}
-static void pruneExecutables(const std::string& volumePrefix, MappedMachOsByCategory& fileSet)
-{
-    // don't build closures for xcode shims in /usr/bin (e.g. /usr/bin/clang) which re-exec themselves to a tool inside Xcode.app
-    fileSet.mainExecutables.erase(std::remove_if(fileSet.mainExecutables.begin(), fileSet.mainExecutables.end(),
-        [&](const DyldSharedCache::MappedMachO& aFile) {
-            if ( !startsWith(aFile.runtimePath, "/usr/bin/") )
-                return false;
-            __block bool isXcodeShim = false;
-            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;
-            });
-            return isXcodeShim;
-        }), fileSet.mainExecutables.end());
-}
-
-static bool existingCacheUpToDate(const std::string& existingCache, const std::vector<DyldSharedCache::MappedMachO>& currentDylibs)
-{
-    // if no existing cache, it is not up-to-date
-    int fd = ::open(existingCache.c_str(), O_RDONLY);
-    if ( fd < 0 )
-        return false;
-    struct stat statbuf;
-    if ( ::fstat(fd, &statbuf) == -1 ) {
-        ::close(fd);
-        return false;
-    }
-
-    // build map of found dylibs
-    std::unordered_map<std::string, const DyldSharedCache::MappedMachO*> currentDylibMap;
-    for (const DyldSharedCache::MappedMachO& aFile : currentDylibs) {
-        //fprintf(stderr, "0x%0llX 0x%0llX  %s\n", aFile.inode, aFile.modTime, aFile.runtimePath.c_str());
-        currentDylibMap[aFile.runtimePath] = &aFile;
-    }
-
-    // make sure all dylibs in existing cache have same mtime and inode as found dylib
-    __block bool foundMismatch = false;
-    const uint64_t cacheMapLen = statbuf.st_size;
-    void *p = ::mmap(NULL, cacheMapLen, PROT_READ, MAP_PRIVATE, fd, 0);
-    if ( p != MAP_FAILED ) {
-        const DyldSharedCache* cache = (DyldSharedCache*)p;
-        cache->forEachImageEntry(^(const char* installName, uint64_t mTime, uint64_t inode) {
-            bool foundMatch = false;
-            auto pos = currentDylibMap.find(installName);
-            if ( pos != currentDylibMap.end() ) {
-                const DyldSharedCache::MappedMachO* foundDylib = pos->second;
-                if ( (foundDylib->inode == inode) && (foundDylib->modTime == mTime) ) {
-                    foundMatch = true;
-                }
-            }
-            if ( !foundMatch ) {
-                // use slow path and look for any dylib with a matching inode and mtime
-                bool foundSlow = false;
-                for (const DyldSharedCache::MappedMachO& aFile : currentDylibs) {
-                    if ( (aFile.inode == inode) && (aFile.modTime == mTime) ) {
-                        foundSlow = true;
-                        break;
-                    }
-                }
-                if ( !foundSlow ) {
-                    foundMismatch = true;
-                    if ( verbose )
-                        fprintf(stderr, "rebuilding dyld cache because dylib changed: %s\n", installName);
-                }
-            }
-         });
-        ::munmap(p, cacheMapLen);
-    }
-
-    ::close(fd);
-
-    return !foundMismatch;
-}
-
-
-inline uint32_t absolutetime_to_milliseconds(uint64_t abstime)
-{
-    return (uint32_t)(abstime/1000/1000);
-}
-
-static bool runningOnHaswell()
-{
-    // check system is capable of running x86_64h code
-    struct host_basic_info  info;
-    mach_msg_type_number_t  count    = HOST_BASIC_INFO_COUNT;
-    mach_port_t             hostPort = mach_host_self();
-    kern_return_t           result   = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count);
-    mach_port_deallocate(mach_task_self(), hostPort);
-
-    return ( (result == KERN_SUCCESS) && (info.cpu_subtype == CPU_SUBTYPE_X86_64_H) );
-}
-
-#if !BUILDING_UPDATE_OTHER_DYLD_CACHE_BUILDER
-static std::string currentToolRealPath()
-{
-    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 )
-            return resolvedCurToolPath;
-        else
-            return curToolPath;
-    }
-    return "/usr/bin/update_dyld_shared_cache";
-}
-#endif
-
-#define TERMINATE_IF_LAST_ARG( s )      \
-    do {                                \
-        if ( i == argc - 1 ) {          \
-            fprintf(stderr, s );        \
-            return 1;                   \
-        }                               \
-    } while ( 0 )
 
 int main(int argc, const char* argv[], const char* envp[])
 {
-    std::string                     rootPath;
-    std::string                     overlayPath;
-    std::string                     dylibListFile;
-    bool                            universal = false;
-    bool                            force = false;
-    bool                            searchDisk = false;
-    bool                            dylibsRemoved = false;
-    std::string                     cacheDir;
-    std::unordered_set<std::string> archStrs;
-    std::unordered_set<std::string> skipDylibs;
-
-    // parse command line options
-    for (int i = 1; i < argc; ++i) {
-        const char* arg = argv[i];
-        if (strcmp(arg, "-debug") == 0) {
-            verbose = true;
-        }
-        else if (strcmp(arg, "-verbose") == 0) {
-            verbose = true;
-        }
-        else if (strcmp(arg, "-dont_map_local_symbols") == 0) {
-            //We are going to ignore this
-        }
-        else if (strcmp(arg, "-dylib_list") == 0) {
-            TERMINATE_IF_LAST_ARG("-dylib_list missing argument");
-            dylibListFile = argv[++i];
-        }
-        else if ((strcmp(arg, "-root") == 0) || (strcmp(arg, "--root") == 0)) {
-            TERMINATE_IF_LAST_ARG("-root missing path argument\n");
-            rootPath = argv[++i];
-        }
-        else if (strcmp(arg, "-overlay") == 0) {
-            TERMINATE_IF_LAST_ARG("-overlay missing path argument\n");
-            overlayPath = argv[++i];
-        }
-        else if (strcmp(arg, "-cache_dir") == 0) {
-            TERMINATE_IF_LAST_ARG("-cache_dir missing path argument\n");
-            cacheDir = argv[++i];
-        }
-        else if (strcmp(arg, "-arch") == 0) {
-            TERMINATE_IF_LAST_ARG("-arch missing argument\n");
-            archStrs.insert(argv[++i]);
-        }
-        else if (strcmp(arg, "-search_disk") == 0) {
-            searchDisk = true;
-        }
-        else if (strcmp(arg, "-dylibs_removed_in_mastering") == 0) {
-            dylibsRemoved = true;
-        }
-        else if (strcmp(arg, "-force") == 0) {
-            force = true;
-        }
-        else if (strcmp(arg, "-sort_by_name") == 0) {
-            //No-op, we always do this now
-        }
-        else if (strcmp(arg, "-universal_boot") == 0) {
-            universal = true;
-        }
-        else if (strcmp(arg, "-skip") == 0) {
-            TERMINATE_IF_LAST_ARG("-skip missing argument\n");
-            skipDylibs.insert(argv[++i]);
-        }
-        else {
-            //usage();
-            fprintf(stderr, "update_dyld_shared_cache: unknown option: %s\n", arg);
-            return 1;
-        }
-    }
-
-    if ( !rootPath.empty() & !overlayPath.empty() ) {
-        fprintf(stderr, "-root and -overlay cannot be used together\n");
-        return 1;
-    }
-    // canonicalize rootPath
-    if ( !rootPath.empty() ) {
-        char resolvedPath[PATH_MAX];
-        if ( realpath(rootPath.c_str(), resolvedPath) != NULL ) {
-            rootPath = resolvedPath;
-        }
-        // <rdar://problem/33223984> when building closures for boot volume, pathPrefixes should be empty
-        if ( rootPath == "/" ) {
-            rootPath = "";
-        }
-    }
-    // canonicalize overlayPath
-    if ( !overlayPath.empty() ) {
-        char resolvedPath[PATH_MAX];
-        if ( realpath(overlayPath.c_str(), resolvedPath) != NULL ) {
-            overlayPath = resolvedPath;
-        }
-    }
-
-#if !BUILDING_UPDATE_OTHER_DYLD_CACHE_BUILDER
-    // <rdar://problem/36362221> update_dyld_shared_cache -root should re-exec() itself to a newer version
-    std::string newTool;
-    if ( !rootPath.empty() )
-        newTool = rootPath + "/usr/bin/update_dyld_shared_cache_root_mode";
-    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 ) {
-            // don't re-exec if we are already running that tool
-            if ( newTool != currentToolRealPath() ) {
-                argv[0] = newTool.c_str();
-                execve(newTool.c_str(), (char**)argv, (char**)envp);
-                fprintf(stderr, "update_dyld_shared_cache: error: could not find '%s/usr/bin/update_dyld_shared_cache_root_mode' in target volume\n", rootPath.c_str());
-                return 1;
-            }
-        }
-        if ( !rootPath.empty() ) {
-            // could be old macOS dmg, try old tool name
-            newTool = rootPath + "/usr/bin/update_dyld_shared_cache";
-            if ( stat(newTool.c_str(), &newToolStatBuf) == 0 ) {
-                // don't re-exec if we are already running that tool
-                if ( newTool != currentToolRealPath() ) {
-                    argv[0] = newTool.c_str();
-                    execve(newTool.c_str(), (char**)argv, (char**)envp);
-                }
-            }
-            fprintf(stderr, "update_dyld_shared_cache: error: could not find '%s/usr/bin/update_dyld_shared_cache_root_mode' in target volume\n", rootPath.c_str());
-            return 1;
-        }
-    }
-#else
-    if ( rootPath.empty() ) {
-        fprintf(stderr, "update_dyld_shared_cache_root_mode: error: -root option missing\n");
-        return 1;
-    }
-#endif
-
-    // Find the boot volume so that we can ensure all overlays are on the same volume
-    struct stat rootStatBuf;
-    if ( stat(rootPath == "" ? "/" : rootPath.c_str(), &rootStatBuf) != 0 ) {
-        fprintf(stderr, "update_dyld_shared_cache: error: could not stat root file system because '%s'\n", strerror(errno));
-        return 1;
-    }
-    dev_t rootFS = rootStatBuf.st_dev;
-
-
-    //
-    // pathPrefixes for three modes:
-    //   1) no options: { "" }           // search only boot volume
-    //   2) -overlay:   { overlay, "" }  // search overlay, then boot volume
-    //   3) -root:      { root }         // search only -root volume
-    //
-    std::vector<std::string> pathPrefixes;
-    if ( !overlayPath.empty() ) {
-        // Only add the overlay path if it exists, and is the same volume as the root
-        struct stat overlayStatBuf;
-        if ( stat(overlayPath.c_str(), &overlayStatBuf) != 0 ) {
-            fprintf(stderr, "update_dyld_shared_cache: warning: ignoring overlay dir '%s' because '%s'\n", overlayPath.c_str(), strerror(errno));
-            overlayPath.clear();
-        }
-        else {
-            char resolvedOverlayPath[PATH_MAX];
-            if ( realpath(overlayPath.c_str(), resolvedOverlayPath) != NULL ) {
-                overlayPath = resolvedOverlayPath;
-            }
-            else {
-                fprintf(stderr, "update_dyld_shared_cache: warning: ignoring overlay dir '%s' because realpath() failed\n", overlayPath.c_str());
-                overlayPath.clear();
-            }
-        }
-        if ( !overlayPath.empty() )
-            pathPrefixes.push_back(overlayPath);
-    }
-    pathPrefixes.push_back(rootPath);
-
-    // build FileSystem object
-    const char* fsRoot    = rootPath.empty()    ? nullptr : rootPath.c_str();
-    const char* fsOverlay = overlayPath.empty() ? nullptr : overlayPath.c_str();
-    dyld3::closure::FileSystemPhysical fileSystem(fsRoot, fsOverlay);
-
-    // normalize output directory
-    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;
-        else if ( !overlayPath.empty()  )
-            cacheDir = overlayPath +  MACOSX_DYLD_SHARED_CACHE_DIR;
-        else
-            cacheDir = MACOSX_DYLD_SHARED_CACHE_DIR;
-    }
-    int err = mkpath_np(cacheDir.c_str(), S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
-    if ( (err != 0) && (err != EEXIST) ) {
-        fprintf(stderr, "update_dyld_shared_cache: could not access cache dir: mkpath_np(%s) failed errno=%d\n", cacheDir.c_str(), err);
-        return 1;
-    }
-    // make sure cacheDir is always a real path, so it can be checked later to see if it changed
-    char resolvedCachePath[PATH_MAX];
-    ::realpath(cacheDir.c_str(), resolvedCachePath);
-    cacheDir = resolvedCachePath;
-
-#if BUILDING_UPDATE_OTHER_DYLD_CACHE_BUILDER
-    bool requireDylibsBeRootlessProtected = false;
-#else
-    bool requireDylibsBeRootlessProtected = isProtectedBySIPExceptDyld(cacheDir);
-    if ( requireDylibsBeRootlessProtected && !overlayPath.empty() && !isProtectedBySIP(overlayPath.c_str()) ) {
-        fprintf(stderr, "update_dyld_shared_cache: warning: ignoring overlay dir '%s' because it is not SIP protected\n", overlayPath.c_str());
-        overlayPath.clear();
-        pathPrefixes.clear();
-        pathPrefixes.push_back(rootPath);
-    }
-#endif
-
-    if ( archStrs.empty() ) {
-        // <rdar://44190126> check if OS has enough i386 to make a shared cache
-        char realerPath[MAXPATHLEN];
-        Diagnostics testDiag;
-        const char* foundationPath = "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation";
-        dyld3::closure::LoadedFileInfo foundationInfo = dyld3::MachOAnalyzer::load(testDiag, fileSystem, foundationPath, dyld3::GradedArchs::i386, dyld3::Platform::macOS, realerPath);
-        bool foundationHas32bit = (foundationInfo.fileContent != NULL);
-        if ( foundationHas32bit )
-            fileSystem.unloadFile(foundationInfo);
-
-        if ( universal ) {
-            // <rdar://problem/26182089> -universal_boot should make all possible dyld caches
-            if ( foundationHas32bit )
-                archStrs.insert("i386");
-            archStrs.insert("x86_64");
-            archStrs.insert("x86_64h");
-        }
-        else {
-            // just make caches for this machine
-            if ( foundationHas32bit )
-                archStrs.insert("i386");
-            archStrs.insert(runningOnHaswell() ? "x86_64h" : "x86_64");
-        }
-    }
-
-    uint64_t t1 = mach_absolute_time();
-
-    // find all mach-o files for requested architectures
-    __block std::vector<MappedMachOsByCategory> allFileSets;
-    if ( archStrs.count("x86_64") )
-        allFileSets.push_back({dyld3::GradedArchs::x86_64});
-    if ( archStrs.count("x86_64h") )
-        allFileSets.push_back({dyld3::GradedArchs::x86_64h});
-    if ( archStrs.count("i386") )
-        allFileSets.push_back({dyld3::GradedArchs::i386});
-    if ( searchDisk )
-        findAllFiles(fileSystem, pathPrefixes, requireDylibsBeRootlessProtected, rootFS, allFileSets);
-    else {
-        std::unordered_set<std::string> runtimePathsFound;
-        findOSFilesViaBOMS(fileSystem, pathPrefixes, requireDylibsBeRootlessProtected, rootFS, allFileSets);
-    }
-
-    // nothing in OS uses i386 dylibs, so only dylibs used by third party apps need to be in cache
-    for (MappedMachOsByCategory& fileSet : allFileSets) {
-        pruneCachedDylibs(rootPath, skipDylibs, fileSet, verbose);
-        pruneOtherDylibs(rootPath, fileSet);
-        pruneExecutables(rootPath, fileSet);
-   }
-
-    uint64_t t2 = mach_absolute_time();
-    if ( verbose ) {
-        if ( searchDisk )
-            fprintf(stderr, "time to scan file system and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2-t1));
-        else
-            fprintf(stderr, "time to read BOM and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2-t1));
-    }
-
-    // build caches in parallel on machines with at leat 4GB of RAM
-    uint64_t memSize = 0;
-    size_t sz = sizeof(memSize);;
-    bool buildInParallel = false;
-    if ( sysctlbyname("hw.memsize", &memSize, &sz, NULL, 0) == 0 ) {
-        if ( memSize >= 0x100000000ULL )
-            buildInParallel = true;
-    }
-    dispatch_queue_t dqueue = buildInParallel ? dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
-                                              : dispatch_queue_create("serial-queue", DISPATCH_QUEUE_SERIAL);
-
-    // build all caches
-    __block bool cacheBuildFailure = false;
-    __block bool wroteSomeCacheFile = false;
-    dispatch_apply(allFileSets.size(), dqueue, ^(size_t index) {
-        MappedMachOsByCategory& fileSet = allFileSets[index];
-        const std::string outFile = cacheDir + "/dyld_shared_cache_" + fileSet.archs.name();
-
-        DyldSharedCache::MappedMachO (^loader)(const std::string&) = ^DyldSharedCache::MappedMachO(const std::string& runtimePath) {
-            if ( skipDylibs.count(runtimePath) )
-                return DyldSharedCache::MappedMachO();
-            if (fileSet.badZippered.count(runtimePath)) {
-                return DyldSharedCache::MappedMachO();
-            }
-            for (const std::string& prefix : pathPrefixes) {
-                std::string fullPath = prefix + runtimePath;
-                struct stat statBuf;
-                if ( stat(fullPath.c_str(), &statBuf) == 0 ) {
-                    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.archs});
-                   if ( addIfMachO(fileSystem, runtimePath, statBuf, requireDylibsBeRootlessProtected, rootFS, 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();
-                        }
-                    }
-                }
-            }
-            return DyldSharedCache::MappedMachO();
-        };
-        size_t startCount = fileSet.dylibsForCache.size();
-        std::vector<std::pair<DyldSharedCache::MappedMachO, std::set<std::string>>> excludes;
-        DyldSharedCache::verifySelfContained(fileSet.dylibsForCache, fileSet.badZippered, loader, excludes);
-        for (size_t i=startCount; i < fileSet.dylibsForCache.size(); ++i) {
-            fprintf(stderr, "update_dyld_shared_cache: warning: %s not in .bom, but adding required dylib %s\n", fileSet.archs.name(), fileSet.dylibsForCache[i].runtimePath.c_str());
-        }
-        for (auto& exclude : excludes) {
-            std::string reasons = "(\"";
-            for (auto i = exclude.second.begin(); i != exclude.second.end(); ++i) {
-                reasons += *i;
-                if (i != --exclude.second.end()) {
-                    reasons += "\", \"";
-                }
-            }
-            reasons += "\")";
-            fprintf(stderr, "update_dyld_shared_cache: warning: %s rejected from cached dylibs: %s (%s)\n", fileSet.archs.name(), exclude.first.runtimePath.c_str(), reasons.c_str());
-            fileSet.otherDylibsAndBundles.push_back(exclude.first);
-        }
-
-        // check if cache is already up to date
-        if ( !force ) {
-            if ( existingCacheUpToDate(outFile, fileSet.dylibsForCache) )
-                return;
-        }
-
-         // add any extra dylibs needed which were not in .bom
-        fprintf(stderr, "update_dyld_shared_cache: %s incorporating %lu OS dylibs, tracking %lu others, building closures for %lu executables\n",
-                        fileSet.archs.name(), fileSet.dylibsForCache.size(), fileSet.otherDylibsAndBundles.size(), fileSet.mainExecutables.size());
-        //for (const DyldSharedCache::MappedMachO& aFile : fileSet.otherDylibsAndBundles) {
-        //    fprintf(stderr, "  %s\n", aFile.runtimePath.c_str());
-        //}
-
-
-        // build cache new cache file
-        DyldSharedCache::CreateOptions options;
-        options.outputFilePath               = outFile;
-        options.outputMapFilePath            = cacheDir + "/dyld_shared_cache_" + fileSet.archs.name() + ".map";
-        options.archs                        = &fileSet.archs;
-        options.platform                     = dyld3::Platform::macOS;
-        options.excludeLocalSymbols          = false;
-        options.optimizeStubs                = false;
-        options.optimizeObjC                 = true;
-        options.codeSigningDigestMode        = DyldSharedCache::SHA256only;
-        options.dylibsRemovedDuringMastering = dylibsRemoved;
-        options.inodesAreSameAsRuntime       = true;
-        options.cacheSupportsASLR            = (&fileSet.archs != &dyld3::GradedArchs::i386);
-        options.forSimulator                 = false;
-        options.isLocallyBuiltCache          = true;
-        options.verbose                      = verbose;
-        options.evictLeafDylibsOnOverflow    = true;
-        DyldSharedCache::CreateResults results = DyldSharedCache::create(options, fileSystem, fileSet.dylibsForCache, fileSet.otherDylibsAndBundles, fileSet.mainExecutables);
-
-        // print any warnings
-        for (const std::string& warn : results.warnings) {
-            fprintf(stderr, "update_dyld_shared_cache: warning: %s %s\n", fileSet.archs.name(), warn.c_str());
-        }
-        if ( results.errorMessage.empty() ) {
-            wroteSomeCacheFile = true;
-        }
-        else {
-            fprintf(stderr, "update_dyld_shared_cache: %s\n", results.errorMessage.c_str());
-            cacheBuildFailure = true;
-        }
-    });
-
-
-    // Save off spintrace data
-    if ( wroteSomeCacheFile ) {
-        void* h = dlopen("/usr/lib/libdscsym.dylib", 0);
-        if ( h != nullptr ) {
-            typedef int (*dscym_func)(const char*);
-            dscym_func func = (dscym_func)dlsym(h, "dscsym_save_dscsyms_for_current_caches");
-            std::string nuggetRoot = rootPath;
-            if ( nuggetRoot.empty() )
-                 nuggetRoot = overlayPath;
-            if ( nuggetRoot.empty() )
-                 nuggetRoot = "/";
-            (*func)(nuggetRoot.c_str());
-        }
-    }
-
-
-    // we could unmap all input files, but tool is about to quit
-
-    return (cacheBuildFailure ? 1 : 0);
+    fprintf(stderr, "This tool is deprecated.\n");
+    return 0;
 }
 
index 586ff5e6bac2a022e75d94f29a7b6ade7a2f4dd0..b1a680f65a619e5211dd8ac1e1630f42a12a9a03 100644 (file)
@@ -56,6 +56,7 @@
 #include <unordered_set>
 #include <iostream>
 #include <fstream>
+#include <sstream>
 
 #include "FileUtils.h"
 #include "StringUtils.h"
@@ -100,7 +101,7 @@ static const char* sMacOSBinaries[] = {
 static bool verbose = false;
 
 
-static bool addIfMachO(const dyld3::closure::FileSystem& fileSystem, const std::string& runtimePath, const struct stat& statBuf, std::vector<MappedMachOsByCategory>& files, dyld3::Platform platform)
+static bool addIfMachO(const dyld3::closure::FileSystem& fileSystem, const std::string& runtimePath, const struct stat& statBuf, std::vector<MappedMachOsByCategory>& files, dyld3::Platform platform, Diagnostics& diag)
 {
     // don't precompute closure info for any debug or profile dylibs
     if ( endsWith(runtimePath, "_profile.dylib") || endsWith(runtimePath, "_debug.dylib") || endsWith(runtimePath, "_asan.dylib") || endsWith(runtimePath, "_profile") || endsWith(runtimePath, "_debug") )
@@ -110,13 +111,12 @@ static bool addIfMachO(const dyld3::closure::FileSystem& fileSystem, const std::
 
     bool result = false;
     for (MappedMachOsByCategory& file : files) {
-        Diagnostics diag;
         char realerPath[MAXPATHLEN];
         dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, runtimePath.c_str(), file.archs, platform, realerPath);
         const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
 
         if ( ma != nullptr ) {
-            bool            sipProtected    = false; // isProtectedBySIP(fd);
+            bool            sipProtected    = false;
             bool            issetuid        = false;
             const uint64_t  sliceLen        = loadedFileInfo.sliceLen;
             if ( ma->isDynamicExecutable() ) {
@@ -126,7 +126,9 @@ static bool addIfMachO(const dyld3::closure::FileSystem& fileSystem, const std::
                 result = true;
         #endif
             }
-            else if ( ma->canBePlacedInDyldCache(runtimePath.c_str(), ^(const char* msg) {}) ) {
+            else if ( ma->canBePlacedInDyldCache(runtimePath.c_str(), ^(const char* msg) {
+                diag.error("Dylib located at '%s' cannot be placed in cache because: '%s'", loadedFileInfo.path, msg);
+            }) ) {
                 file.dylibsForCache.emplace_back(runtimePath, ma, sliceLen, issetuid, sipProtected, loadedFileInfo.sliceOffset, statBuf.st_mtime, statBuf.st_ino);
                 result = true;
            }
@@ -167,7 +169,8 @@ static void findAllFiles(const dyld3::closure::FileSystem& fileSystem, const std
                         return;
                 }
                 // if the file is mach-o, add to list
-                if ( addIfMachO(fileSystem, path, statBuf, files, platform) ) {
+                Diagnostics diag;
+                if ( addIfMachO(fileSystem, path, statBuf, files, platform, diag) ) {
                     if ( multiplePrefixes )
                         alreadyUsed.insert(path);
                 }
@@ -182,7 +185,10 @@ static void addMacOSHostLibs(std::vector<MappedMachOsByCategory>& allFileSets, d
     for (const char* path : sMacOSHostLibs) {
         struct stat statBuf;
         if ( stat(path, &statBuf) == 0 ) {
-            addIfMachO(fileSystem, path, statBuf, allFileSets, dyld3::Platform::macOS);
+            Diagnostics diag;
+            addIfMachO(fileSystem, path, statBuf, allFileSets, dyld3::Platform::macOS, diag);
+            if ( diag.hasError() )
+                fprintf(stderr, "update_dyld_sim_shared_cache: warning: skipping %s because %s\n", path, diag.errorMessage().c_str());
         }
     }
 }
@@ -194,7 +200,10 @@ static void addMacOSBinaries(const dyld3::closure::FileSystem& fileSystem, const
             std::string fullPath = prefix + path;
             struct stat statBuf;
             if ( stat(fullPath.c_str(), &statBuf) == 0 ) {
-                addIfMachO(fileSystem, path, statBuf, files, dyld3::Platform::macOS);
+                Diagnostics diag;
+                addIfMachO(fileSystem, path, statBuf, files, dyld3::Platform::macOS, diag);
+                if ( diag.hasError() )
+                    fprintf(stderr, "update_dyld_sim_shared_cache: warning: skipping %s because %s\n", fullPath.c_str(), diag.errorMessage().c_str());
             }
         }
     }
@@ -305,6 +314,14 @@ static void pruneOtherDylibs(const std::string& volumePrefix, MappedMachOsByCate
 }
 
 
+static std::string getOrderFileContent(const std::string& orderFile)
+{
+    std::ifstream fstream(orderFile);
+    std::stringstream stringBuf;
+    stringBuf << fstream.rdbuf();
+    return stringBuf.str();
+}
+
 static bool existingCacheUpToDate(const std::string& existingCache, const std::vector<DyldSharedCache::MappedMachO>& currentDylibs)
 {
     // if no existing cache, it is not up-to-date
@@ -363,6 +380,26 @@ static bool existingCacheUpToDate(const std::string& existingCache, const std::v
     return !foundMismatch;
 }
 
+static void addFileSets(const std::unordered_set<std::string>& allowedArchs, std::unordered_set<std::string>& requestedArchs, std::vector<MappedMachOsByCategory>& fileSets)
+{
+    if ( requestedArchs.empty() ) {
+#if __arm64__
+        requestedArchs.insert("arm64");
+#elif __x86_64__
+        requestedArchs.insert("x86_64");
+        requestedArchs.insert("i386");
+#else
+    #error unknown platform
+#endif
+    }
+
+    for (auto& requested : requestedArchs) {
+        if ( allowedArchs.find(requested) != allowedArchs.end() ) {
+            const dyld3::GradedArchs& archs = dyld3::GradedArchs::forName(requested.c_str(), false);
+            fileSets.push_back({archs});
+        }
+    }
+}
 
 inline uint32_t absolutetime_to_milliseconds(uint64_t abstime)
 {
@@ -388,6 +425,7 @@ int main(int argc, const char* argv[], const char* envp[])
     std::string                     dirtyDataOrderFile;
     dyld3::Platform                 platform = dyld3::Platform::iOS_simulator;
     std::unordered_set<std::string> skipDylibs;
+    std::unordered_set<std::string> requestedArchs;
     
     // parse command line options
     for (int i = 1; i < argc; ++i) {
@@ -426,6 +464,10 @@ int main(int argc, const char* argv[], const char* envp[])
             TERMINATE_IF_LAST_ARG("-dirty_data_order_file missing path argument\n");
             dirtyDataOrderFile = argv[++i];
         }
+        else if (strcmp(arg, "-arch") == 0) {
+            TERMINATE_IF_LAST_ARG("-arch missing arch argument\n");
+            requestedArchs.insert(argv[++i]);
+        }
         else if (strcmp(arg, "-force") == 0) {
             force = true;
         }
@@ -440,11 +482,11 @@ int main(int argc, const char* argv[], const char* envp[])
         }
     }
     
-    if ( rootPath.empty()) {
+    if ( rootPath.empty() ) {
         fprintf(stderr, "-root should be specified\n");
         return 1;
     }
-    if (cacheDir.empty()) {
+    if ( cacheDir.empty() ) {
         fprintf(stderr, "-cache_dir should be specified\n");
         return 1;
     }
@@ -453,6 +495,12 @@ int main(int argc, const char* argv[], const char* envp[])
     if ( realpath(rootPath.c_str(), resolvedPath) != NULL ) {
         rootPath = resolvedPath;
     }
+    
+    // canonicalize cacheDir.
+    // Later, path is checked against real path name before writing cache file to avoid TOCTU race condition.
+    if ( realpath(cacheDir.c_str(), resolvedPath) != NULL ) {
+        cacheDir = resolvedPath;
+    }
 
     // Find the boot volume so that we can ensure all overlays are on the same volume
     struct stat rootStatBuf;
@@ -476,23 +524,45 @@ int main(int argc, const char* argv[], const char* envp[])
         return 1;
     }
 
+    std::string dylibOrderFileContent;
+    if ( !dylibOrderFile.empty() ) {
+        dylibOrderFileContent = getOrderFileContent(dylibOrderFile);
+    }
+
+    std::string dirtyDataOrderFileContent;
+    if ( !dirtyDataOrderFile.empty() ) {
+        dirtyDataOrderFileContent = getOrderFileContent(dirtyDataOrderFile);
+    }
     uint64_t t1 = mach_absolute_time();
 
     __block std::vector<MappedMachOsByCategory> allFileSets;
+    std::unordered_set<std::string> allowedArchs;
     switch ( platform ) {
         case dyld3::Platform::iOS_simulator:
-            allFileSets.push_back({dyld3::GradedArchs::x86_64});
+            allowedArchs.insert("x86_64");
+            allowedArchs.insert("arm64");
             break;
         case dyld3::Platform::watchOS_simulator:
-            allFileSets.push_back({dyld3::GradedArchs::i386});
+            allowedArchs.insert("i386");
+            allowedArchs.insert("x86_64");
+            allowedArchs.insert("arm64");
+
             break;
         case dyld3::Platform::tvOS_simulator:
-            allFileSets.push_back({dyld3::GradedArchs::x86_64});
+            allowedArchs.insert("x86_64");
+            allowedArchs.insert("arm64");
             break;
         default:
             assert(0 && "invalid platform");
             break;
     }
+
+    addFileSets(allowedArchs, requestedArchs, allFileSets);
+    if ( allFileSets.empty() ) {
+        fprintf(stderr, "update_dyld_sim_shared_cache: error: no valid architecture specified\n");
+        return 1;
+    }
+
     findAllFiles(fileSystem, pathPrefixes, allFileSets, platform);
     addMacOSHostLibs(allFileSets, platform);
     addMacOSBinaries(fileSystem, pathPrefixes, allFileSets);
@@ -526,7 +596,7 @@ int main(int argc, const char* argv[], const char* envp[])
         MappedMachOsByCategory& fileSet = allFileSets[index];
         const std::string outFile = cacheDir + "/dyld_sim_shared_cache_" + fileSet.archs.name();
         
-        DyldSharedCache::MappedMachO (^loader)(const std::string&) = ^DyldSharedCache::MappedMachO(const std::string& runtimePath) {
+        DyldSharedCache::MappedMachO (^loader)(const std::string&, Diagnostics&) = ^DyldSharedCache::MappedMachO(const std::string& runtimePath, Diagnostics& diag) {
             if ( skipDylibs.count(runtimePath) )
                 return DyldSharedCache::MappedMachO();
 
@@ -554,7 +624,7 @@ int main(int argc, const char* argv[], const char* envp[])
                     
                     std::vector<MappedMachOsByCategory> mappedFiles;
                     mappedFiles.push_back({fileSet.archs});
-                    if ( addIfMachO(fileSystem, runtimePath, statBuf, mappedFiles, platform) ) {
+                    if ( addIfMachO(fileSystem, runtimePath, statBuf, mappedFiles, platform, diag) ) {
                         if ( !mappedFiles.back().dylibsForCache.empty() ) {
                             if ( verbose )
                                 fprintf(stderr, "verifySelfContained, add %s\n", mappedFiles.back().dylibsForCache.back().runtimePath.c_str());
@@ -581,7 +651,7 @@ int main(int argc, const char* argv[], const char* envp[])
                 }
             }
             reasons += "\")";
-            fprintf(stderr, "update_dyld_shared_cache: warning: %s rejected from cached dylibs: %s (%s)\n", fileSet.archs.name(), exclude.first.runtimePath.c_str(), reasons.c_str());
+            fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s rejected from cached dylibs: %s (%s)\n", fileSet.archs.name(), exclude.first.runtimePath.c_str(), reasons.c_str());
             fileSet.otherDylibsAndBundles.push_back(exclude.first);
         }
         
@@ -598,9 +668,10 @@ int main(int argc, const char* argv[], const char* envp[])
         options.outputMapFilePath            = cacheDir + "/dyld_sim_shared_cache_" + fileSet.archs.name() + ".map";
         options.archs                        = &fileSet.archs;
         options.platform                     = platform;
-        options.excludeLocalSymbols          = false;
+        options.localSymbolMode              = DyldSharedCache::LocalSymbolsMode::keep;
         options.optimizeStubs                = false;
-        options.optimizeObjC                 = true;
+        options.optimizeDyldDlopens          = false;   // don't add dyld3 closures to simulator cache
+        options.optimizeDyldLaunches         = false;   // don't add dyld3 closures to simulator cache
         options.codeSigningDigestMode        = DyldSharedCache::SHA256only;
         options.dylibsRemovedDuringMastering = dylibsRemoved;
         options.inodesAreSameAsRuntime       = true;
@@ -609,8 +680,8 @@ int main(int argc, const char* argv[], const char* envp[])
         options.isLocallyBuiltCache          = true;
         options.verbose                      = verbose;
         options.evictLeafDylibsOnOverflow    = true;
-        options.dylibOrdering                = parseOrderFile(dylibOrderFile);
-        options.dirtyDataSegmentOrdering     = parseOrderFile(dirtyDataOrderFile);
+        options.dylibOrdering                = parseOrderFile(dylibOrderFileContent);
+        options.dirtyDataSegmentOrdering     = parseOrderFile(dirtyDataOrderFileContent);
         DyldSharedCache::CreateResults results = DyldSharedCache::create(options, fileSystem, fileSet.dylibsForCache, fileSet.otherDylibsAndBundles, fileSet.mainExecutables);
         
         // print any warnings
diff --git a/include/dlfcn_private.h b/include/dlfcn_private.h
new file mode 100644 (file)
index 0000000..3f55277
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, 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 _DLFCN_PRIVATE_H_
+#define _DLFCN_PRIVATE_H_
+
+#include <dlfcn.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Internal interface for dlopen; intended to help audit internal use of
+ * dlopen.
+ */
+#if __i386__
+/*
+ * i386 makes tail calls difficult, and RTLD_NEXT requires that dlopen_audited
+ * tail call dlopen.  Just directly call dlopen on i386.
+ */
+#define dlopen_audited(__path, __mode) dlopen(__path, __mode)
+#else
+extern void * dlopen_audited(const char * __path, int __mode) __DYLDDL_DRIVERKIT_UNAVAILABLE;
+#endif
+
+
+/*
+ * Sometimes dlopen() looks at who called it (such as for @rpath and @loader_path).
+ * This SPI allows you to simulate dlopen() being called by other code.
+ * Available in macOS 11.0 and iOS 14.0 and later.
+ */
+extern void* dlopen_from(const char* __path, int __mode, void* __addressInCaller) __DYLDDL_DRIVERKIT_UNAVAILABLE;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DLFCN_PRIVATE_H_ */
index cc8c89ff2fdf19d6943a6f495bd38df7887b29f4..a2e641c4b6176398b63e90d62f36118bb187e91a 100644 (file)
@@ -112,6 +112,15 @@ extern void _tlv_atexit(void (*termFunc)(void* objAddr), void* objAddr)      __O
  */
 extern void _tlv_bootstrap(void)                                             __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) DYLD_DRIVERKIT_UNAVAILABLE ;
 
+
+/*
+ * Dylibs that are incorporated into the dyld cache are removed from disk. That means code
+ * cannot stat() the file to see if it "exists".  This function is like a stat() call that checks if a
+ * path is to a dylib that was removed from disk and is incorporated into the active dyld cache.
+ */
+extern bool _dyld_shared_cache_contains_path(const char* path)               __API_AVAILABLE(macos(11.0), ios(14.0), watchos(7.0), tvos(14.0), bridgeos(5.0)) DYLD_DRIVERKIT_UNAVAILABLE;
+
+
 /*
  * The following dyld API's are deprecated as of Mac OS X 10.5.  They are either  
  * no longer necessary or are superceeded by dlopen and friends in <dlfcn.h>.
diff --git a/include/mach-o/dyld.modulemap b/include/mach-o/dyld.modulemap
new file mode 100644 (file)
index 0000000..02bb277
--- /dev/null
@@ -0,0 +1,4 @@
+module MachO.dyld [system] [extern_c] {
+  header "dyld.h"
+  export *
+}
index 1c7ba7f0fefc6f231ba185095ff9a5842d40dbe7..dcf2dc891dc1dec14352e55314dff95654c454f8 100644 (file)
@@ -84,6 +84,19 @@ struct dyld_uuid_info {
        uuid_t                                          imageUUID;                      /* UUID of image */
 };
 
+#define DYLD_AOT_IMAGE_KEY_SIZE 32
+struct dyld_aot_image_info {
+    const struct mach_header*   x86LoadAddress;
+    const struct mach_header*   aotLoadAddress;
+    const uint64_t              aotImageSize;
+    const uint8_t               aotImageKey[DYLD_AOT_IMAGE_KEY_SIZE]; // uniquely identifying SHA-256 key for this aot
+};
+
+struct dyld_aot_shared_cache_info {
+    const uintptr_t cacheBaseAddress;
+    uuid_t          cacheUUID;
+};
+
 typedef void (*dyld_image_notifier)(enum dyld_image_mode mode, uint32_t infoCount, const struct dyld_image_info info[]);
 
 /* for use in dyld_all_image_infos.errorKind field */
@@ -154,7 +167,14 @@ struct dyld_all_image_infos {
        /* the following field is only in version 16 (macOS 10.13, iOS 11.0) and later */
     uintptr_t                       compact_dyld_image_info_addr;
     size_t                          compact_dyld_image_info_size;
-    uint32_t                        platform; // FIXME: really a dyld_platform_t, but those aren't exposed here. 
+    uint32_t                        platform; // FIXME: really a dyld_platform_t, but those aren't exposed here.
+
+    /* the following field is only in version 17 (macOS 10.16) and later */
+    uint32_t                          aotInfoCount;
+    const struct dyld_aot_image_info* aotInfoArray;
+    uint64_t                          aotInfoArrayChangeTimestamp;
+    uintptr_t                         aotSharedCacheBaseAddress;
+    uint8_t                           aotSharedCacheUUID[16];
 };
 
 /*
index 6013e59efac8ef0bb09c4c92d36f53441f222223..dede2fba4e589d3a85497213aa8dbb5692c0ae51 100644 (file)
@@ -113,6 +113,12 @@ 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);
 
+//
+// Return the mach header of the process
+//
+// Exists in Mac OS X 10.16 and later
+extern const struct mach_header* _dyld_get_prog_image_header(void);
+
 typedef uint32_t dyld_platform_t;
 
 typedef struct {
@@ -125,6 +131,28 @@ extern dyld_platform_t dyld_get_active_platform(void) __API_AVAILABLE(macos(10.1
 
 // 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
+
+// It is intended that most code in the OS will use the version set constants, which will correctly deal with secret and future
+// platforms. For example:
+
+//  if (dyld_program_sdk_at_least(dyld_fall_2018_os_versions)) {
+//      New behaviour for programs built against the iOS 12, tvOS 12, watchOS 5, macOS 10.14, or bridgeOS 3 (or newer) SDKs
+//  } else {
+//      Old behaviour
+//  }
+
+// In cases where more precise control is required (such as APIs that were added to varions platforms in different years)
+// the os specific values may be used instead. Unlike the version set constants, the platform specific ones will only ever
+// return true if the running binary is the platform being testsed, allowing conditions to be built for specific platforms
+// and releases that came out at different times. For example:
+
+//  if (dyld_program_sdk_at_least(dyld_platform_version_iOS_12_0)
+//      || dyld_program_sdk_at_least(dyld_platform_version_watchOS_6_0)) {
+//      New behaviour for programs built against the iOS 12 (fall 2018), watchOS 6 (fall 2019) (or newer) SDKs
+//  } else {
+//      Old behaviour all other platforms, as well as older iOSes and watchOSes
+//  }
+
 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
@@ -149,29 +177,16 @@ extern void dyld_get_image_versions(const struct mach_header* mh, void (^callbac
 
 // 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.
-
-//@MAC_VERSION_DEFS@
-
-//@IOS_VERSION_DEFS@
-
-//@WATCHOS_VERSION_DEFS@
+// Because we now have so many different OSes with different versions these version set values are intended to
+// to provide a more convenient way to version check. They may be used instead of platform specific version in
+// dyld_sdk_at_least(), dyld_minos_at_least(), dyld_program_sdk_at_least(), and dyld_program_minos_at_least().
+// Since they are references into a lookup table they MUST NOT be used by any code that does not ship as part of
+// the OS, as the values may change and the tables in older OSes may not have the necessary values for back
+// deployed binaries. These values are future proof against new platforms being added, and any checks against
+// platforms that did not exist at the epoch of a version set will return true since all versions of that platform
+// are inherently newer.
 
-//@TVOS_VERSION_DEFS@
-
-//@BRIDGEOS_VERSION_DEFS@
+//@VERSION_DEFS@
 
 //
 // This finds the SDK version a binary was built against.
@@ -254,6 +269,8 @@ extern bool dyld_shared_cache_some_image_overridden(void);
        
 //
 // Returns if the process is setuid or is code signed with entitlements.
+// NOTE: It is safe to call this prior to malloc being initialized.  This function
+// is guaranteed to not call malloc, or depend on its state.
 //
 // Exists in Mac OS X 10.9 and later
 extern bool dyld_process_is_restricted(void);
@@ -274,6 +291,14 @@ extern const char* dyld_shared_cache_file_path(void);
 // Exists in Mac OS X 10.15 and later
 extern bool dyld_has_inserted_or_interposing_libraries(void);
 
+//
+// Return true if dyld contains a fix for a specific identifier. Intended for staging breaking SPI
+// changes
+//
+// Exists in macOS 10.16, iOS 14, tvOS14, watchOS 7 and later
+
+extern bool _dyld_has_fix_for_radar(const char *rdar);
+
 
 //
 // <rdar://problem/13820686> for OpenGL to tell dyld it is ok to deallocate a memory based image when done.
@@ -297,7 +322,7 @@ extern void dyld_dynamic_interpose(const struct mach_header* mh, const struct dy
 
 
 struct dyld_shared_cache_dylib_text_info {
-       uint64_t                version;                // current version 1
+       uint64_t                version;                // current version 2
        // following fields all exist in version 1
        uint64_t                loadAddressUnslid;
        uint64_t                textSegmentSize; 
@@ -387,7 +412,7 @@ extern bool _dyld_shared_cache_is_locally_built(void);
 //
 // Exists in Mac OS X 10.15 and later
 // Exists in iOS 13.0 and later
-extern bool dyld_need_closure(const char* execPath, const char* tempDir);
+extern bool dyld_need_closure(const char* execPath, const char* dataContainerRootDir);
 
 
 struct dyld_image_uuid_offset {
@@ -438,6 +463,30 @@ extern void _dyld_register_for_bulk_image_loads(void (*func)(unsigned imageCount
 extern void _dyld_register_driverkit_main(void (*mainFunc)(void));
 
 
+//
+// This is similar to _dyld_shared_cache_contains_path(), except that it returns the canonical
+// shared cache path for the given path.
+//
+// Exists in macOS 10.16 and later
+// Exists in iOS 14.0 and later
+extern const char* _dyld_shared_cache_real_path(const char* path);
+
+
+//
+// Dyld has a number of modes. This function returns the mode for the current process.
+// dyld2 is the classic "interpreter" way to run.
+// dyld3 runs by compiling down and caching what dyld needs to do into a "closure".
+//
+// Exists in macOS 10.16 and later
+// Exists in iOS 14.0 and later
+//
+#define DYLD_LAUNCH_MODE_USING_CLOSURE               0x00000001     // if 0, then running in classic dyld2 mode
+#define DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH     0x00000002     // launch was slow, to build closure
+#define DYLD_LAUNCH_MODE_CLOSURE_SAVED_TO_FILE       0x00000004     // next launch will be faster
+#define DYLD_LAUNCH_MODE_CLOSURE_FROM_OS             0x00000008     // closure built into dyld cache
+#define DYLD_LAUNCH_MODE_MINIMAL_CLOSURE             0x00000010     // closure does not contain fix ups
+extern uint32_t _dyld_launch_mode(void);
+
 
 //
 // When dyld must terminate a process because of a required dependent dylib
@@ -526,6 +575,18 @@ extern void _dyld_for_each_objc_protocol(const char* protocolName,
 // objects are destroyed before global objects.
 extern void _tlv_exit(void);
 
+typedef enum {
+    dyld_objc_string_kind
+} DyldObjCConstantKind;
+
+// CF constants such as CFString's can be moved in to a contiguous range of
+// shared cache memory.  This returns true if the given pointer is to an object of
+// the given kind.
+//
+// Exists in Mac OS X 10.16 and later
+// Exists in iOS 14.0 and later
+extern bool _dyld_is_objc_constant(DyldObjCConstantKind kind, const void* addr);
+
 
 // temp exports to keep tapi happy, until ASan stops using dyldVersionNumber
 extern double      dyldVersionNumber;
index 5111f5c4098f0b4b58a73a35fe19d8a78ff18dc3..4f23a3ee815d73e0634eb28316b4f74e25e25114 100644 (file)
@@ -62,6 +62,12 @@ struct dyld_process_cache_info {
 };
 typedef struct dyld_process_cache_info dyld_process_cache_info;
 
+struct dyld_process_aot_cache_info {
+    uuid_t      cacheUUID;
+    uint64_t    cacheBaseAddress;
+};
+typedef struct dyld_process_aot_cache_info dyld_process_aot_cache_info;
+
 enum {
     dyld_process_state_not_started              = 0x00,     // process is suspended, dyld has not started running yet
     dyld_process_state_dyld_initialized         = 0x10,     // dyld has initialzed itself
@@ -105,9 +111,15 @@ extern void  _dyld_process_info_get_state(dyld_process_info info, dyld_process_s
 // fill in struct with info about dyld cache in use by process
 extern void  _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_info* cacheInfo);
 
+// fill in struct with info about aot cache in use by process
+extern void  _dyld_process_info_get_aot_cache(dyld_process_info info, dyld_process_aot_cache_info* aotCacheInfo);
+
 // iterate all images in process
 extern void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path));
 
+// iterate all aot images in process
+extern void _dyld_process_info_for_each_aot_image(dyld_process_info info, bool (^callback)(uint64_t x86Address, uint64_t aotAddress, uint64_t aotSize, uint8_t* aotImageKey, size_t aotImageKeySize))  __API_UNAVAILABLE(ios, tvos, watchos) __API_UNAVAILABLE(bridgeos);
+
 // iterate all segments in an image
 extern void _dyld_process_info_for_each_segment(dyld_process_info info, uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName));
 
index 2aba7bf6ab8aa293e77e491bca317a4dfeb984c1..601ffc051f4f0ef1138ae186982de332bfea1456 100644 (file)
@@ -23,7 +23,7 @@
  */
 
 #ifndef __MACH_O_FIXUP_CHAINS__
-#define __MACH_O_FIXUP_CHAINS__ 4
+#define __MACH_O_FIXUP_CHAINS__ 6
 
 
 #include <stdint.h>
@@ -88,17 +88,19 @@ struct dyld_chained_starts_offsets
 
 // values for dyld_chained_starts_in_segment.pointer_format
 enum {
-    DYLD_CHAINED_PTR_ARM64E             =  1,    // stride 8, unauth target is vmaddr
-    DYLD_CHAINED_PTR_64                 =  2,    // target is vmaddr
-    DYLD_CHAINED_PTR_32                 =  3,
-    DYLD_CHAINED_PTR_32_CACHE           =  4,
-    DYLD_CHAINED_PTR_32_FIRMWARE        =  5,
-    DYLD_CHAINED_PTR_64_OFFSET          =  6,    // target is vm offset
-    DYLD_CHAINED_PTR_ARM64E_OFFSET      =  7,    // old name
-    DYLD_CHAINED_PTR_ARM64E_KERNEL      =  7,    // stride 4, unauth target is vm offset
-    DYLD_CHAINED_PTR_64_KERNEL_CACHE    =  8,
-    DYLD_CHAINED_PTR_ARM64E_USERLAND    =  9,    // stride 8, unauth target is vm offset
-    DYLD_CHAINED_PTR_ARM64E_FIRMWARE    = 10,    // stride 4, unauth target is vmaddr
+    DYLD_CHAINED_PTR_ARM64E                 =  1,    // stride 8, unauth target is vmaddr
+    DYLD_CHAINED_PTR_64                     =  2,    // target is vmaddr
+    DYLD_CHAINED_PTR_32                     =  3,
+    DYLD_CHAINED_PTR_32_CACHE               =  4,
+    DYLD_CHAINED_PTR_32_FIRMWARE            =  5,
+    DYLD_CHAINED_PTR_64_OFFSET              =  6,    // target is vm offset
+    DYLD_CHAINED_PTR_ARM64E_OFFSET          =  7,    // old name
+    DYLD_CHAINED_PTR_ARM64E_KERNEL          =  7,    // stride 4, unauth target is vm offset
+    DYLD_CHAINED_PTR_64_KERNEL_CACHE        =  8,
+    DYLD_CHAINED_PTR_ARM64E_USERLAND        =  9,    // stride 8, unauth target is vm offset
+    DYLD_CHAINED_PTR_ARM64E_FIRMWARE        = 10,    // stride 4, unauth target is vmaddr
+    DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE    = 11,    // stride 1, x86_64 kernel caches
+    DYLD_CHAINED_PTR_ARM64E_USERLAND24      = 12,    // stride 8, unauth target is vm offset, 24-bit bind
 };
 
 
@@ -158,6 +160,32 @@ struct dyld_chained_ptr_64_rebase
                 bind      :  1;    // == 0
 };
 
+
+// DYLD_CHAINED_PTR_ARM64E_USERLAND24
+struct dyld_chained_ptr_arm64e_bind24
+{
+    uint64_t    ordinal   : 24,
+                zero      :  8,
+                addend    : 19,    // +/-256K
+                next      : 11,    // 8-byte stide
+                bind      :  1,    // == 1
+                auth      :  1;    // == 0
+};
+
+// DYLD_CHAINED_PTR_ARM64E_USERLAND24
+struct dyld_chained_ptr_arm64e_auth_bind24
+{
+    uint64_t    ordinal   : 24,
+                zero      :  8,
+                diversity : 16,
+                addrDiv   :  1,
+                key       :  2,
+                next      : 11,    // 8-byte stide
+                bind      :  1,    // == 1
+                auth      :  1;    // == 1
+};
+
+
 // DYLD_CHAINED_PTR_64
 struct dyld_chained_ptr_64_bind
 {
@@ -168,7 +196,7 @@ struct dyld_chained_ptr_64_bind
                 bind      :  1;   // == 1
 };
 
-// DYLD_CHAINED_PTR_64_KERNEL_CACHE
+// DYLD_CHAINED_PTR_64_KERNEL_CACHE, DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE
 struct dyld_chained_ptr_64_kernel_cache_rebase
 {
     uint64_t    target     : 30,   // basePointers[cacheLevel] + target
@@ -176,7 +204,7 @@ struct dyld_chained_ptr_64_kernel_cache_rebase
                 diversity  : 16,
                 addrDiv    :  1,
                 key        :  2,
-                next       : 12,    // 4-byte stide
+                next       : 12,    // 1 or 4-byte stide
                 isAuth     :  1;    // 0 -> not authenticated.  1 -> authenticated
 };
 
diff --git a/launch-cache/Architectures.hpp b/launch-cache/Architectures.hpp
deleted file mode 100644 (file)
index 2da76e5..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 
- *
- * Copyright (c) 2005-2011 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
-#ifndef __ARCHITECTURES__
-#define __ARCHITECTURES__
-
-#include "FileAbstraction.hpp"
-
-
-//
-// Architectures
-//
-struct x86
-{
-       typedef Pointer32<LittleEndian>         P;
-       
-};
-
-struct x86_64
-{
-       typedef Pointer64<LittleEndian>         P;
-};
-
-struct arm
-{
-       typedef Pointer32<LittleEndian>         P;
-       
-};
-
-struct arm64
-{
-       typedef Pointer64<LittleEndian>         P;
-
-};
-
-struct arm64_32
-{
-       typedef Pointer32<LittleEndian>         P;
-
-};
-
-
-
-
-#endif // __ARCHITECTURES__
-
-
diff --git a/launch-cache/CacheFileAbstraction.hpp b/launch-cache/CacheFileAbstraction.hpp
deleted file mode 100644 (file)
index e229bb3..0000000
+++ /dev/null
@@ -1,442 +0,0 @@
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 
- *
- * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
-#ifndef __DYLD_CACHE_ABSTRACTION__
-#define __DYLD_CACHE_ABSTRACTION__
-
-#include "dyld_cache_format.h"
-
-#include "FileAbstraction.hpp"
-#include "Architectures.hpp"
-
-template <typename E>
-class dyldCacheHeader {
-public:
-       const char*             magic() const                                                   INLINE { return fields.magic; }
-       void                    set_magic(const char* value)                    INLINE { memcpy(fields.magic, value, 16); }
-
-       uint32_t                mappingOffset() const                                   INLINE { return E::get32(fields.mappingOffset); }
-       void                    set_mappingOffset(uint32_t value)               INLINE { E::set32(fields.mappingOffset, value); }
-
-       uint32_t                mappingCount() const                                    INLINE { return E::get32(fields.mappingCount); }
-       void                    set_mappingCount(uint32_t value)                INLINE { E::set32(fields.mappingCount, value); }
-
-       uint32_t                imagesOffset() const                                    INLINE { return E::get32(fields.imagesOffset); }
-       void                    set_imagesOffset(uint32_t value)                INLINE { E::set32(fields.imagesOffset, value); }
-
-       uint32_t                imagesCount() const                                             INLINE { return E::get32(fields.imagesCount); }
-       void                    set_imagesCount(uint32_t value)                 INLINE { E::set32(fields.imagesCount, value); }
-
-       uint64_t                dyldBaseAddress() const                                 INLINE { return E::get64(fields.dyldBaseAddress); }
-       void                    set_dyldBaseAddress(uint64_t value)             INLINE { E::set64(fields.dyldBaseAddress, value); }
-
-       uint64_t                codeSignatureOffset() const                             INLINE { return E::get64(fields.codeSignatureOffset); }
-       void                    set_codeSignatureOffset(uint64_t value) INLINE { E::set64(fields.codeSignatureOffset, value); }
-
-       uint64_t                codeSignatureSize() const                               INLINE { return E::get64(fields.codeSignatureSize); }
-       void                    set_codeSignatureSize(uint64_t value)   INLINE { E::set64(fields.codeSignatureSize, value); }
-
-       uint64_t                slideInfoOffset() const                                 INLINE { return E::get64(fields.slideInfoOffset); }
-       void                    set_slideInfoOffset(uint64_t value)             INLINE { E::set64(fields.slideInfoOffset, value); }
-
-       uint64_t                slideInfoSize() const                                   INLINE { return E::get64(fields.slideInfoSize); }
-       void                    set_slideInfoSize(uint64_t value)               INLINE { E::set64(fields.slideInfoSize, value); }
-
-       uint64_t                localSymbolsOffset() const                              INLINE { return E::get64(fields.localSymbolsOffset); }
-       void                    set_localSymbolsOffset(uint64_t value)  INLINE { E::set64(fields.localSymbolsOffset, value); }
-
-       uint64_t                localSymbolsSize() const                                INLINE { return E::get64(fields.localSymbolsSize); }
-       void                    set_localSymbolsSize(uint64_t value)    INLINE { E::set64(fields.localSymbolsSize, value); }
-
-       const uint8_t*  uuid() const                                                    INLINE { return fields.uuid; }
-       void                    set_uuid(const uint8_t value[16])               INLINE { memcpy(fields.uuid, value, 16); }
-       
-       uint64_t                cacheType() const                                               INLINE { return E::get64(fields.cacheType); }
-       void                    set_cacheType(uint64_t value)                   INLINE { E::set64(fields.cacheType, value); }
-
-       uint32_t                branchPoolsOffset() const                               INLINE { return E::get32(fields.branchPoolsOffset); }
-       void                    set_branchPoolsOffset(uint32_t value)   INLINE { E::set32(fields.branchPoolsOffset, value); }
-
-       uint32_t                branchPoolsCount() const                                INLINE { return E::get32(fields.branchPoolsCount); }
-       void                    set_branchPoolsCount(uint32_t value)    INLINE { E::set32(fields.branchPoolsCount, value); }
-
-       uint64_t                accelerateInfoAddr() const                              INLINE { return E::get64(fields.accelerateInfoAddr); }
-       void                    set_accelerateInfoAddr(uint64_t value)  INLINE { E::set64(fields.accelerateInfoAddr, value); }
-
-       uint64_t                accelerateInfoSize() const                              INLINE { return E::get64(fields.accelerateInfoSize); }
-       void                    set_accelerateInfoSize(uint64_t value)  INLINE { E::set64(fields.accelerateInfoSize, value); }
-
-       uint64_t                imagesTextOffset() const                                INLINE { return E::get64(fields.imagesTextOffset); }
-       void                    set_imagesTextOffset(uint64_t value)    INLINE { E::set64(fields.imagesTextOffset, value); }
-
-       uint64_t                imagesTextCount() const                                 INLINE { return E::get64(fields.imagesTextCount); }
-       void                    set_imagesTextCount(uint64_t value)             INLINE { E::set64(fields.imagesTextCount, value); }
-
-private:
-       dyld_cache_header                       fields;
-};
-
-
-
-template <typename E>
-class dyldCacheFileMapping {
-public:                
-       uint64_t                address() const                                                         INLINE { return E::get64(fields.address); }
-       void                    set_address(uint64_t value)                                     INLINE { E::set64(fields.address, value); }
-
-       uint64_t                size() const                                                            INLINE { return E::get64(fields.size); }
-       void                    set_size(uint64_t value)                                        INLINE { E::set64(fields.size, value); }
-
-       uint64_t                file_offset() const                                                     INLINE { return E::get64(fields.fileOffset); }
-       void                    set_file_offset(uint64_t value)                         INLINE { E::set64(fields.fileOffset, value); }
-
-       uint32_t                max_prot() const                                                        INLINE { return E::get32(fields.maxProt); }
-       void                    set_max_prot(uint32_t value)                            INLINE { E::set32((uint32_t&)fields.maxProt, value); }
-
-       uint32_t                init_prot() const                                                       INLINE { return E::get32(fields.initProt); }
-       void                    set_init_prot(uint32_t value)                           INLINE { E::set32((uint32_t&)fields.initProt, value); }
-
-private:
-       dyld_cache_mapping_info                 fields;
-};
-
-
-template <typename E>
-class dyldCacheImageInfo {
-public:                
-       uint64_t                address() const                                                         INLINE { return E::get64(fields.address); }
-       void                    set_address(uint64_t value)                                     INLINE { E::set64(fields.address, value); }
-
-       uint64_t                modTime() const                                                         INLINE { return E::get64(fields.modTime); }
-       void                    set_modTime(uint64_t value)                                     INLINE { E::set64(fields.modTime, value); }
-
-       uint64_t                inode() const                                                           INLINE { return E::get64(fields.inode); }
-       void                    set_inode(uint64_t value)                                       INLINE { E::set64(fields.inode, value); }
-
-       uint32_t                pathFileOffset() const                                          INLINE { return E::get32(fields.pathFileOffset); }
-       void                    set_pathFileOffset(uint32_t value)                      INLINE { E::set32(fields.pathFileOffset, value); fields.pad=0; }
-
-private:
-       dyld_cache_image_info                   fields;
-};
-
-template <typename E>
-class dyldCacheImageTextInfo {
-public:                
-       const uint8_t*  uuid() const                                                            INLINE { return fields.uuid; }
-       void                    set_uuid(const uint8_t value[16])                       INLINE { memcpy(fields.uuid, value, 16); }
-
-       uint64_t                loadAddress() const                                                     INLINE { return E::get64(fields.loadAddress); }
-       void                    set_loadAddress(uint64_t value)                         INLINE { E::set64(fields.loadAddress, value); }
-
-       uint32_t                textSegmentSize() const                                         INLINE { return E::get32(fields.textSegmentSize); }
-       void                    set_textSegmentSize(uint32_t value)                     INLINE { E::set32(fields.textSegmentSize, value); }
-
-       uint32_t                pathOffset() const                                                      INLINE { return E::get32(fields.pathOffset); }
-       void                    set_pathOffset(uint32_t value)                          INLINE { E::set32(fields.pathOffset, value); }
-
-private:
-       dyld_cache_image_text_info                      fields;
-};
-
-
-
-template <typename E>
-class dyldCacheImageInfoExtra {
-public:                
-       uint64_t                exportsTrieAddr() const                                                 INLINE { return E::get64(fields.exportsTrieAddr); }
-       void                    set_exportsTrieAddr(uint64_t value)                             INLINE { E::set64(fields.exportsTrieAddr, value); }
-
-       uint64_t                weakBindingsAddr() const                                                INLINE { return E::get64(fields.weakBindingsAddr); }
-       void                    set_weakBindingsAddr(uint64_t value)                    INLINE { E::set64(fields.weakBindingsAddr, value); }
-
-       uint32_t                exportsTrieSize() const                                                 INLINE { return E::get32(fields.exportsTrieSize); }
-       void                    set_exportsTrieSize(uint32_t value)                             INLINE { E::set32(fields.exportsTrieSize, value); }
-
-       uint32_t                weakBindingsSize() const                                                INLINE { return E::get32(fields.weakBindingsSize); }
-       void                    set_weakBindingsSize(uint32_t value)                    INLINE { E::set32(fields.weakBindingsSize, value); }
-
-       uint32_t                dependentsStartArrayIndex() const                               INLINE { return E::get32(fields.dependentsStartArrayIndex); }
-       void                    set_dependentsStartArrayIndex(uint32_t value)   INLINE { E::set32(fields.dependentsStartArrayIndex, value); }
-
-       uint32_t                reExportsStartArrayIndex() const                                INLINE { return E::get32(fields.reExportsStartArrayIndex); }
-       void                    set_reExportsStartArrayIndex(uint32_t value)    INLINE { E::set32(fields.reExportsStartArrayIndex, value); }
-
-private:
-       dyld_cache_image_info_extra                     fields;
-};
-
-
-template <typename E>
-class dyldCacheAcceleratorInfo {
-public:                
-       uint32_t                version() const                                                         INLINE { return E::get32(fields.version); }
-       void                    set_version(uint32_t value)                                     INLINE { E::set32(fields.version, value); }
-
-       uint32_t                imageExtrasCount() const                                        INLINE { return E::get32(fields.imageExtrasCount); }
-       void                    set_imageExtrasCount(uint32_t value)            INLINE { E::set32(fields.imageExtrasCount, value); }
-
-       uint32_t                imagesExtrasOffset() const                                      INLINE { return E::get32(fields.imagesExtrasOffset); }
-       void                    set_imagesExtrasOffset(uint32_t value)          INLINE { E::set32(fields.imagesExtrasOffset, value); }
-
-       uint32_t                bottomUpListOffset() const                                      INLINE { return E::get32(fields.bottomUpListOffset); }
-       void                    set_bottomUpListOffset(uint32_t value)          INLINE { E::set32(fields.bottomUpListOffset, value); }
-
-       uint32_t                dylibTrieOffset() const                                         INLINE { return E::get32(fields.dylibTrieOffset); }
-       void                    set_dylibTrieOffset(uint32_t value)                     INLINE { E::set32(fields.dylibTrieOffset, value); }
-
-       uint32_t                dylibTrieSize() const                                           INLINE { return E::get32(fields.dylibTrieSize); }
-       void                    set_dylibTrieSize(uint32_t value)                       INLINE { E::set32(fields.dylibTrieSize, value); }
-
-       uint32_t                initializersOffset() const                                      INLINE { return E::get32(fields.initializersOffset); }
-       void                    set_initializersOffset(uint32_t value)          INLINE { E::set32(fields.initializersOffset, value); }
-
-       uint32_t                initializersCount() const                                       INLINE { return E::get32(fields.initializersCount); }
-       void                    set_initializersCount(uint32_t value)           INLINE { E::set32(fields.initializersCount, value); }
-
-       uint32_t                dofSectionsOffset() const                                       INLINE { return E::get32(fields.dofSectionsOffset); }
-       void                    set_dofSectionsOffset(uint32_t value)           INLINE { E::set32(fields.dofSectionsOffset, value); }
-
-       uint32_t                dofSectionsCount() const                                        INLINE { return E::get32(fields.dofSectionsCount); }
-       void                    set_dofSectionsCount(uint32_t value)            INLINE { E::set32(fields.dofSectionsCount, value); }
-
-       uint32_t                reExportListOffset() const                                      INLINE { return E::get32(fields.reExportListOffset); }
-       void                    set_reExportListOffset(uint32_t value)          INLINE { E::set32(fields.reExportListOffset, value); }
-
-       uint32_t                reExportCount() const                                           INLINE { return E::get32(fields.reExportCount); }
-       void                    set_reExportCount(uint32_t value)                       INLINE { E::set32(fields.reExportCount, value); }
-
-       uint32_t                depListOffset() const                                           INLINE { return E::get32(fields.depListOffset); }
-       void                    set_depListOffset(uint32_t value)                       INLINE { E::set32(fields.depListOffset, value); }
-
-       uint32_t                depListCount() const                                            INLINE { return E::get32(fields.depListCount); }
-       void                    set_depListCount(uint32_t value)                        INLINE { E::set32(fields.depListCount, value); }
-
-       uint32_t                rangeTableOffset() const                                        INLINE { return E::get32(fields.rangeTableOffset); }
-       void                    set_rangeTableOffset(uint32_t value)            INLINE { E::set32(fields.rangeTableOffset, value); }
-
-       uint32_t                rangeTableCount() const                                         INLINE { return E::get32(fields.rangeTableCount); }
-       void                    set_rangeTableCount(uint32_t value)                     INLINE { E::set32(fields.rangeTableCount, value); }
-
-       uint64_t                dyldSectionAddr() const                                         INLINE { return E::get64(fields.dyldSectionAddr); }
-       void                    set_dyldSectionAddr(uint64_t value)                     INLINE { E::set64(fields.dyldSectionAddr, value); }
-
-
-private:
-       dyld_cache_accelerator_info                     fields;
-};
-
-
-template <typename E>
-class dyldCacheAcceleratorInitializer {
-public:                
-       uint32_t                functionOffset() const                                          INLINE { return E::get32(fields.functionOffset); }
-       void                    set_functionOffset(uint32_t value)                      INLINE { E::set32(fields.functionOffset, value); }
-
-       uint32_t                imageIndex() const                                                      INLINE { return E::get32(fields.imageIndex); }
-       void                    set_imageIndex(uint32_t value)                          INLINE { E::set32(fields.imageIndex, value); }
-
-private:
-       dyld_cache_accelerator_initializer                      fields;
-};
-
-
-template <typename E>
-class dyldCacheAcceleratorRangeEntry {
-public:                
-       uint64_t                startAddress() const                                            INLINE { return E::get64(fields.startAddress); }
-       void                    set_startAddress(uint64_t value)                        INLINE { E::set64(fields.startAddress, value); }
-
-       uint32_t                size() const                                                            INLINE { return E::get32(fields.size); }
-       void                    set_size(uint32_t value)                                        INLINE { E::set32(fields.size, value); }
-
-       uint32_t                imageIndex() const                                                      INLINE { return E::get32(fields.imageIndex); }
-       void                    set_imageIndex(uint32_t value)                          INLINE { E::set32(fields.imageIndex, value); }
-
-private:
-       dyld_cache_range_entry                  fields;
-};
-
-template <typename E>
-class dyldCacheAcceleratorDOFEntry {
-public:                
-       uint64_t                sectionAddress() const                                          INLINE { return E::get64(fields.sectionAddress); }
-       void                    set_sectionAddress(uint64_t value)                      INLINE { E::set64(fields.sectionAddress, value); }
-
-       uint32_t                sectionSize() const                                                     INLINE { return E::get32(fields.sectionSize); }
-       void                    set_sectionSize(uint32_t value)                         INLINE { E::set32(fields.sectionSize, value); }
-
-       uint32_t                imageIndex() const                                                      INLINE { return E::get32(fields.imageIndex); }
-       void                    set_imageIndex(uint32_t value)                          INLINE { E::set32(fields.imageIndex, value); }
-
-private:
-       dyld_cache_accelerator_dof                      fields;
-};
-
-
-template <typename E>
-class dyldCacheSlideInfo {
-public:                
-       uint32_t                version() const                                                         INLINE { return E::get32(fields.version); }
-       void                    set_version(uint32_t value)                                     INLINE { E::set32(fields.version, value); }
-
-       uint32_t                toc_offset() const                                                      INLINE { return E::get32(fields.toc_offset); }
-       void                    set_toc_offset(uint32_t value)                          INLINE { E::set32(fields.toc_offset, value); }
-
-       uint32_t                toc_count() const                                                       INLINE { return E::get32(fields.toc_count); }
-       void                    set_toc_count(uint32_t value)                           INLINE { E::set32(fields.toc_count, value); }
-
-       uint32_t                entries_offset() const                                          INLINE { return E::get32(fields.entries_offset); }
-       void                    set_entries_offset(uint32_t value)                      INLINE { E::set32(fields.entries_offset, value); }
-       
-       uint32_t                entries_count() const                                           INLINE { return E::get32(fields.entries_count); }
-       void                    set_entries_count(uint32_t value)                       INLINE { E::set32(fields.entries_count, value); }
-
-       uint32_t                entries_size() const                                            INLINE { return E::get32(fields.entries_size); }
-       void                    set_entries_size(uint32_t value)                        INLINE { E::set32(fields.entries_size, value); }
-
-       uint16_t                toc(unsigned index) const                                       INLINE { return E::get16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.toc_offset)))[index]); }
-       void                    set_toc(unsigned index, uint16_t value)         INLINE { return E::set16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.toc_offset)))[index], value); }
-
-private:
-       dyld_cache_slide_info                   fields;
-};
-
-
-struct dyldCacheSlideInfoEntry {
-       uint8_t  bits[4096/(8*4)]; // 128-byte bitmap
-};
-
-
-template <typename E>
-class dyldCacheSlideInfo2 {
-public:                
-       uint32_t                version() const                                                         INLINE { return E::get32(fields.version); }
-       void                    set_version(uint32_t value)                                     INLINE { E::set32(fields.version, value); }
-
-       uint32_t                page_starts_offset() const                                      INLINE { return E::get32(fields.page_starts_offset); }
-       void                    set_page_starts_offset(uint32_t value)          INLINE { E::set32(fields.page_starts_offset, value); }
-
-       uint32_t                page_starts_count() const                                       INLINE { return E::get32(fields.page_starts_count); }
-       void                    set_page_starts_count(uint32_t value)           INLINE { E::set32(fields.page_starts_count, value); }
-
-       uint32_t                page_extras_offset() const                                      INLINE { return E::get32(fields.page_extras_offset); }
-       void                    set_page_extras_offset(uint32_t value)          INLINE { E::set32(fields.page_extras_offset, value); }
-
-       uint32_t                page_extras_count() const                                       INLINE { return E::get32(fields.page_extras_count); }
-       void                    set_page_extras_count(uint32_t value)           INLINE { E::set32(fields.page_extras_count, value); }
-
-       uint32_t                page_size() const                                                       INLINE { return E::get32(fields.page_size); }
-       void                    set_page_size(uint32_t value)                           INLINE { E::set32(fields.page_size, value); }
-
-       uint64_t                delta_mask() const                                                      INLINE { return E::get64(fields.delta_mask); }
-       void                    set_delta_mask(uint64_t value)                          INLINE { E::set64(fields.delta_mask, value); }
-
-       uint64_t                value_add() const                                                       INLINE { return E::get64(fields.value_add); }
-       void                    set_value_add(uint64_t value)                           INLINE { E::set64(fields.value_add, value); }
-
-       uint16_t                page_starts(unsigned index) const                               INLINE { return E::get16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_starts_offset)))[index]); }
-       void                    set_page_starts(unsigned index, uint16_t value) INLINE { return E::set16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_starts_offset)))[index], value); }
-
-       uint16_t                page_extras(unsigned index) const                               INLINE { return E::get16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_extras_offset)))[index]); }
-       void                    set_page_extras(unsigned index, uint16_t value) INLINE { return E::set16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_extras_offset)))[index], value); }
-
-
-private:
-       dyld_cache_slide_info2                  fields;
-};
-
-
-template <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 {
-public:                
-       uint32_t                nlistOffset() const                                                     INLINE { return E::get32(fields.nlistOffset); }
-       void                    set_nlistOffset(uint32_t value)                         INLINE { E::set32(fields.nlistOffset, value); }
-
-       uint32_t                nlistCount() const                                                      INLINE { return E::get32(fields.nlistCount); }
-       void                    set_nlistCount(uint32_t value)                          INLINE { E::set32(fields.nlistCount, value); }
-
-       uint32_t                stringsOffset() const                                           INLINE { return E::get32(fields.stringsOffset); }
-       void                    set_stringsOffset(uint32_t value)                       INLINE { E::set32(fields.stringsOffset, value); }
-       
-       uint32_t                stringsSize() const                                                     INLINE { return E::get32(fields.stringsSize); }
-       void                    set_stringsSize(uint32_t value)                         INLINE { E::set32(fields.stringsSize, value); }
-
-       uint32_t                entriesOffset() const                                           INLINE { return E::get32(fields.entriesOffset); }
-       void                    set_entriesOffset(uint32_t value)                       INLINE { E::set32(fields.entriesOffset, value); }
-
-       uint32_t                entriesCount() const                                            INLINE { return E::get32(fields.entriesCount); }
-       void                    set_entriesCount(uint32_t value)                        INLINE { E::set32(fields.entriesCount, value); }
-
-private:
-       dyld_cache_local_symbols_info                   fields;
-};
-
-
-template <typename E>
-class dyldCacheLocalSymbolEntry {
-public:                
-       uint32_t                dylibOffset() const                                                     INLINE { return E::get32(fields.dylibOffset); }
-       void                    set_dylibOffset(uint32_t value)                         INLINE { E::set32(fields.dylibOffset, value); }
-
-       uint32_t                nlistStartIndex() const                                         INLINE { return E::get32(fields.nlistStartIndex); }
-       void                    set_nlistStartIndex(uint32_t value)                     INLINE { E::set32(fields.nlistStartIndex, value); }
-
-       uint32_t                nlistCount() const                                                      INLINE { return E::get32(fields.nlistCount); }
-       void                    set_nlistCount(uint32_t value)                          INLINE { E::set32(fields.nlistCount, value); }
-       
-private:
-       dyld_cache_local_symbols_entry                  fields;
-};
-
-
-
-
-#endif // __DYLD_CACHE_ABSTRACTION__
-
-
diff --git a/launch-cache/FileAbstraction.hpp b/launch-cache/FileAbstraction.hpp
deleted file mode 100644 (file)
index 1d73d74..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 
- *
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
-#ifndef __FILE_ABSTRACTION__
-#define __FILE_ABSTRACTION__
-
-
-#include <stdint.h>
-#include <string.h>
-#include <libkern/OSByteOrder.h>
-
-#ifdef __OPTIMIZE__
-#define INLINE __attribute__((always_inline))
-#else
-#define INLINE
-#endif
-
-//
-// This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants
-//
-// For example: to make a utility that handles 32-bit little enidan files use:  Pointer32<LittleEndian>
-//
-//
-//             get16()                 read a 16-bit number from an E endian struct
-//             set16()                 write a 16-bit number to an E endian struct
-//             get32()                 read a 32-bit number from an E endian struct
-//             set32()                 write a 32-bit number to an E endian struct
-//             get64()                 read a 64-bit number from an E endian struct
-//             set64()                 write a 64-bit number to an E endian struct
-//
-//             getBits()               read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field)
-//             setBits()               write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field)
-//
-//             getBitsRaw()    read a bit field from a struct with native endianness
-//             setBitsRaw()    write a bit field from a struct with native endianness
-//
-
-class BigEndian
-{
-public:
-       static uint16_t get16(const uint16_t& from)                             INLINE { return OSReadBigInt16(&from, 0); }
-       static void             set16(uint16_t& into, uint16_t value)   INLINE { OSWriteBigInt16(&into, 0, value); }
-       
-       static uint32_t get32(const uint32_t& from)                             INLINE { return OSReadBigInt32(&from, 0); }
-       static void             set32(uint32_t& into, uint32_t value)   INLINE { OSWriteBigInt32(&into, 0, value); }
-
-       static int32_t  get32(const int32_t& from)                              INLINE { return OSReadBigInt32(&from, 0); }
-       static void             set32(int32_t& into, int32_t value)             INLINE { OSWriteBigInt32(&into, 0, value); }
-       
-       static uint64_t get64(const uint64_t& from)                             INLINE { return OSReadBigInt64(&from, 0); }
-       static void             set64(uint64_t& into, uint64_t value)   INLINE { OSWriteBigInt64(&into, 0, value); }
-       
-       static uint32_t getBits(const uint32_t& from, 
-                                               uint8_t firstBit, uint8_t bitCount)     INLINE { return getBitsRaw(get32(from), firstBit, bitCount); }
-       static void             setBits(uint32_t& into, uint32_t value,
-                                               uint8_t firstBit, uint8_t bitCount)     INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); }
-
-       static uint32_t getBitsRaw(const uint32_t& from, 
-                                               uint8_t firstBit, uint8_t bitCount)     INLINE { return ((from >> (32-firstBit-bitCount)) & ((1<<bitCount)-1)); }
-       static void             setBitsRaw(uint32_t& into, uint32_t value,
-                                               uint8_t firstBit, uint8_t bitCount)     INLINE { uint32_t temp = into; 
-                                                                                                                                                                                       const uint32_t mask = ((1<<bitCount)-1); 
-                                                                                                                                                                                       temp &= ~(mask << (32-firstBit-bitCount)); 
-                                                                                                                                                                                       temp |= ((value & mask) << (32-firstBit-bitCount)); 
-                                                                                                                                                                                       into = temp; }
-       enum { little_endian = 0 };
-};
-
-
-class LittleEndian
-{
-public:
-       static uint16_t get16(const uint16_t& from)                             INLINE { return OSReadLittleInt16(&from, 0); }
-       static void             set16(uint16_t& into, uint16_t value)   INLINE { OSWriteLittleInt16(&into, 0, value); }
-       
-       static uint32_t get32(const uint32_t& from)                             INLINE { return OSReadLittleInt32(&from, 0); }
-       static void             set32(uint32_t& into, uint32_t value)   INLINE { OSWriteLittleInt32(&into, 0, value); }
-       
-       static int32_t  get32(const int32_t& from)                              INLINE { return OSReadLittleInt32(&from, 0); }
-       static void             set32(int32_t& into, int32_t value)             INLINE { OSWriteLittleInt32(&into, 0, value); }
-       
-       static uint64_t get64(const uint64_t& from)                             INLINE { return OSReadLittleInt64(&from, 0); }
-       static void             set64(uint64_t& into, uint64_t value)   INLINE { OSWriteLittleInt64(&into, 0, value); }
-
-       static uint32_t getBits(const uint32_t& from,
-                                               uint8_t firstBit, uint8_t bitCount)     INLINE { return getBitsRaw(get32(from), firstBit, bitCount); }
-       static void             setBits(uint32_t& into, uint32_t value,
-                                               uint8_t firstBit, uint8_t bitCount)     INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); }
-
-       static uint32_t getBitsRaw(const uint32_t& from,
-                                               uint8_t firstBit, uint8_t bitCount)     INLINE { return ((from >> firstBit) & ((1<<bitCount)-1)); }
-       static void             setBitsRaw(uint32_t& into, uint32_t value,
-                                               uint8_t firstBit, uint8_t bitCount)     INLINE {  uint32_t temp = into; 
-                                                                                                                                                                                       const uint32_t mask = ((1<<bitCount)-1); 
-                                                                                                                                                                                       temp &= ~(mask << firstBit); 
-                                                                                                                                                                                       temp |= ((value & mask) << firstBit); 
-                                                                                                                                                                                       into = temp; }
-       enum { little_endian = 1 };
-};
-
-
-template <typename _E>
-class Pointer32
-{
-public:
-       typedef uint32_t        uint_t;
-       typedef _E                      E;
-       
-       static uint64_t getP(const uint_t& from)                                INLINE { return _E::get32(from); }
-       static void             setP(uint_t& into, uint64_t value)              INLINE { _E::set32(into, (uint32_t)value); }
-
-    // Round to a P-size boundary
-    template <typename T>
-    static T round_up(T value) { return (value+3) & ~(T)3; }
-    template <typename T>
-    static T round_down(T value) { return value & ~(T)3; }
-};
-
-
-template <typename _E>
-class Pointer64
-{
-public:
-       typedef uint64_t        uint_t;
-       typedef _E                      E;
-       
-       static uint64_t getP(const uint_t& from)                                INLINE { return _E::get64(from); }
-       static void             setP(uint_t& into, uint64_t value)              INLINE { _E::set64(into, value); }
-
-    // Round to a P-size boundary
-    template <typename T>
-    static T round_up(T value) { return (value+7) & ~(T)7; }
-    template <typename T>
-    static T round_down(T value) { return value & ~(T)7; }
-};
-
-
-
-
-
-#endif // __FILE_ABSTRACTION__
-
-
diff --git a/launch-cache/MachOFileAbstraction.hpp b/launch-cache/MachOFileAbstraction.hpp
deleted file mode 100644 (file)
index 7168951..0000000
+++ /dev/null
@@ -1,906 +0,0 @@
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 
- *
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
-*/
-#ifndef __MACH_O_FILE_ABSTRACTION__
-#define __MACH_O_FILE_ABSTRACTION__
-
-#include <mach-o/loader.h>
-#include <mach-o/nlist.h>
-#include <mach-o/reloc.h>
-#include <mach/machine.h>
-
-// suport older versions of mach-o/loader.h
-
-
-
-#define ARM64_RELOC_UNSIGNED            0 // for pointers
-
-
-
-
-#define DYLD_CACHE_ADJ_V2_FORMAT                               0x7F
-
-#define DYLD_CACHE_ADJ_V2_POINTER_32                   0x01
-#define DYLD_CACHE_ADJ_V2_POINTER_64                   0x02
-#define DYLD_CACHE_ADJ_V2_DELTA_32                         0x03
-#define DYLD_CACHE_ADJ_V2_DELTA_64                         0x04
-#define DYLD_CACHE_ADJ_V2_ARM64_ADRP                   0x05
-#define DYLD_CACHE_ADJ_V2_ARM64_OFF12                  0x06
-#define DYLD_CACHE_ADJ_V2_ARM64_BR26                   0x07
-#define DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT                        0x08
-#define DYLD_CACHE_ADJ_V2_ARM_BR24                             0x09
-#define DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT              0x0A
-#define DYLD_CACHE_ADJ_V2_THUMB_BR22                   0x0B
-#define DYLD_CACHE_ADJ_V2_IMAGE_OFF_32                 0x0C
-
-
-#include "FileAbstraction.hpp"
-#include "Architectures.hpp"
-
-// utility to pair together a cpu-type and cpu-sub-type
-struct ArchPair
-{
-       uint32_t        arch;
-       uint32_t        subtype;
-       
-       ArchPair(uint32_t cputype, uint32_t cpusubtype) : arch(cputype), subtype(cpusubtype) {}
-       
-       bool operator<(const ArchPair& other) const { 
-               if ( this->arch != other.arch )
-                       return (this->arch < other.arch);
-               return (this->subtype < other.subtype);
-       }
-
-       bool operator==(const ArchPair& other) const { 
-        return this->arch == other.arch  &&  this->subtype == other.subtype;
-       }
-};
-
-
-//
-// This abstraction layer makes every mach-o file look like a 64-bit mach-o file with native endianness
-//
-
-//
-// mach-o load command
-//
-template <typename P>
-class macho_load_command {
-public:
-       uint32_t                cmd() const                                             INLINE { return E::get32(command.cmd); }
-       void                    set_cmd(uint32_t value)                 INLINE { E::set32(command.cmd, value); }
-
-       uint32_t                cmdsize() const                                 INLINE { return E::get32(command.cmdsize); }
-       void                    set_cmdsize(uint32_t value)             INLINE { E::set32(command.cmdsize, value); }
-
-       typedef typename P::E           E;
-private:
-       load_command    command;
-};
-
-
-//
-// mach-o segment load command
-//
-template <typename P> struct macho_segment_content {};
-template <> struct macho_segment_content<Pointer32<BigEndian> >    { segment_command   fields; enum { CMD = LC_SEGMENT         }; };
-template <> struct macho_segment_content<Pointer64<BigEndian> >           { segment_command_64 fields; enum { CMD = LC_SEGMENT_64      }; };
-template <> struct macho_segment_content<Pointer32<LittleEndian> > { segment_command   fields; enum { CMD = LC_SEGMENT         }; };
-template <> struct macho_segment_content<Pointer64<LittleEndian> > { segment_command_64        fields; enum { CMD = LC_SEGMENT_64      }; };
-
-template <typename P>
-class macho_segment_command {
-public:
-       uint32_t                cmd() const                                             INLINE { return E::get32(segment.fields.cmd); }
-       void                    set_cmd(uint32_t value)                 INLINE { E::set32(segment.fields.cmd, value); }
-
-       uint32_t                cmdsize() const                                 INLINE { return E::get32(segment.fields.cmdsize); }
-       void                    set_cmdsize(uint32_t value)             INLINE { E::set32(segment.fields.cmdsize, value); }
-
-       const char*             segname() const                                 INLINE { return segment.fields.segname; }
-       void                    set_segname(const char* value)  INLINE { strncpy(segment.fields.segname, value, 16); }
-       
-       uint64_t                vmaddr() const                                  INLINE { return P::getP(segment.fields.vmaddr); }
-       void                    set_vmaddr(uint64_t value)              INLINE { P::setP(segment.fields.vmaddr, value); }
-
-       uint64_t                vmsize() const                                  INLINE { return P::getP(segment.fields.vmsize); }
-       void                    set_vmsize(uint64_t value)              INLINE { P::setP(segment.fields.vmsize, value); }
-
-       uint64_t                fileoff() const                                 INLINE { return P::getP(segment.fields.fileoff); }
-       void                    set_fileoff(uint64_t value)             INLINE { P::setP(segment.fields.fileoff, value); }
-
-       uint64_t                filesize() const                                INLINE { return P::getP(segment.fields.filesize); }
-       void                    set_filesize(uint64_t value)    INLINE { P::setP(segment.fields.filesize, value); }
-
-       uint32_t                maxprot() const                                 INLINE { return E::get32(segment.fields.maxprot); }
-       void                    set_maxprot(uint32_t value)             INLINE { E::set32((uint32_t&)segment.fields.maxprot, value); }
-
-       uint32_t                initprot() const                                INLINE { return E::get32(segment.fields.initprot); }
-       void                    set_initprot(uint32_t value)    INLINE { E::set32((uint32_t&)segment.fields.initprot, value); }
-
-       uint32_t                nsects() const                                  INLINE { return E::get32(segment.fields.nsects); }
-       void                    set_nsects(uint32_t value)              INLINE { E::set32(segment.fields.nsects, value); }
-
-       uint32_t                flags() const                                   INLINE { return E::get32(segment.fields.flags); }
-       void                    set_flags(uint32_t value)               INLINE { E::set32(segment.fields.flags, value); }
-
-       enum {
-               CMD = macho_segment_content<P>::CMD
-       };
-
-       typedef typename P::E           E;
-private:
-       macho_segment_content<P>        segment;
-};
-
-
-//
-// mach-o section 
-//
-template <typename P> struct macho_section_content {};
-template <> struct macho_section_content<Pointer32<BigEndian> >    { section   fields; };
-template <> struct macho_section_content<Pointer64<BigEndian> >           { section_64 fields; };
-template <> struct macho_section_content<Pointer32<LittleEndian> > { section   fields; };
-template <> struct macho_section_content<Pointer64<LittleEndian> > { section_64        fields; };
-
-template <typename P>
-class macho_section {
-public:
-       const char*             sectname() const                                INLINE { return section.fields.sectname; }
-       void                    set_sectname(const char* value) INLINE { strncpy(section.fields.sectname, value, 16); }
-       
-       const char*             segname() const                                 INLINE { return section.fields.segname; }
-       void                    set_segname(const char* value)  INLINE { strncpy(section.fields.segname, value, 16); }
-       
-       uint64_t                addr() const                                    INLINE { return P::getP(section.fields.addr); }
-       void                    set_addr(uint64_t value)                INLINE { P::setP(section.fields.addr, value); }
-
-       uint64_t                size() const                                    INLINE { return P::getP(section.fields.size); }
-       void                    set_size(uint64_t value)                INLINE { P::setP(section.fields.size, value); }
-
-       uint32_t                offset() const                                  INLINE { return E::get32(section.fields.offset); }
-       void                    set_offset(uint32_t value)              INLINE { E::set32(section.fields.offset, value); }
-
-       uint32_t                align() const                                   INLINE { return E::get32(section.fields.align); }
-       void                    set_align(uint32_t value)               INLINE { E::set32(section.fields.align, value); }
-
-       uint32_t                reloff() const                                  INLINE { return E::get32(section.fields.reloff); }
-       void                    set_reloff(uint32_t value)              INLINE { E::set32(section.fields.reloff, value); }
-
-       uint32_t                nreloc() const                                  INLINE { return E::get32(section.fields.nreloc); }
-       void                    set_nreloc(uint32_t value)              INLINE { E::set32(section.fields.nreloc, value); }
-
-       uint32_t                flags() const                                   INLINE { return E::get32(section.fields.flags); }
-       void                    set_flags(uint32_t value)               INLINE { E::set32(section.fields.flags, value); }
-
-       uint32_t                reserved1() const                               INLINE { return E::get32(section.fields.reserved1); }
-       void                    set_reserved1(uint32_t value)   INLINE { E::set32(section.fields.reserved1, value); }
-
-       uint32_t                reserved2() const                               INLINE { return E::get32(section.fields.reserved2); }
-       void                    set_reserved2(uint32_t value)   INLINE { E::set32(section.fields.reserved2, value); }
-
-       typedef typename P::E           E;
-private:
-       macho_section_content<P>        section;
-};
-
-
-//
-// mach-o dylib load command
-//
-template <typename P>
-class macho_dylib_command {
-public:
-       uint32_t                cmd() const                                                                     INLINE { return E::get32(fields.cmd); }
-       void                    set_cmd(uint32_t value)                                         INLINE { E::set32(fields.cmd, value); }
-
-       uint32_t                cmdsize() const                                                         INLINE { return E::get32(fields.cmdsize); }
-       void                    set_cmdsize(uint32_t value)                                     INLINE { E::set32(fields.cmdsize, value); }
-
-       uint32_t                name_offset() const                                                     INLINE { return E::get32(fields.dylib.name.offset); }
-       void                    set_name_offset(uint32_t value)                         INLINE { E::set32(fields.dylib.name.offset, value);  }
-       
-       uint32_t                timestamp() const                                                       INLINE { return E::get32(fields.dylib.timestamp); }
-       void                    set_timestamp(uint32_t value)                           INLINE { E::set32(fields.dylib.timestamp, value); }
-
-       uint32_t                current_version() const                                         INLINE { return E::get32(fields.dylib.current_version); }
-       void                    set_current_version(uint32_t value)                     INLINE { E::set32(fields.dylib.current_version, value); }
-
-       uint32_t                compatibility_version() const                           INLINE { return E::get32(fields.dylib.compatibility_version); }
-       void                    set_compatibility_version(uint32_t value)       INLINE { E::set32(fields.dylib.compatibility_version, value); }
-
-       const char*             name() const                                                            INLINE { return (const char*)&fields + name_offset(); }
-       void                    set_name_offset()                                                       INLINE { set_name_offset(sizeof(fields)); }
-       
-       typedef typename P::E           E;
-private:
-       dylib_command   fields;
-};
-
-
-//
-// mach-o dylinker load command
-//
-template <typename P>
-class macho_dylinker_command {
-public:
-       uint32_t                cmd() const                                                     INLINE { return E::get32(fields.cmd); }
-       void                    set_cmd(uint32_t value)                         INLINE { E::set32(fields.cmd, value); }
-
-       uint32_t                cmdsize() const                                         INLINE { return E::get32(fields.cmdsize); }
-       void                    set_cmdsize(uint32_t value)                     INLINE { E::set32(fields.cmdsize, value); }
-
-       uint32_t                name_offset() const                                     INLINE { return E::get32(fields.name.offset); }
-       void                    set_name_offset(uint32_t value)         INLINE { E::set32(fields.name.offset, value);  }
-       
-       const char*             name() const                                            INLINE { return (const char*)&fields + name_offset(); }
-       void                    set_name_offset()                                       INLINE { set_name_offset(sizeof(fields)); }
-       
-       typedef typename P::E           E;
-private:
-       dylinker_command        fields;
-};
-
-
-//
-// mach-o sub_framework load command
-//
-template <typename P>
-class macho_sub_framework_command {
-public:
-       uint32_t                cmd() const                                                     INLINE { return E::get32(fields.cmd); }
-       void                    set_cmd(uint32_t value)                         INLINE { E::set32(fields.cmd, value); }
-
-       uint32_t                cmdsize() const                                         INLINE { return E::get32(fields.cmdsize); }
-       void                    set_cmdsize(uint32_t value)                     INLINE { E::set32(fields.cmdsize, value); }
-
-       uint32_t                umbrella_offset() const                         INLINE { return E::get32(fields.umbrella.offset); }
-       void                    set_umbrella_offset(uint32_t value)     INLINE { E::set32(fields.umbrella.offset, value);  }
-       
-       const char*             umbrella() const                                        INLINE { return (const char*)&fields + umbrella_offset(); }
-       void                    set_umbrella_offset()                           INLINE { set_umbrella_offset(sizeof(fields)); }
-               
-       typedef typename P::E           E;
-private:
-       sub_framework_command   fields;
-};
-
-
-//
-// mach-o sub_client load command
-//
-template <typename P>
-class macho_sub_client_command {
-public:
-       uint32_t                cmd() const                                                     INLINE { return E::get32(fields.cmd); }
-       void                    set_cmd(uint32_t value)                         INLINE { E::set32(fields.cmd, value); }
-
-       uint32_t                cmdsize() const                                         INLINE { return E::get32(fields.cmdsize); }
-       void                    set_cmdsize(uint32_t value)                     INLINE { E::set32(fields.cmdsize, value); }
-
-       uint32_t                client_offset() const                           INLINE { return E::get32(fields.client.offset); }
-       void                    set_client_offset(uint32_t value)       INLINE { E::set32(fields.client.offset, value);  }
-       
-       const char*             client() const                                          INLINE { return (const char*)&fields + client_offset(); }
-       void                    set_client_offset()                                     INLINE { set_client_offset(sizeof(fields)); }
-               
-       typedef typename P::E           E;
-private:
-       sub_client_command      fields;
-};
-
-
-//
-// mach-o sub_umbrella load command
-//
-template <typename P>
-class macho_sub_umbrella_command {
-public:
-       uint32_t                cmd() const                                                             INLINE { return E::get32(fields.cmd); }
-       void                    set_cmd(uint32_t value)                                 INLINE { E::set32(fields.cmd, value); }
-
-       uint32_t                cmdsize() const                                                 INLINE { return E::get32(fields.cmdsize); }
-       void                    set_cmdsize(uint32_t value)                             INLINE { E::set32(fields.cmdsize, value); }
-
-       uint32_t                sub_umbrella_offset() const                             INLINE { return E::get32(fields.sub_umbrella.offset); }
-       void                    set_sub_umbrella_offset(uint32_t value) INLINE { E::set32(fields.sub_umbrella.offset, value);  }
-       
-       const char*             sub_umbrella() const                                    INLINE { return (const char*)&fields + sub_umbrella_offset(); }
-       void                    set_sub_umbrella_offset()                               INLINE { set_sub_umbrella_offset(sizeof(fields)); }
-               
-       typedef typename P::E           E;
-private:
-       sub_umbrella_command    fields;
-};
-
-
-//
-// mach-o sub_library load command
-//
-template <typename P>
-class macho_sub_library_command {
-public:
-       uint32_t                cmd() const                                                             INLINE { return E::get32(fields.cmd); }
-       void                    set_cmd(uint32_t value)                                 INLINE { E::set32(fields.cmd, value); }
-
-       uint32_t                cmdsize() const                                                 INLINE { return E::get32(fields.cmdsize); }
-       void                    set_cmdsize(uint32_t value)                             INLINE { E::set32(fields.cmdsize, value); }
-
-       uint32_t                sub_library_offset() const                              INLINE { return E::get32(fields.sub_library.offset); }
-       void                    set_sub_library_offset(uint32_t value)  INLINE { E::set32(fields.sub_library.offset, value);  }
-       
-       const char*             sub_library() const                                             INLINE { return (const char*)&fields + sub_library_offset(); }
-       void                    set_sub_library_offset()                                INLINE { set_sub_library_offset(sizeof(fields)); }
-               
-       typedef typename P::E           E;
-private:
-       sub_library_command     fields;
-};
-
-
-//
-// mach-o uuid load command
-//
-template <typename P>
-class macho_uuid_command {
-public:
-       uint32_t                cmd() const                                                             INLINE { return E::get32(fields.cmd); }
-       void                    set_cmd(uint32_t value)                                 INLINE { E::set32(fields.cmd, value); }
-
-       uint32_t                cmdsize() const                                                 INLINE { return E::get32(fields.cmdsize); }
-       void                    set_cmdsize(uint32_t value)                             INLINE { E::set32(fields.cmdsize, value); }
-
-       const uint8_t*  uuid() const                                                    INLINE { return fields.uuid; }
-       void                    set_uuid(uint8_t value[16])                             INLINE { memcpy(&fields.uuid, value, 16); }
-                       
-       typedef typename P::E           E;
-private:
-       uuid_command    fields;
-};
-
-
-//
-// mach-o routines load command
-//
-template <typename P> struct macho_routines_content {};
-template <> struct macho_routines_content<Pointer32<BigEndian> >    { routines_command         fields; enum { CMD = LC_ROUTINES        }; };
-template <> struct macho_routines_content<Pointer64<BigEndian> >       { routines_command_64   fields; enum { CMD = LC_ROUTINES_64     }; };
-template <> struct macho_routines_content<Pointer32<LittleEndian> > { routines_command         fields; enum { CMD = LC_ROUTINES        }; };
-template <> struct macho_routines_content<Pointer64<LittleEndian> > { routines_command_64      fields; enum { CMD = LC_ROUTINES_64     }; };
-
-template <typename P>
-class macho_routines_command {
-public:
-       uint32_t                cmd() const                                                     INLINE { return E::get32(routines.fields.cmd); }
-       void                    set_cmd(uint32_t value)                         INLINE { E::set32(routines.fields.cmd, value); }
-
-       uint32_t                cmdsize() const                                         INLINE { return E::get32(routines.fields.cmdsize); }
-       void                    set_cmdsize(uint32_t value)                     INLINE { E::set32(routines.fields.cmdsize, value); }
-
-       uint64_t                init_address() const                            INLINE { return P::getP(routines.fields.init_address); }
-       void                    set_init_address(uint64_t value)        INLINE { P::setP(routines.fields.init_address, value); }
-
-       uint64_t                init_module() const                                     INLINE { return P::getP(routines.fields.init_module); }
-       void                    set_init_module(uint64_t value)         INLINE { P::setP(routines.fields.init_module, value); }
-
-       uint64_t                reserved1() const                                       INLINE { return P::getP(routines.fields.reserved1); }
-       void                    set_reserved1(uint64_t value)           INLINE { P::setP(routines.fields.reserved1, value); }
-       
-       uint64_t                reserved2() const                                       INLINE { return P::getP(routines.fields.reserved2); }
-       void                    set_reserved2(uint64_t value)           INLINE { P::setP(routines.fields.reserved2, value); }
-       
-       uint64_t                reserved3() const                                       INLINE { return P::getP(routines.fields.reserved3); }
-       void                    set_reserved3(uint64_t value)           INLINE { P::setP(routines.fields.reserved3, value); }
-       
-       uint64_t                reserved4() const                                       INLINE { return P::getP(routines.fields.reserved4); }
-       void                    set_reserved4(uint64_t value)           INLINE { P::setP(routines.fields.reserved4, value); }
-       
-       uint64_t                reserved5() const                                       INLINE { return P::getP(routines.fields.reserved5); }
-       void                    set_reserved5(uint64_t value)           INLINE { P::setP(routines.fields.reserved5, value); }
-       
-       uint64_t                reserved6() const                                       INLINE { return P::getP(routines.fields.reserved6); }
-       void                    set_reserved6(uint64_t value)           INLINE { P::setP(routines.fields.reserved6, value); }
-       
-       typedef typename P::E           E;
-       enum {
-               CMD = macho_routines_content<P>::CMD
-       };
-private:
-       macho_routines_content<P>       routines;
-};
-
-
-//
-// mach-o symbol table load command
-//
-template <typename P>
-class macho_symtab_command {
-public:
-       uint32_t                cmd() const                                     INLINE { return E::get32(fields.cmd); }
-       void                    set_cmd(uint32_t value)         INLINE { E::set32(fields.cmd, value); }
-
-       uint32_t                cmdsize() const                         INLINE { return E::get32(fields.cmdsize); }
-       void                    set_cmdsize(uint32_t value)     INLINE { E::set32(fields.cmdsize, value); }
-
-       uint32_t                symoff() const                          INLINE { return E::get32(fields.symoff); }
-       void                    set_symoff(uint32_t value)      INLINE { E::set32(fields.symoff, value);  }
-       
-       uint32_t                nsyms() const                           INLINE { return E::get32(fields.nsyms); }
-       void                    set_nsyms(uint32_t value)       INLINE { E::set32(fields.nsyms, value);  }
-       
-       uint32_t                stroff() const                          INLINE { return E::get32(fields.stroff); }
-       void                    set_stroff(uint32_t value)      INLINE { E::set32(fields.stroff, value);  }
-       
-       uint32_t                strsize() const                         INLINE { return E::get32(fields.strsize); }
-       void                    set_strsize(uint32_t value)     INLINE { E::set32(fields.strsize, value);  }
-       
-       
-       typedef typename P::E           E;
-private:
-       symtab_command  fields;
-};
-
-
-//
-// mach-o dynamic symbol table load command
-//
-template <typename P>
-class macho_dysymtab_command {
-public:
-       uint32_t                cmd() const                                                     INLINE { return E::get32(fields.cmd); }
-       void                    set_cmd(uint32_t value)                         INLINE { E::set32(fields.cmd, value); }
-
-       uint32_t                cmdsize() const                                         INLINE { return E::get32(fields.cmdsize); }
-       void                    set_cmdsize(uint32_t value)                     INLINE { E::set32(fields.cmdsize, value); }
-
-       uint32_t                ilocalsym() const                                       INLINE { return E::get32(fields.ilocalsym); }
-       void                    set_ilocalsym(uint32_t value)           INLINE { E::set32(fields.ilocalsym, value);  }
-       
-       uint32_t                nlocalsym() const                                       INLINE { return E::get32(fields.nlocalsym); }
-       void                    set_nlocalsym(uint32_t value)           INLINE { E::set32(fields.nlocalsym, value);  }
-       
-       uint32_t                iextdefsym() const                                      INLINE { return E::get32(fields.iextdefsym); }
-       void                    set_iextdefsym(uint32_t value)          INLINE { E::set32(fields.iextdefsym, value);  }
-       
-       uint32_t                nextdefsym() const                                      INLINE { return E::get32(fields.nextdefsym); }
-       void                    set_nextdefsym(uint32_t value)          INLINE { E::set32(fields.nextdefsym, value);  }
-       
-       uint32_t                iundefsym() const                                       INLINE { return E::get32(fields.iundefsym); }
-       void                    set_iundefsym(uint32_t value)           INLINE { E::set32(fields.iundefsym, value);  }
-       
-       uint32_t                nundefsym() const                                       INLINE { return E::get32(fields.nundefsym); }
-       void                    set_nundefsym(uint32_t value)           INLINE { E::set32(fields.nundefsym, value);  }
-       
-       uint32_t                tocoff() const                                          INLINE { return E::get32(fields.tocoff); }
-       void                    set_tocoff(uint32_t value)                      INLINE { E::set32(fields.tocoff, value);  }
-       
-       uint32_t                ntoc() const                                            INLINE { return E::get32(fields.ntoc); }
-       void                    set_ntoc(uint32_t value)                        INLINE { E::set32(fields.ntoc, value);  }
-       
-       uint32_t                modtaboff() const                                       INLINE { return E::get32(fields.modtaboff); }
-       void                    set_modtaboff(uint32_t value)           INLINE { E::set32(fields.modtaboff, value);  }
-       
-       uint32_t                nmodtab() const                                         INLINE { return E::get32(fields.nmodtab); }
-       void                    set_nmodtab(uint32_t value)                     INLINE { E::set32(fields.nmodtab, value);  }
-       
-       uint32_t                extrefsymoff() const                            INLINE { return E::get32(fields.extrefsymoff); }
-       void                    set_extrefsymoff(uint32_t value)        INLINE { E::set32(fields.extrefsymoff, value);  }
-       
-       uint32_t                nextrefsyms() const                                     INLINE { return E::get32(fields.nextrefsyms); }
-       void                    set_nextrefsyms(uint32_t value)         INLINE { E::set32(fields.nextrefsyms, value);  }
-       
-       uint32_t                indirectsymoff() const                          INLINE { return E::get32(fields.indirectsymoff); }
-       void                    set_indirectsymoff(uint32_t value)      INLINE { E::set32(fields.indirectsymoff, value);  }
-       
-       uint32_t                nindirectsyms() const                           INLINE { return E::get32(fields.nindirectsyms); }
-       void                    set_nindirectsyms(uint32_t value)       INLINE { E::set32(fields.nindirectsyms, value);  }
-       
-       uint32_t                extreloff() const                                       INLINE { return E::get32(fields.extreloff); }
-       void                    set_extreloff(uint32_t value)           INLINE { E::set32(fields.extreloff, value);  }
-       
-       uint32_t                nextrel() const                                         INLINE { return E::get32(fields.nextrel); }
-       void                    set_nextrel(uint32_t value)                     INLINE { E::set32(fields.nextrel, value);  }
-       
-       uint32_t                locreloff() const                                       INLINE { return E::get32(fields.locreloff); }
-       void                    set_locreloff(uint32_t value)           INLINE { E::set32(fields.locreloff, value);  }
-       
-       uint32_t                nlocrel() const                                         INLINE { return E::get32(fields.nlocrel); }
-       void                    set_nlocrel(uint32_t value)                     INLINE { E::set32(fields.nlocrel, value);  }
-       
-       typedef typename P::E           E;
-private:
-       dysymtab_command        fields;
-};
-
-
-//
-// mach-o two-level hints load command
-//
-template <typename P>
-class macho_twolevel_hints_command {
-public:
-       uint32_t                cmd() const                                     INLINE { return E::get32(fields.cmd); }
-       void                    set_cmd(uint32_t value)         INLINE { E::set32(fields.cmd, value); }
-
-       uint32_t                cmdsize() const                         INLINE { return E::get32(fields.cmdsize); }
-       void                    set_cmdsize(uint32_t value)     INLINE { E::set32(fields.cmdsize, value); }
-
-       uint32_t                offset() const                          INLINE { return E::get32(fields.offset); }
-       void                    set_offset(uint32_t value)      INLINE { E::set32(fields.offset, value);  }
-       
-       uint32_t                nhints() const                          INLINE { return E::get32(fields.nhints); }
-       void                    set_nhints(uint32_t value)      INLINE { E::set32(fields.nhints, value);  }
-       
-       typedef typename P::E           E;
-private:
-       twolevel_hints_command  fields;
-};
-
-
-//
-// mach-o threads load command
-//
-template <typename P>
-class macho_thread_command {
-public:
-       uint32_t                cmd() const                                                                                     INLINE { return E::get32(fields.cmd); }
-       void                    set_cmd(uint32_t value)                                                         INLINE { E::set32(fields.cmd, value); }
-
-       uint32_t                cmdsize() const                                                                         INLINE { return E::get32(fields.cmdsize); }
-       void                    set_cmdsize(uint32_t value)                                                     INLINE { E::set32(fields.cmdsize, value); }
-
-       uint32_t                flavor() const                                                                          INLINE { return E::get32(fields_flavor); }
-       void                    set_flavor(uint32_t value)                                                      INLINE { E::set32(fields_flavor, value);  }
-       
-       uint32_t                count() const                                                                           INLINE { return E::get32(fields_count); }
-       void                    set_count(uint32_t value)                                                       INLINE { E::set32(fields_count, value);  }
-       
-       uint64_t                thread_register(uint32_t index) const                           INLINE { return P::getP(thread_registers[index]); }
-       void                    set_thread_register(uint32_t index, uint64_t value)     INLINE { P::setP(thread_registers[index], value); }
-       
-       typedef typename P::E           E;
-       typedef typename P::uint_t      pint_t;
-private:
-       struct thread_command   fields;
-       uint32_t                                fields_flavor;
-       uint32_t                                fields_count;
-       pint_t                                  thread_registers[1];
-};
-
-
-//
-// mach-o misc data 
-//
-template <typename P>
-class macho_linkedit_data_command {
-public:
-       uint32_t                cmd() const                                     INLINE { return E::get32(fields.cmd); }
-       void                    set_cmd(uint32_t value)         INLINE { E::set32(fields.cmd, value); }
-
-       uint32_t                cmdsize() const                         INLINE { return E::get32(fields.cmdsize); }
-       void                    set_cmdsize(uint32_t value)     INLINE { E::set32(fields.cmdsize, value); }
-
-       uint32_t                dataoff() const                         INLINE { return E::get32(fields.dataoff); }
-       void                    set_dataoff(uint32_t value)     INLINE { E::set32(fields.dataoff, value);  }
-       
-       uint32_t                datasize() const                        INLINE { return E::get32(fields.datasize); }
-       void                    set_datasize(uint32_t value)INLINE { E::set32(fields.datasize, value);  }
-       
-       
-       typedef typename P::E           E;
-private:
-       linkedit_data_command   fields;
-};
-
-
-//
-// mach-o symbol table entry 
-//
-template <typename P> struct macho_nlist_content {};
-template <> struct macho_nlist_content<Pointer32<BigEndian> >    { struct nlist                fields; };
-template <> struct macho_nlist_content<Pointer64<BigEndian> >   { struct nlist_64      fields; };
-template <> struct macho_nlist_content<Pointer32<LittleEndian> > { struct nlist                fields; };
-template <> struct macho_nlist_content<Pointer64<LittleEndian> > { struct nlist_64     fields; };
-
-template <typename P>
-class macho_nlist {
-public:
-       uint32_t                n_strx() const                                  INLINE { return E::get32(entry.fields.n_un.n_strx); }
-       void                    set_n_strx(uint32_t value)              INLINE { E::set32((uint32_t&)entry.fields.n_un.n_strx, value); }
-
-       uint8_t                 n_type() const                                  INLINE { return entry.fields.n_type; }
-       void                    set_n_type(uint8_t value)               INLINE { entry.fields.n_type = value; }
-
-       uint8_t                 n_sect() const                                  INLINE { return entry.fields.n_sect; }
-       void                    set_n_sect(uint8_t value)               INLINE { entry.fields.n_sect = value; }
-
-       uint16_t                n_desc() const                                  INLINE { return E::get16(entry.fields.n_desc); }
-       void                    set_n_desc(uint16_t value)              INLINE { E::set16((uint16_t&)entry.fields.n_desc, value); }
-
-       uint64_t                n_value() const                                 INLINE { return P::getP(entry.fields.n_value); }
-       void                    set_n_value(uint64_t value)             INLINE { P::setP(entry.fields.n_value, value); }
-
-       typedef typename P::E           E;
-private:
-       macho_nlist_content<P>  entry;
-};
-
-
-
-//
-// mach-o relocation info
-//
-template <typename P>
-class macho_relocation_info {
-public:
-       uint32_t                r_address() const                               INLINE { return E::get32(address); }
-       void                    set_r_address(uint32_t value)   INLINE { E::set32(address, value); }
-
-       uint32_t                r_symbolnum() const                             INLINE { return E::getBits(other, 0, 24); }
-       void                    set_r_symbolnum(uint32_t value) INLINE { E::setBits(other, value, 0, 24); }
-
-       bool                    r_pcrel() const                                 INLINE { return E::getBits(other, 24, 1); }
-       void                    set_r_pcrel(bool value)                 INLINE { E::setBits(other, value, 24, 1); }     
-       
-       uint8_t                 r_length() const                                INLINE { return E::getBits(other, 25, 2); }
-       void                    set_r_length(uint8_t value)             INLINE { E::setBits(other, value, 25, 2); }
-       
-       bool                    r_extern() const                                INLINE { return E::getBits(other, 27, 1); }
-       void                    set_r_extern(bool value)                INLINE { E::setBits(other, value, 27, 1); }
-       
-       uint8_t                 r_type() const                                  INLINE { return E::getBits(other, 28, 4); }
-       void                    set_r_type(uint8_t value)               INLINE { E::setBits(other, value, 28, 4); }
-               
-       void                    set_r_length()                                  INLINE { set_r_length((sizeof(typename P::uint_t)==8) ? 3 : 2); }
-
-       typedef typename P::E           E;
-private:
-       uint32_t                address;
-       uint32_t                other;
-};
-
-
-//
-// mach-o scattered relocation info
-// The bit fields are always in big-endian order (see mach-o/reloc.h)
-//
-template <typename P>
-class macho_scattered_relocation_info {
-public:
-       bool                    r_scattered() const                     INLINE { return BigEndian::getBitsRaw(E::get32(other), 0, 1); }
-       void                    set_r_scattered(bool x)         INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 0, 1);  E::set32(other, temp); }
-
-       bool                    r_pcrel() const                         INLINE { return BigEndian::getBitsRaw(E::get32(other), 1, 1); }
-       void                    set_r_pcrel(bool x)                     INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 1, 1);  E::set32(other, temp); }
-
-       uint8_t                 r_length() const                        INLINE { return BigEndian::getBitsRaw(E::get32(other), 2, 2); }
-       void                    set_r_length(uint8_t x)         INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 2, 2);  E::set32(other, temp); }
-
-       uint8_t                 r_type() const                          INLINE { return BigEndian::getBitsRaw(E::get32(other), 4, 4); }
-       void                    set_r_type(uint8_t x)           INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 4, 4);  E::set32(other, temp); }
-
-       uint32_t                r_address() const                       INLINE { return BigEndian::getBitsRaw(E::get32(other), 8, 24); }
-       void                    set_r_address(uint32_t x)       INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 8, 24);  E::set32(other, temp); }
-
-       uint32_t                r_value() const                         INLINE { return E::get32(value); }
-       void                    set_r_value(uint32_t x)         INLINE { E::set32(value, x); }
-
-       uint32_t                r_other() const                         INLINE { return other; }
-       
-       typedef typename P::E           E;
-private:
-       uint32_t                other;
-       uint32_t                value;
-};
-
-
-//
-// mach-o file header
-//
-template <typename P> struct macho_header_content {};
-template <> struct macho_header_content<Pointer32<BigEndian> >    { mach_header                fields; };
-template <> struct macho_header_content<Pointer64<BigEndian> >   { mach_header_64      fields; };
-template <> struct macho_header_content<Pointer32<LittleEndian> > { mach_header                fields; };
-template <> struct macho_header_content<Pointer64<LittleEndian> > { mach_header_64     fields; };
-
-template <typename P>
-class macho_header {
-public:
-       uint32_t                magic() const                                   INLINE { return E::get32(header.fields.magic); }
-       void                    set_magic(uint32_t value)               INLINE { E::set32(header.fields.magic, value); }
-
-       uint32_t                cputype() const                                 INLINE { return E::get32(header.fields.cputype); }
-       void                    set_cputype(uint32_t value)             INLINE { E::set32((uint32_t&)header.fields.cputype, value); }
-
-       uint32_t                cpusubtype() const                              INLINE { return E::get32(header.fields.cpusubtype); }
-       void                    set_cpusubtype(uint32_t value)  INLINE { E::set32((uint32_t&)header.fields.cpusubtype, value); }
-
-       uint32_t                filetype() const                                INLINE { return E::get32(header.fields.filetype); }
-       void                    set_filetype(uint32_t value)    INLINE { E::set32(header.fields.filetype, value); }
-
-       uint32_t                ncmds() const                                   INLINE { return E::get32(header.fields.ncmds); }
-       void                    set_ncmds(uint32_t value)               INLINE { E::set32(header.fields.ncmds, value); }
-
-       uint32_t                sizeofcmds() const                              INLINE { return E::get32(header.fields.sizeofcmds); }
-       void                    set_sizeofcmds(uint32_t value)  INLINE { E::set32(header.fields.sizeofcmds, value); }
-
-       uint32_t                flags() const                                   INLINE { return E::get32(header.fields.flags); }
-       void                    set_flags(uint32_t value)               INLINE { E::set32(header.fields.flags, value); }
-
-       uint32_t                reserved() const                                INLINE { return E::get32(header.fields.reserved); }
-       void                    set_reserved(uint32_t value)    INLINE { E::set32(header.fields.reserved, value); }
-
-    const macho_segment_command<P>* getSegment(const char *segname) const
-    {
-        const macho_load_command<P>* cmds = (macho_load_command<P>*)((uint8_t*)this + sizeof(macho_header<P>));
-        uint32_t cmd_count = this->ncmds();
-        const macho_load_command<P>* cmd = cmds;
-        for (uint32_t i = 0; i < cmd_count; ++i) {
-            if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
-                const macho_segment_command<P>* segcmd = (macho_segment_command<P>*)cmd;
-                if (0 == strncmp(segname, segcmd->segname(), 16)) {
-                    return segcmd;
-              }
-            }
-            cmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-        }
-        return NULL;
-    }
-
-    const macho_section<P>* getSection(const char *segname, const char *sectname) const
-    {
-       const  macho_segment_command<P>* segcmd = getSegment(segname);
-        if (!segcmd) return NULL;
-
-        const macho_section<P>* sectcmd = (macho_section<P>*)(segcmd+1);
-        uint32_t section_count = segcmd->nsects();
-        for (uint32_t j = 0; j < section_count; ++j) {
-            if (0 == ::strncmp(sectcmd[j].sectname(), sectname, 16)) {
-                return sectcmd+j;
-            }
-        }
-
-        if (strcmp(segname, "__DATA") == 0) {
-                       if (const macho_section<P>* dataConst = getSection("__DATA_CONST", sectname))
-                return dataConst;
-            if (const macho_section<P>* dataDirty = getSection("__DATA_DIRTY", sectname))
-                return dataDirty;
-        }
-        return NULL;
-    }
-
-    const macho_load_command<P>* getLoadCommand(int query) const
-    {
-        const macho_load_command<P>* cmds = (macho_load_command<P>*)((uint8_t*)this + sizeof(macho_header<P>));
-        uint32_t cmd_count = this->ncmds();
-        const macho_load_command<P>* cmd = cmds;
-        for (uint32_t i = 0; i < cmd_count; ++i) {
-            if ( cmd->cmd() == query ) {
-                return cmd;
-            }
-            cmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-        }
-        return NULL;
-    }
-
-       typedef typename P::E           E;
-private:
-       macho_header_content<P> header;
-};
-
-
-
-//
-// compressed dyld info load command
-//
-template <typename P>
-class macho_dyld_info_command {
-public:
-       uint32_t                cmd() const                                     INLINE { return E::get32(fields.cmd); }
-       void                    set_cmd(uint32_t value)         INLINE { E::set32(fields.cmd, value); }
-
-       uint32_t                cmdsize() const                         INLINE { return E::get32(fields.cmdsize); }
-       void                    set_cmdsize(uint32_t value)     INLINE { E::set32(fields.cmdsize, value); }
-
-       uint32_t                rebase_off() const                              INLINE { return E::get32(fields.rebase_off); }
-       void                    set_rebase_off(uint32_t value)  INLINE { E::set32(fields.rebase_off, value);  }
-       
-       uint32_t                rebase_size() const                             INLINE { return E::get32(fields.rebase_size); }
-       void                    set_rebase_size(uint32_t value) INLINE { E::set32(fields.rebase_size, value);  }
-       
-       uint32_t                bind_off() const                                INLINE { return E::get32(fields.bind_off); }
-       void                    set_bind_off(uint32_t value)    INLINE { E::set32(fields.bind_off, value);  }
-       
-       uint32_t                bind_size() const                               INLINE { return E::get32(fields.bind_size); }
-       void                    set_bind_size(uint32_t value)   INLINE { E::set32(fields.bind_size, value);  }
-       
-       uint32_t                weak_bind_off() const                           INLINE { return E::get32(fields.weak_bind_off); }
-       void                    set_weak_bind_off(uint32_t value)       INLINE { E::set32(fields.weak_bind_off, value);  }
-       
-       uint32_t                weak_bind_size() const                          INLINE { return E::get32(fields.weak_bind_size); }
-       void                    set_weak_bind_size(uint32_t value)      INLINE { E::set32(fields.weak_bind_size, value);  }
-       
-       uint32_t                lazy_bind_off() const                           INLINE { return E::get32(fields.lazy_bind_off); }
-       void                    set_lazy_bind_off(uint32_t value)       INLINE { E::set32(fields.lazy_bind_off, value);  }
-       
-       uint32_t                lazy_bind_size() const                          INLINE { return E::get32(fields.lazy_bind_size); }
-       void                    set_lazy_bind_size(uint32_t value)      INLINE { E::set32(fields.lazy_bind_size, value);  }
-       
-       uint32_t                export_off() const                              INLINE { return E::get32(fields.export_off); }
-       void                    set_export_off(uint32_t value)  INLINE { E::set32(fields.export_off, value);  }
-       
-       uint32_t                export_size() const                             INLINE { return E::get32(fields.export_size); }
-       void                    set_export_size(uint32_t value) INLINE { E::set32(fields.export_size, value);  }
-       
-       
-       typedef typename P::E           E;
-private:
-       dyld_info_command       fields;
-};
-
-#ifndef NO_ULEB 
-inline uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) {
-       uint64_t result = 0;
-       int              bit = 0;
-       do {
-               if (p == end)
-                       throw "malformed uleb128 extends beyond trie";
-
-               uint64_t slice = *p & 0x7f;
-
-               if (bit >= 64 || slice << bit >> bit != slice)
-                       throw "uleb128 too big for 64-bits";
-               else {
-                       result |= (slice << bit);
-                       bit += 7;
-               }
-       } 
-       while (*p++ & 0x80);
-       return result;
-}
-       
-
-inline int64_t read_sleb128(const uint8_t*& p, const uint8_t* end)
-{
-       int64_t result = 0;
-       int bit = 0;
-       uint8_t byte;
-       do {
-               if (p == end)
-                       throw "malformed sleb128";
-               byte = *p++;
-               result |= (((int64_t)(byte & 0x7f)) << bit);
-               bit += 7;
-       } while (byte & 0x80);
-       // sign extend negative numbers
-       if ( (byte & 0x40) != 0 )
-               result |= (~0ULL) << bit;
-       return result;
-}
-
-#endif
-
-
-#endif // __MACH_O_FILE_ABSTRACTION__
-
-
diff --git a/launch-cache/MachOTrie.hpp b/launch-cache/MachOTrie.hpp
deleted file mode 100644 (file)
index d2f137e..0000000
+++ /dev/null
@@ -1,399 +0,0 @@
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 
- *
- * Copyright (c) 2008-2010 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
-*/
-#ifndef __MACH_O_TRIE__
-#define __MACH_O_TRIE__
-
-#include <algorithm>
-#include <vector>
-
-#include "MachOFileAbstraction.hpp"
-
-
-namespace mach_o {
-namespace trie {
-
-struct Edge
-{
-                                       Edge(const char* s, struct Node* n) : fSubString(s), fChild(n) { }
-                                       ~Edge() {  }
-       const char*             fSubString;
-       struct Node*    fChild;
-       
-};
-
-struct Node
-{
-                                               Node(const char* s) : fCummulativeString(s), fAddress(0), fFlags(0), 
-                                                                                       fOther(0), fImportedName(NULL), fOrdered(false), 
-                                                                                       fHaveExportInfo(false), fTrieOffset(0) {}
-                                               ~Node() { }
-       const char*                     fCummulativeString;
-       std::vector<Edge>       fChildren;
-       uint64_t                        fAddress;
-       uint64_t                        fFlags;
-       uint64_t                        fOther;
-       const char*                     fImportedName;
-       bool                            fOrdered;
-       bool                            fHaveExportInfo;
-       uint32_t                        fTrieOffset;
-       
-       void addSymbol(const char* fullStr, uint64_t address, uint64_t flags, uint64_t other, const char* importName) {
-               const char* partialStr = &fullStr[strlen(fCummulativeString)];
-               for (std::vector<Edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) {
-                       Edge& e = *it;
-                       long subStringLen = strlen(e.fSubString);
-                       if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) {
-                               // already have matching edge, go down that path
-                               e.fChild->addSymbol(fullStr, address, flags, other, importName);
-                               return;
-                       }
-                       else {
-                               for (long i=subStringLen-1; i > 0; --i) {
-                                       if ( strncmp(e.fSubString, partialStr, i) == 0 ) {
-                                               // found a common substring, splice in new node
-                                               //  was A -> C,  now A -> B -> C
-                                               char* bNodeCummStr = strdup(e.fChild->fCummulativeString);
-                                               bNodeCummStr[strlen(bNodeCummStr)+i-subStringLen] = '\0';
-                                               //node* aNode = this;
-                                               Node* bNode = new Node(bNodeCummStr);
-                                               Node* cNode = e.fChild;
-                                               char* abEdgeStr = strdup(e.fSubString);
-                                               abEdgeStr[i] = '\0';
-                                               char* bcEdgeStr = strdup(&e.fSubString[i]);
-                                               Edge& abEdge = e;
-                                               abEdge.fSubString = abEdgeStr;
-                                               abEdge.fChild = bNode;
-                                               Edge bcEdge(bcEdgeStr, cNode);
-                                               bNode->fChildren.push_back(bcEdge);
-                                               bNode->addSymbol(fullStr, address, flags, other, importName);
-                                               return;
-                                       }
-                               }
-                       }
-               }
-
-               // no commonality with any existing child, make a new edge that is this whole string
-               Node* newNode = new Node(strdup(fullStr));
-               Edge newEdge(strdup(partialStr), newNode);
-               fChildren.push_back(newEdge);
-               newNode->fAddress = address;
-               newNode->fFlags = flags;
-               newNode->fOther = other;
-               if ( (flags & EXPORT_SYMBOL_FLAGS_REEXPORT) && (importName != NULL) && (strcmp(fullStr,importName) != 0) )
-                       newNode->fImportedName = importName;
-               else
-                       newNode->fImportedName = NULL;
-               newNode->fHaveExportInfo = true;
-       }
-       
-       void addOrderedNodes(const char* name, std::vector<Node*>& orderedNodes) {
-               if ( !fOrdered ) {
-                       orderedNodes.push_back(this);
-                       //fprintf(stderr, "ordered %p %s\n", this, fCummulativeString);
-                       fOrdered = true;
-               }
-               const char* partialStr = &name[strlen(fCummulativeString)];
-               for (std::vector<Edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) {
-                       Edge& e = *it;
-                       long subStringLen = strlen(e.fSubString);
-                       if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) {
-                               // already have matching edge, go down that path
-                               e.fChild->addOrderedNodes(name, orderedNodes);
-                               return;
-                       }
-               }
-       }
-
-       // byte for terminal node size in bytes, or 0x00 if not terminal node
-       // teminal node (uleb128 flags, uleb128 addr [uleb128 other])
-       // byte for child node count
-       //  each child: zero terminated substring, uleb128 node offset
-       bool updateOffset(uint32_t& offset) {
-               uint32_t nodeSize = 1; // length of export info when no export info
-               if ( fHaveExportInfo ) {
-                       if ( fFlags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
-                               nodeSize = uleb128_size(fFlags) + uleb128_size(fOther); // ordinal
-                               if ( fImportedName != NULL )
-                                       nodeSize += strlen(fImportedName);
-                               ++nodeSize; // trailing zero in imported name
-                       }
-                       else {
-                               nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress);
-                               if ( fFlags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER )
-                                       nodeSize += uleb128_size(fOther);
-                       }
-                       // do have export info, overall node size so far is uleb128 of export info + export info
-                       nodeSize += uleb128_size(nodeSize); 
-               }
-               // add children
-               ++nodeSize; // byte for count of chidren
-               for (std::vector<Edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) {
-                       Edge& e = *it;
-                       nodeSize += strlen(e.fSubString) + 1 + uleb128_size(e.fChild->fTrieOffset);
-               }
-               bool result = (fTrieOffset != offset);
-               fTrieOffset = offset;
-               //fprintf(stderr, "updateOffset %p %05d %s\n", this, fTrieOffset, fCummulativeString);
-               offset += nodeSize;
-               // return true if fTrieOffset was changed
-               return result;
-       }
-
-       void appendToStream(std::vector<uint8_t>& out) {
-               if ( fHaveExportInfo ) {
-                       if ( fFlags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
-                               if ( fImportedName != NULL ) {
-                                       // nodes with re-export info: size, flags, ordinal, string
-                                       uint32_t nodeSize = (uint32_t)(uleb128_size(fFlags) + uleb128_size(fOther) + strlen(fImportedName) + 1);
-                                       out.push_back(nodeSize);
-                                       append_uleb128(fFlags, out);
-                                       append_uleb128(fOther, out);
-                                       append_string(fImportedName, out);
-                               }
-                               else {
-                                       // nodes with re-export info: size, flags, ordinal, empty-string
-                                       uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fOther) + 1;
-                                       out.push_back(nodeSize);
-                                       append_uleb128(fFlags, out);
-                                       append_uleb128(fOther, out);
-                                       out.push_back(0);
-                               }
-                       }
-                       else if ( fFlags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) {
-                               // nodes with export info: size, flags, address, other
-                               uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress) + uleb128_size(fOther);
-                               out.push_back(nodeSize);
-                               append_uleb128(fFlags, out);
-                               append_uleb128(fAddress, out);
-                               append_uleb128(fOther, out);
-                       }
-                       else {
-                               // nodes with export info: size, flags, address
-                               uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress);
-                               out.push_back(nodeSize);
-                               append_uleb128(fFlags, out);
-                               append_uleb128(fAddress, out);
-                       }
-               }
-               else {
-                       // no export info uleb128 of zero is one byte of zero
-                       out.push_back(0);
-               }
-               // write number of children
-               out.push_back(fChildren.size());
-               // write each child
-               for (std::vector<Edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) {
-                       Edge& e = *it;
-                       append_string(e.fSubString, out);
-                       append_uleb128(e.fChild->fTrieOffset, out);
-               }
-       }
-       
-private:
-       static void append_uleb128(uint64_t value, std::vector<uint8_t>& out) {
-               uint8_t byte;
-               do {
-                       byte = value & 0x7F;
-                       value &= ~0x7F;
-                       if ( value != 0 )
-                               byte |= 0x80;
-                       out.push_back(byte);
-                       value = value >> 7;
-               } while( byte >= 0x80 );
-       }
-       
-       static void append_string(const char* str, std::vector<uint8_t>& out) {
-               for (const char* s = str; *s != '\0'; ++s)
-                       out.push_back(*s);
-               out.push_back('\0');
-       }
-       
-       static unsigned int     uleb128_size(uint64_t value) {
-               uint32_t result = 0;
-               do {
-                       value = value >> 7;
-                       ++result;
-               } while ( value != 0 );
-               return result;
-       }
-       
-
-};
-
-inline uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) {
-       uint64_t result = 0;
-       int              bit = 0;
-       do {
-               if (p == end)
-#if __EXCEPTIONS
-                       throw "malformed uleb128 extends beyond trie";
-#else
-                       return result;
-#endif
-               uint64_t slice = *p & 0x7f;
-
-               if (bit >= 64 || slice << bit >> bit != slice)
-#if __EXCEPTIONS
-                       throw "uleb128 too big for 64-bits";
-#else
-                       return result;
-#endif
-               else {
-                       result |= (slice << bit);
-                       bit += 7;
-               }
-       } 
-       while (*p++ & 0x80);
-       return result;
-}
-       
-
-
-struct Entry
-{
-       const char*             name;
-       uint64_t                address;
-       uint64_t                flags;
-       uint64_t                other;
-       const char*             importName;
-};
-
-
-
-inline void makeTrie(const std::vector<Entry>& entries, std::vector<uint8_t>& output)
-{
-       Node start(strdup(""));
-       
-       // make nodes for all exported symbols
-       for (std::vector<Entry>::const_iterator it = entries.begin(); it != entries.end(); ++it) {
-               start.addSymbol(it->name, it->address, it->flags, it->other, it->importName);
-       }
-
-       // create vector of nodes
-       std::vector<Node*> orderedNodes;
-       orderedNodes.reserve(entries.size()*2);
-       for (std::vector<Entry>::const_iterator it = entries.begin(); it != entries.end(); ++it) {
-               start.addOrderedNodes(it->name, orderedNodes);
-       }
-       
-       // assign each node in the vector an offset in the trie stream, iterating until all uleb128 sizes have stabilized
-       bool more;
-       do {
-               uint32_t offset = 0;
-               more = false;
-               for (std::vector<Node*>::iterator it = orderedNodes.begin(); it != orderedNodes.end(); ++it) {
-                       if ( (*it)->updateOffset(offset) )
-                               more = true;
-               }
-       } while ( more );
-       
-       // create trie stream
-       for (std::vector<Node*>::iterator it = orderedNodes.begin(); it != orderedNodes.end(); ++it) {
-               (*it)->appendToStream(output);
-       }
-}
-
-struct EntryWithOffset
-{
-       uintptr_t               nodeOffset;
-       Entry                   entry;
-       
-       bool operator<(const EntryWithOffset& other) const { return ( nodeOffset < other.nodeOffset ); }
-};
-
-
-
-static inline void processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end, 
-                                                                       char* cummulativeString, int curStrOffset, 
-                                                                       std::vector<EntryWithOffset>& output) 
-{
-       if ( p >= end )
-#if __EXCEPTIONS
-               throw "malformed trie, node past end";
-#else
-               return;
-#endif
-       const uint8_t terminalSize = read_uleb128(p, end);
-       const uint8_t* children = p + terminalSize;
-       if ( terminalSize != 0 ) {
-               EntryWithOffset e;
-               e.nodeOffset = p-start;
-               e.entry.name = strdup(cummulativeString);
-               e.entry.flags = read_uleb128(p, end);
-               if ( e.entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
-                       e.entry.address = 0;
-                       e.entry.other = read_uleb128(p, end); // dylib ordinal
-                       e.entry.importName = (char*)p;
-               }
-               else {
-                       e.entry.address = read_uleb128(p, end); 
-                       if ( e.entry.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER )
-                               e.entry.other = read_uleb128(p, end); 
-                       else
-                               e.entry.other = 0;
-                       e.entry.importName = NULL;
-               }
-               output.push_back(e);
-       }
-       const uint8_t childrenCount = *children++;
-       const uint8_t* s = children;
-       for (uint8_t i=0; i < childrenCount; ++i) {
-               int edgeStrLen = 0;
-               while (*s != '\0') {
-                       cummulativeString[curStrOffset+edgeStrLen] = *s++;
-                       ++edgeStrLen;
-               }
-               cummulativeString[curStrOffset+edgeStrLen] = *s++;
-               uint32_t childNodeOffet = (uint32_t)read_uleb128(s, end);
-               processExportNode(start, start+childNodeOffet, end, cummulativeString, curStrOffset+edgeStrLen, output);        
-       }
-}
-
-
-inline void parseTrie(const uint8_t* start, const uint8_t* end, std::vector<Entry>& output)
-{
-       // empty trie has no entries
-       if ( start == end )
-               return;
-       char cummulativeString[32000];
-       std::vector<EntryWithOffset> entries;
-       processExportNode(start, start, end, cummulativeString, 0, entries);
-       // to preserve tie layout order, sort by node offset
-       std::sort(entries.begin(), entries.end());
-       // copy to output
-       output.reserve(entries.size());
-       for (std::vector<EntryWithOffset>::iterator it=entries.begin(); it != entries.end(); ++it)
-               output.push_back(it->entry);
-}
-
-
-
-
-}; // namespace trie
-}; // namespace mach_o
-
-
-#endif // __MACH_O_TRIE__
-
-
diff --git a/launch-cache/dsc_extractor.cpp b/launch-cache/dsc_extractor.cpp
deleted file mode 100644 (file)
index 95a5fa1..0000000
+++ /dev/null
@@ -1,883 +0,0 @@
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2011 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <string.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <sys/mman.h>
-#include <sys/syslimits.h>
-#include <libkern/OSByteOrder.h>
-#include <mach-o/arch.h>
-#include <mach-o/loader.h>
-#include <Availability.h>
-
-#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"
-
-#include "dsc_iterator.h"
-#include "dsc_extractor.h"
-#include "MachOTrie.hpp"
-#include "SupportedArchs.h"
-#include "DyldSharedCache.h"
-
-#include <vector>
-#include <set>
-#include <map>
-#include <unordered_map>
-#include <algorithm>
-#include <dispatch/dispatch.h>
-
-struct seg_info
-{
-    seg_info(const char* n, uint64_t o, uint64_t s)
-    : segName(n), offset(o), sizem(s) { }
-    const char* segName;
-    uint64_t    offset;
-    uint64_t    sizem;
-};
-
-class CStringHash {
-public:
-    size_t operator()(const char* __s) const {
-        size_t __h = 0;
-        for ( ; *__s; ++__s)
-            __h = 5 * __h + *__s;
-        return __h;
-    };
-};
-class CStringEquals {
-public:
-    bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
-};
-typedef std::unordered_map<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;
-    }
-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;
-};
-
-template <typename P>
-struct LoadCommandInfo {
-};
-
-template <typename A>
-class LinkeditOptimizer {
-    typedef typename A::P P;
-    typedef typename A::P::E E;
-    typedef typename A::P::uint_t pint_t;
-
-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)
-{
-    //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>
-void 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) {
-        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;
-
-        //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);
-
-    dylib_data.insert(dylib_data.end(), new_dylib_data.begin(), new_dylib_data.end());
-}
-
-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 (auto 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_TRUNC | 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;
-    }
-
-    std::vector<uint8_t> vec;
-    context.dylib_create_func(context.mapped_cache, vec, segInfo);
-    context.progress(context.count++, (unsigned)context.map.size());
-
-    // Write file data
-    if( write(fd, &vec.front(), vec.size()) == -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);
-
-    // 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;
-    }
-
-    uint32_t pageSize = 1 << cd->pageSize;
-    uint32_t slotCountFromRegions = (uint32_t)((inBbufferSize + pageSize - 1) / pageSize);
-    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:
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-            dscDigestFormat = kCCDigestSHA1;
-#pragma clang diagnostic pop
-            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 + pageSize - 1) / pageSize);
-
-        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, (size_t)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,
-                                              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;
-    }
-
-    close(cache_fd);
-
-    // 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;
-    }
-
-    // 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;
-    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");
-        munmap(mapped_cache, (size_t)statbuf.st_size);
-        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;
-}
-
-
-
-int dyld_shared_cache_extract_dylibs(const char* shared_cache_file_path, const char* extraction_root_path)
-{
-    return dyld_shared_cache_extract_dylibs_progress(shared_cache_file_path, extraction_root_path,
-                                                     ^(unsigned , unsigned) {} );
-}
-
-
-#if 0
-// test program
-#include <stdio.h>
-#include <stddef.h>
-#include <dlfcn.h>
-
-
-typedef int (*extractor_proc)(const char* shared_cache_file_path, const char* extraction_root_path,
-                              void (^progress)(unsigned current, unsigned total));
-
-int main(int argc, const char* argv[])
-{
-    if ( argc != 3 ) {
-        fprintf(stderr, "usage: dsc_extractor <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
-
-
-
-
diff --git a/launch-cache/dsc_extractor.h b/launch-cache/dsc_extractor.h
deleted file mode 100644 (file)
index a8620d0..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 
- *
- * Copyright (c) 2010 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#ifndef _DYLD_SHARED_CACHE_EXTRACTOR_
-#define _DYLD_SHARED_CACHE_EXTRACTOR_
-
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif 
-
-extern int dyld_shared_cache_extract_dylibs(const char* shared_cache_file_path, const char* extraction_root_path);
-extern int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path, const char* extraction_root_path,
-                                                                                                       void (^progress)(unsigned current, unsigned total));
-
-#ifdef __cplusplus
-}
-#endif 
-
-#endif // _DYLD_SHARED_CACHE_EXTRACTOR_
-
diff --git a/launch-cache/dsc_iterator.cpp b/launch-cache/dsc_iterator.cpp
deleted file mode 100644 (file)
index 2196ac3..0000000
+++ /dev/null
@@ -1,255 +0,0 @@
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 
- *
- * Copyright (c) 2009-2012 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <Availability.h>
-
-
-#include "dsc_iterator.h"
-#include "dyld_cache_format.h"
-#define NO_ULEB
-#include "Architectures.hpp"
-#include "MachOFileAbstraction.hpp"
-#include "CacheFileAbstraction.hpp"
-#include "SupportedArchs.h"
-
-namespace dyld {
-
-
-       // convert an address in the shared region where the cache would normally be mapped, into an address where the cache is currently mapped
-       template <typename E>
-       const uint8_t* mappedAddress(const uint8_t* cache, const uint8_t* cacheEnd, uint64_t addr)
-       {
-               const dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)cache;
-               const dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)&cache[header->mappingOffset()];
-               for (uint32_t i=0; i < header->mappingCount(); ++i) {
-                       if ( (mappings[i].address() <= addr) &&  (addr < (mappings[i].address() + mappings[i].size())) ) {
-                               uint64_t cacheOffset = mappings[i].file_offset() + addr - mappings[i].address();
-                               const uint8_t* result =  &cache[cacheOffset];
-                               if ( result < cacheEnd )
-                                       return result;
-                               else 
-                                       return NULL;
-                       }
-               }
-               return NULL;
-       }
-
-       // call the callback block on each segment in this image                                                          
-       template <typename A>
-       int walkSegments(const uint8_t* cache, const uint8_t* cacheEnd, const uint8_t* firstSeg, const char* dylibPath, uint64_t inode,uint64_t modTime, const uint8_t* machHeader, uint64_t cache_unslid_base_address,
-                                                                                       void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) 
-       {
-               typedef typename A::P           P;      
-               typedef typename A::P::E        E;      
-               dyld_shared_cache_dylib_info    dylibInfo;
-               dyld_shared_cache_segment_info  segInfo;
-               dylibInfo.version = 2;
-               dylibInfo.isAlias = (dylibPath < (char*)firstSeg); // paths for aliases are store between cache header and first segment
-               dylibInfo.machHeader = machHeader;
-               dylibInfo.path = dylibPath;
-               dylibInfo.inode = inode;
-               dylibInfo.modTime = modTime;
-               const macho_header<P>* mh = (const macho_header<P>*)machHeader;
-               const macho_load_command<P>* const cmds = (macho_load_command<P>*)(machHeader + sizeof(macho_header<P>));
-               if ( (machHeader+ mh->sizeofcmds()) > cacheEnd )
-                       return -1;
-               const uint32_t cmd_count = mh->ncmds();
-               const macho_load_command<P>* cmd = cmds;
-               // scan for LC_UUID
-               dylibInfo.uuid = NULL;
-               for (uint32_t i = 0; i < cmd_count; ++i) {
-                       if ( cmd->cmd() == LC_UUID ) {
-                               const uuid_command* uc = (const uuid_command*)cmd;
-                               dylibInfo.uuid = &uc->uuid;
-                               break;
-                       }
-                       cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-               }
-               // callback for each LC_SEGMENT
-               cmd = cmds;
-               for (uint32_t i = 0; i < cmd_count; ++i) {
-                       if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
-                               macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
-                               uint64_t fileOffset = segCmd->fileoff();
-                               // work around until <rdar://problem/7022345> is fixed
-                               if ( fileOffset == 0 ) {
-                                       fileOffset = (machHeader - cache);
-                               }
-                               uint64_t sizem = segCmd->vmsize();
-                               if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
-                                       // clip LINKEDIT size if bigger than cache file
-                                       if ( (fileOffset+sizem) > (uint64_t)(cacheEnd-cache) )
-                                               sizem = (cacheEnd-cache)-fileOffset;
-                               }
-                               segInfo.version = 2;
-                               segInfo.name = segCmd->segname();
-                               segInfo.fileOffset = fileOffset;
-                               segInfo.fileSize = sizem;
-                               if ( segCmd->filesize() > segCmd->vmsize() )
-                                       return -1;
-                               segInfo.address = segCmd->vmaddr();
-                segInfo.addressOffset = segInfo.address - cache_unslid_base_address;
-                               callback(&dylibInfo, &segInfo);
-                       }
-                       cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
-               }
-               return 0;
-       }
-                       
-                                                         
-       // call walkSegments on each image in the cache                                                           
-       template <typename A>
-       int walkImages(const uint8_t* cache, uint32_t size,     void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) 
-       {
-               // Sanity check there is at least a header
-               if ( (size > 0) && (size < 0x7000) )
-                       return -1;
-               typedef typename A::P::E                        E;      
-               typedef typename A::P                           P;      
-               const dyldCacheHeader<E>*      header   = (dyldCacheHeader<E>*)cache;
-               const dyldCacheImageInfo<E>*   dylibs   = (dyldCacheImageInfo<E>*)&cache[header->imagesOffset()];
-               const dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)&cache[header->mappingOffset()];
-        uint64_t unslid_base_address = mappings[0].address();
-               uint64_t greatestMappingOffset = 0;
-               for (uint32_t i=0; i < header->mappingCount(); ++i) {
-                       if ( (size != 0) && (mappings[i].file_offset() > size) )
-                               return -1;
-                       uint64_t endOffset = mappings[i].file_offset()+mappings[i].size();
-                       if ( (size != 0) && (endOffset > size) )
-                               return -1;
-                       if ( endOffset > greatestMappingOffset ) 
-                               greatestMappingOffset = endOffset;
-               }
-               const uint8_t* cacheEnd = &cache[size];
-               if ( size == 0 ) {
-                       // Zero size means old API is being used, assume all mapped
-                       cacheEnd = &cache[greatestMappingOffset];
-               }
-               else {
-                       // verifiy mappings are not bigger than size
-                       if ( size < greatestMappingOffset ) 
-                               return -1;
-               }
-               // verify all image infos are mapped
-               if ( (const uint8_t*)&dylibs[header->imagesCount()] > cacheEnd )
-                       return -1;
-               const uint8_t* firstSeg = NULL;
-               for (uint32_t i=0; i < header->imagesCount(); ++i) {
-                       const char* dylibPath  = (char*)cache + dylibs[i].pathFileOffset();
-                       uint64_t inode = dylibs[i].inode();
-                       uint64_t modTime = dylibs[i].modTime();
-                       if ( (const uint8_t*)dylibPath > cacheEnd )
-                               return -1;
-                       const uint8_t* machHeader = mappedAddress<E>(cache, cacheEnd, dylibs[i].address());
-                       if ( machHeader == NULL )
-                               return -1;
-                       if ( machHeader > cacheEnd )
-                               return -1;
-                       if ( firstSeg == NULL )
-                               firstSeg = machHeader;
-                       int result = walkSegments<A>(cache, cacheEnd, firstSeg, dylibPath, inode, modTime, machHeader, unslid_base_address, callback);
-                       if ( result != 0 )
-                               return result;
-               }
-               return 0;
-       }
-
-}
-
-
-// Given a pointer to an in-memory copy of a dyld shared cache file,
-// this routine will call the callback block once for each segment
-// in each dylib in the shared cache file.  
-// Returns -1 if there was an error, otherwise 0.
-extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t shared_cache_size, 
-                                                                       void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) {
-       const uint8_t* cache = (uint8_t*)shared_cache_file;
-                if ( strcmp((char*)cache, "dyld_v1    i386") == 0 ) 
-                       return dyld::walkImages<x86>(cache, shared_cache_size, callback);
-       else if ( strcmp((char*)cache, "dyld_v1  x86_64") == 0 ) 
-                       return dyld::walkImages<x86_64>(cache, shared_cache_size, callback);
-       else if ( strcmp((char*)cache, "dyld_v1 x86_64h") == 0 ) 
-                       return dyld::walkImages<x86_64>(cache, shared_cache_size, callback);
-       else if ( strcmp((char*)cache, "dyld_v1   armv5") == 0 ) 
-                       return dyld::walkImages<arm>(cache, shared_cache_size, callback);
-       else if ( strcmp((char*)cache, "dyld_v1   armv6") == 0 ) 
-                       return dyld::walkImages<arm>(cache, shared_cache_size, callback);
-       else if ( strcmp((char*)cache, "dyld_v1   armv7") == 0 ) 
-                       return dyld::walkImages<arm>(cache, shared_cache_size, callback);
-       else if ( strncmp((char*)cache, "dyld_v1  armv7", 14) == 0 ) 
-                       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;
-}
-
-
-// implement old version by calling new version
-int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback)
-{
-       return dyld_shared_cache_iterate(shared_cache_file, 0, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) {
-                                                                                                               callback(dylibInfo->path, segInfo->name, segInfo->fileOffset, segInfo->fileSize, segInfo->address, 0);
-       });
-}
-
-// implement non-block version by calling block version
-int dyld_shared_cache_iterate_segments_with_slide_nb(const void* shared_cache_file, dyld_shared_cache_iterator_slide_nb_t func, void* userData)
-{
-       return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, ^(const char* dylibName, const char* segName, 
-                                                                                                       uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide) {
-                                                                                                               (*func)(dylibName, segName, offset, size, mappedddress, slide, userData);
-       });
-}
-
-
-// implement non-slide version by wrapping slide version in block
-int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback)
-{
-       dyld_shared_cache_iterator_slide_t wrapper_cb = ^(const char* dylibName, const char* segName, uint64_t offset, 
-                                                                                                               uint64_t size, uint64_t mappedddress, uint64_t slide) {
-                                                                                                               callback(dylibName, segName, offset, size, mappedddress);
-                                                                                                       };
-       return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, wrapper_cb);
-}
-
-// implement non-slide,non-block version by wrapping slide version in block
-int dyld_shared_cache_iterate_segments_nb(const void* shared_cache_file, dyld_shared_cache_iterator_nb_t func, void* userData)
-{
-       return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, ^(const char* dylibName, const char* segName, 
-                                                                                                                                                         uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide) {
-               (*func)(dylibName, segName, offset, size, mappedddress, userData);
-       });
-}
-
diff --git a/launch-cache/dsc_iterator.h b/launch-cache/dsc_iterator.h
deleted file mode 100644 (file)
index d0ea94d..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 
- *
- * Copyright (c) 2009-2012 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include <stdint.h>
-#include <uuid/uuid.h>
-
-struct dyld_shared_cache_dylib_info {
-       uint32_t                version;                // current version 2
-       // following fields all exist in version 1
-       uint32_t                isAlias;                // this is alternate path (symlink)
-       const void*             machHeader;             // of dylib in mapped cached file
-       const char*             path;                   // of dylib
-       const uuid_t*   uuid;                   // of dylib, or NULL is missing
-       // following fields all exist in version 2
-       uint64_t                inode;                  // of dylib file or path hash
-       uint64_t                modTime;                // of dylib file
-};
-typedef struct dyld_shared_cache_dylib_info dyld_shared_cache_dylib_info;
-
-struct dyld_shared_cache_segment_info {
-       uint64_t                version;                // initial version 1
-       // following fields exist in version 1
-       const char*             name;                   // of segment
-       uint64_t                fileOffset;             // of segment in cache file
-       uint64_t                fileSize;               // of segment
-       uint64_t                address;                // of segment when cache mapped with ASLR (sliding) off
-    // following fields exist in version 2
-       uint64_t                addressOffset;  // of segment from base of mapped cache
-};
-typedef struct dyld_shared_cache_segment_info dyld_shared_cache_segment_info;
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif 
-
-
-// Given a pointer and size of an in-memory copy of a dyld shared cache file,
-// this routine will call the callback block once for each segment in each dylib 
-// in the shared cache file.  
-// Returns -1 if there was an error, otherwise 0.
-extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t shared_cache_size, 
-                                                                       void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo));
-
-
-
-//
-// The following iterator functions are deprecated:
-//
-typedef void (^dyld_shared_cache_iterator_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t size, uint64_t mappedddress);
-typedef void (^dyld_shared_cache_iterator_slide_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide);
-typedef void (*dyld_shared_cache_iterator_nb_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t sizem, uint64_t mappedddress, void* userData);
-typedef void (*dyld_shared_cache_iterator_slide_nb_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t sizem, uint64_t mappedddress, uint64_t slide, void* userData);
-
-extern int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback) __attribute__((deprecated));
-extern int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback) __attribute__((deprecated));
-extern int dyld_shared_cache_iterate_segments_nb(const void* shared_cache_file, dyld_shared_cache_iterator_nb_t callback, void* userData) __attribute__((deprecated));
-extern int dyld_shared_cache_iterate_segments_with_slide_nb(const void* shared_cache_file, dyld_shared_cache_iterator_slide_nb_t callback, void* userData) __attribute__((deprecated));
-
-
-#ifdef __cplusplus
-}
-#endif 
diff --git a/launch-cache/dyld_cache_format.h b/launch-cache/dyld_cache_format.h
deleted file mode 100644 (file)
index eda7c61..0000000
+++ /dev/null
@@ -1,375 +0,0 @@
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 
- *
- * Copyright (c) 2006-2015 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
-#ifndef __DYLD_CACHE_FORMAT__
-#define __DYLD_CACHE_FORMAT__
-
-#include <sys/types.h>
-#include <stdint.h>
-#include <mach/shared_region.h>
-#include <uuid/uuid.h>
-
-
-struct dyld_cache_header
-{
-       char            magic[16];                              // e.g. "dyld_v0    i386"
-       uint32_t        mappingOffset;                  // file offset to first dyld_cache_mapping_info
-       uint32_t        mappingCount;                   // number of dyld_cache_mapping_info entries
-       uint32_t        imagesOffset;                   // file offset to first dyld_cache_image_info
-       uint32_t        imagesCount;                    // number of dyld_cache_image_info entries
-       uint64_t        dyldBaseAddress;                // base address of dyld when cache was built
-       uint64_t        codeSignatureOffset;    // file offset of code signature blob
-       uint64_t        codeSignatureSize;              // size of code signature blob (zero means to end of file)
-       uint64_t        slideInfoOffset;                // file offset of kernel slid info
-       uint64_t        slideInfoSize;                  // size of kernel slid info
-       uint64_t        localSymbolsOffset;             // file offset of where local symbols are stored
-       uint64_t        localSymbolsSize;               // size of local symbols information
-       uint8_t         uuid[16];                               // unique value for each shared cache file
-       uint64_t        cacheType;                              // 0 for development, 1 for production
-       uint32_t        branchPoolsOffset;              // file offset to table of uint64_t pool addresses
-       uint32_t        branchPoolsCount;           // number of uint64_t entries
-       uint64_t        accelerateInfoAddr;             // (unslid) address of optimization info
-       uint64_t        accelerateInfoSize;             // size of optimization info
-       uint64_t        imagesTextOffset;               // file offset to first dyld_cache_image_text_info
-       uint64_t        imagesTextCount;                // number of dyld_cache_image_text_info entries
-};
-
-
-struct dyld_cache_mapping_info {
-       uint64_t        address;
-       uint64_t        size;
-       uint64_t        fileOffset;
-       uint32_t        maxProt;
-       uint32_t        initProt;
-};
-
-struct dyld_cache_image_info
-{
-       uint64_t        address;
-       uint64_t        modTime;
-       uint64_t        inode;
-       uint32_t        pathFileOffset;
-       uint32_t        pad;
-};
-
-struct dyld_cache_image_info_extra
-{
-       uint64_t        exportsTrieAddr;                // address of trie in unslid cache
-       uint64_t        weakBindingsAddr;
-       uint32_t        exportsTrieSize;
-       uint32_t        weakBindingsSize;
-       uint32_t        dependentsStartArrayIndex;
-       uint32_t        reExportsStartArrayIndex;
-};
-
-
-struct dyld_cache_accelerator_info
-{
-       uint32_t        version;                                // currently 1
-       uint32_t        imageExtrasCount;               // does not include aliases
-       uint32_t        imagesExtrasOffset;             // offset into this chunk of first dyld_cache_image_info_extra
-       uint32_t        bottomUpListOffset;             // offset into this chunk to start of 16-bit array of sorted image indexes
-       uint32_t        dylibTrieOffset;                // offset into this chunk to start of trie containing all dylib paths
-       uint32_t        dylibTrieSize;                  // size of trie containing all dylib paths
-       uint32_t        initializersOffset;             // offset into this chunk to start of initializers list
-       uint32_t        initializersCount;              // size of initializers list
-       uint32_t        dofSectionsOffset;              // offset into this chunk to start of DOF sections list
-       uint32_t        dofSectionsCount;               // size of initializers list
-       uint32_t        reExportListOffset;             // offset into this chunk to start of 16-bit array of re-exports
-       uint32_t        reExportCount;                  // size of re-exports
-       uint32_t        depListOffset;                  // offset into this chunk to start of 16-bit array of dependencies (0x8000 bit set if upward)
-       uint32_t        depListCount;                   // size of dependencies
-       uint32_t        rangeTableOffset;               // offset into this chunk to start of ss
-       uint32_t        rangeTableCount;                // size of dependencies
-       uint64_t        dyldSectionAddr;                // address of libdyld's __dyld section in unslid cache
-};
-
-struct dyld_cache_accelerator_initializer
-{
-       uint32_t        functionOffset;                 // address offset from start of cache mapping
-       uint32_t        imageIndex;
-};
-
-struct dyld_cache_range_entry
-{
-       uint64_t        startAddress;                   // unslid address of start of region
-       uint32_t        size;
-       uint32_t        imageIndex;
-};
-
-struct dyld_cache_accelerator_dof
-{
-       uint64_t        sectionAddress;                 // unslid address of start of region
-       uint32_t        sectionSize;
-       uint32_t        imageIndex;
-};
-
-struct dyld_cache_image_text_info
-{
-       uuid_t          uuid;
-       uint64_t        loadAddress;                    // unslid address of start of __TEXT
-       uint32_t        textSegmentSize;
-       uint32_t        pathOffset;                             // offset from start of cache file
-};
-
-// The rebasing info is to allow the kernel to lazily rebase DATA pages of the
-// dyld shared cache.  Rebasing is adding the slide to interior pointers.
-struct dyld_cache_slide_info
-{
-       uint32_t        version;                // currently 1
-       uint32_t        toc_offset;
-       uint32_t        toc_count;
-       uint32_t        entries_offset;
-       uint32_t        entries_count;
-       uint32_t        entries_size;  // currently 128 
-       // uint16_t toc[toc_count];
-       // entrybitmap entries[entries_count];
-};
-
-
-// The version 2 of the slide info uses a different compression scheme. Since
-// only interior pointers (pointers that point within the cache) are rebased
-// (slid), we know the possible range of the pointers and thus know there are
-// unused bits in each pointer.  We use those bits to form a linked list of
-// locations needing rebasing in each page.
-//
-// Definitions:
-//
-//  pageIndex = (pageAddress - startOfAllDataAddress)/info->page_size
-//  pageStarts[] = info + info->page_starts_offset
-//  pageExtras[] = info + info->page_extras_offset
-//  valueMask = ~(info->delta_mask)
-//  deltaShift = __builtin_ctzll(info->delta_mask) - 2
-//
-// There are three cases:
-//
-// 1) pageStarts[pageIndex] == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE
-//    The page contains no values that need rebasing.
-//
-// 2) (pageStarts[pageIndex] & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0
-//    All rebase locations are in one linked list. The offset of the first
-//    rebase location in the page is pageStarts[pageIndex] * 4.
-//
-// 3) pageStarts[pageIndex] & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA
-//       Multiple linked lists are needed for all rebase locations in a page.
-//    The pagesExtras array contains 2 or more entries each of which is the
-//    start of a new linked list in the page. The first is at:
-//       extrasStartIndex = (pageStarts[pageIndex] & 0x3FFF)
-//       The next is at extrasStartIndex+1.  The last is denoted by
-//    having the high bit (DYLD_CACHE_SLIDE_PAGE_ATTR_END) of the pageExtras[]
-//    set.
-//
-// For 64-bit architectures, there is always enough free bits to encode all
-// possible deltas.  The info->delta_mask field shows where the delta is located
-// in the pointer.  That value must be masked off (valueMask) before the slide
-// is added to the pointer.
-//
-// For 32-bit architectures, there are only three bits free (the three most
-// significant bits). To extract the delta, you must first subtract value_add
-// from the pointer value, then AND with delta_mask, then shift by deltaShift.
-// That still leaves a maximum delta to the next rebase location of 28 bytes.
-// To reduce the number or chains needed, an optimization was added.  Turns
-// out zero is common in the DATA region.  A zero can be turned into a
-// non-rebasing entry in the linked list.  The can be done because nothing
-// in the shared cache should point out of its dylib to the start of the shared
-// cache.
-//
-// The code for processing a linked list (chain) is:
-//   
-//     uint32_t delta = 1;
-//     while ( delta != 0 ) {
-//             uint8_t* loc = pageStart + pageOffset;
-//             uintptr_t rawValue = *((uintptr_t*)loc);
-//             delta = ((rawValue & deltaMask) >> deltaShift);
-//             uintptr_t newValue = (rawValue & valueMask);
-//             if ( newValue != 0 ) {
-//                     newValue += valueAdd;
-//                     newValue += slideAmount;
-//             }
-//             *((uintptr_t*)loc) = newValue;
-//             pageOffset += delta;
-//     }
-//
-//
-struct dyld_cache_slide_info2
-{
-       uint32_t        version;                // currently 2
-       uint32_t        page_size;              // currently 4096 (may also be 16384)
-       uint32_t        page_starts_offset;
-       uint32_t        page_starts_count;
-       uint32_t        page_extras_offset;
-       uint32_t        page_extras_count;
-       uint64_t        delta_mask;             // which (contiguous) set of bits contains the delta to the next rebase location
-       uint64_t        value_add;
-       //uint16_t      page_starts[page_starts_count];
-       //uint16_t      page_extras[page_extras_count];
-};
-#define DYLD_CACHE_SLIDE_PAGE_ATTRS                            0xC000  // high bits of uint16_t are flags
-#define DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA               0x8000  // index is into extras array (not starts array)
-#define DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE   0x4000  // page has no rebasing
-#define DYLD_CACHE_SLIDE_PAGE_ATTR_END                 0x8000  // last chain entry for page
-
-
-
-// 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
-       uint32_t        nlistCount;                     // count of nlist entries
-       uint32_t        stringsOffset;          // offset into this chunk of string pool
-       uint32_t        stringsSize;            // byte count of string pool
-       uint32_t        entriesOffset;          // offset into this chunk of array of dyld_cache_local_symbols_entry 
-       uint32_t        entriesCount;           // number of elements in dyld_cache_local_symbols_entry array
-};
-
-struct dyld_cache_local_symbols_entry
-{
-       uint32_t        dylibOffset;            // offset in cache file of start of dylib
-       uint32_t        nlistStartIndex;        // start index of locals for this dylib
-       uint32_t        nlistCount;                     // number of local symbols for this dylib
-};
-
-struct dyld_cache_image_patches
-{
-    uint32_t    patchExportsStartIndex;
-    uint32_t    patchExportsCount;
-};
-
-struct dyld_cache_patchable_export
-{
-    uint32_t    cacheOffsetOfImpl;
-    uint32_t    patchLocationsStartIndex;
-    uint32_t    patchLocationsCount;
-    uint32_t    exportNameOffset;
-};
-
-struct dyld_cache_patchable_location
-{
-    uint64_t    cacheOffset             : 32,
-                addend                  : 12,    // +/- 2048
-                authenticated           : 1,
-                usesAddressDiversity    : 1,
-                key                     : 2,
-                discriminator           : 16;
-
-    dyld_cache_patchable_location(size_t cacheOffset, uint64_t addend);
-    //dyld_cache_patchable_location(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 dyld_cache_patchable_location& other) const {
-        return this->cacheOffset == other.cacheOffset;
-    }
-};
-
-
-
-#define MACOSX_DYLD_SHARED_CACHE_DIR   "/private/var/db/dyld/"
-#define IPHONE_DYLD_SHARED_CACHE_DIR   "/System/Library/Caches/com.apple.dyld/"
-#define DYLD_SHARED_CACHE_BASE_NAME            "dyld_shared_cache_"
-#define DYLD_SHARED_CACHE_DEVELOPMENT_EXT ".development"
-
-static const uint64_t kDyldSharedCacheTypeDevelopment = 0;
-static const uint64_t kDyldSharedCacheTypeProduction = 1;
-
-
-
-
-#endif // __DYLD_CACHE_FORMAT__
-
-
diff --git a/launch-cache/dyld_shared_cache_util.cpp b/launch-cache/dyld_shared_cache_util.cpp
deleted file mode 100644 (file)
index a4a81b2..0000000
+++ /dev/null
@@ -1,1592 +0,0 @@
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
- *
- * Copyright (c) 2009-2012 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <string.h>
-#include <dlfcn.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <sys/mman.h>
-#include <sys/syslimits.h>
-#include <mach-o/arch.h>
-#include <mach-o/loader.h>
-#include <mach-o/dyld_priv.h>
-#include <bootstrap.h>
-#include <mach/mach.h>
-#include <dispatch/dispatch.h>
-
-#include <TargetConditionals.h>
-
-#include <map>
-#include <vector>
-#include <iostream>
-#include <optional>
-
-#include "ClosureBuilder.h"
-#include "DyldSharedCache.h"
-#include "ClosureFileSystemPhysical.h"
-#include "JSONWriter.h"
-#include "Trie.hpp"
-
-#include "objc-shared-cache.h"
-
-#if TARGET_OS_OSX
-#define DSC_BUNDLE_REL_PATH "../../lib/dsc_extractor.bundle"
-#else
-#define DSC_BUNDLE_REL_PATH "../lib/dsc_extractor.bundle"
-#endif
-
-using dyld3::closure::ClosureBuilder;
-using dyld3::closure::FileSystemPhysical;
-
-// 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) ) {
-        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);
-    
-    return (DyldSharedCache*)result;
-}
-
-enum Mode {
-    modeNone,
-    modeList,
-    modeMap,
-    modeDependencies,
-    modeSlideInfo,
-    modeVerboseSlideInfo,
-    modeAcceleratorInfo,
-    modeTextInfo,
-    modeLinkEdit,
-    modeLocalSymbols,
-    modeJSONMap,
-    modeJSONDependents,
-    modeSectionSizes,
-    modeStrings,
-    modeInfo,
-    modeSize,
-    modeObjCProtocols,
-    modeObjCClasses,
-    modeObjCSelectors,
-    modeExtract
-};
-
-struct Options {
-    Mode            mode;
-    const char*     dependentsOfPath;
-    const char*     extractionDir;
-    bool            printUUIDs;
-    bool            printVMAddrs;
-    bool            printDylibVersions;
-    bool            printInodes;
-};
-
-
-void usage() {
-    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");
-}
-
-static void checkMode(Mode mode) {
-    if ( mode != modeNone ) {
-        fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -verbose_slide_info, -linkedit, -map, -extract, or -size\n");
-        usage();
-        exit(1);
-    }
-}
-
-static bool isAlias(const char* path, const DyldSharedCache* dyldCache) {
-    const dyld_cache_header* header = &dyldCache->header;
-    const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)((char*)dyldCache + header->mappingOffset);
-    const dyld_cache_mapping_info* textMapping = &mappings[0];
-    // paths for aliases are store between cache header and first segment
-    return path < (char*)textMapping;
-}
-
-int main (int argc, const char* argv[]) {
-
-    const char* sharedCachePath = nullptr;
-
-    Options options;
-    options.mode = modeNone;
-    options.printUUIDs = false;
-    options.printVMAddrs = false;
-    options.printDylibVersions = false;
-    options.printInodes = false;
-    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] == '-') {
-            if (strcmp(opt, "-list") == 0) {
-                checkMode(options.mode);
-                options.mode = modeList;
-            }
-            else if (strcmp(opt, "-dependents") == 0) {
-                checkMode(options.mode);
-                options.mode = modeDependencies;
-                options.dependentsOfPath = argv[++i];
-                if ( i >= argc ) {
-                    fprintf(stderr, "Error: option -depdendents requires an argument\n");
-                    usage();
-                    exit(1);
-                }
-            }
-            else if (strcmp(opt, "-linkedit") == 0) {
-                checkMode(options.mode);
-                options.mode = modeLinkEdit;
-            }
-            else if (strcmp(opt, "-info") == 0) {
-                checkMode(options.mode);
-                options.mode = modeInfo;
-            }
-            else if (strcmp(opt, "-slide_info") == 0) {
-                checkMode(options.mode);
-                options.mode = modeSlideInfo;
-            }
-            else if (strcmp(opt, "-verbose_slide_info") == 0) {
-                checkMode(options.mode);
-                options.mode = modeVerboseSlideInfo;
-            }
-            else if (strcmp(opt, "-accelerator_info") == 0) {
-                checkMode(options.mode);
-                options.mode = modeAcceleratorInfo;
-            }
-            else if (strcmp(opt, "-text_info") == 0) {
-                checkMode(options.mode);
-                options.mode = modeTextInfo;
-            }
-            else if (strcmp(opt, "-local_symbols") == 0) {
-                checkMode(options.mode);
-                options.mode = modeLocalSymbols;
-            }
-            else if (strcmp(opt, "-strings") == 0) {
-                if (options.mode != modeStrings)
-                    checkMode(options.mode);
-                options.mode = modeStrings;
-                printStrings = true;
-            }
-            else if (strcmp(opt, "-sections") == 0) {
-                checkMode(options.mode);
-                options.mode = modeSectionSizes;
-            }
-            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;
-            }
-            else if (strcmp(opt, "-json-map") == 0) {
-                checkMode(options.mode);
-                options.mode = modeJSONMap;
-            }
-            else if (strcmp(opt, "-json-dependents") == 0) {
-                checkMode(options.mode);
-                options.mode = modeJSONDependents;
-            }
-            else if (strcmp(opt, "-size") == 0) {
-                checkMode(options.mode);
-                options.mode = modeSize;
-            }
-            else if (strcmp(opt, "-objc-protocols") == 0) {
-                checkMode(options.mode);
-                options.mode = modeObjCProtocols;
-            }
-            else if (strcmp(opt, "-objc-classes") == 0) {
-                checkMode(options.mode);
-                options.mode = modeObjCClasses;
-            }
-            else if (strcmp(opt, "-objc-selectors") == 0) {
-                checkMode(options.mode);
-                options.mode = modeObjCSelectors;
-            }
-            else if (strcmp(opt, "-extract") == 0) {
-                checkMode(options.mode);
-                options.mode = modeExtract;
-                options.extractionDir = argv[++i];
-                if ( i >= argc ) {
-                    fprintf(stderr, "Error: option -extract requires a directory argument\n");
-                    usage();
-                    exit(1);
-                }
-            }
-            else if (strcmp(opt, "-uuid") == 0) {
-                options.printUUIDs = true;
-            }
-            else if (strcmp(opt, "-inode") == 0) {
-                options.printInodes = true;
-            }
-            else if (strcmp(opt, "-versions") == 0) {
-                options.printDylibVersions = true;
-            }
-            else if (strcmp(opt, "-vmaddr") == 0) {
-                options.printVMAddrs = true;
-            }
-            else {
-                fprintf(stderr, "Error: unrecognized option %s\n", opt);
-                usage();
-                exit(1);
-            }
-        }
-        else {
-            sharedCachePath = opt;
-        }
-    }
-
-    if ( options.mode == modeNone ) {
-        fprintf(stderr, "Error: select one of -list, -dependents, -info, -linkedit, or -map\n");
-        usage();
-        exit(1);
-    }
-
-    if ( options.mode != modeSlideInfo && options.mode != modeVerboseSlideInfo ) {
-        if ( options.printUUIDs && (options.mode != modeList) )
-            fprintf(stderr, "Warning: -uuid option ignored outside of -list mode\n");
-
-        if ( options.printVMAddrs && (options.mode != modeList) )
-            fprintf(stderr, "Warning: -vmaddr option ignored outside of -list mode\n");
-
-        if ( options.printDylibVersions && (options.mode != modeDependencies) )
-            fprintf(stderr, "Warning: -versions option ignored outside of -dependents mode\n");
-
-        if ( (options.mode == modeDependencies) && (options.dependentsOfPath == NULL) ) {
-            fprintf(stderr, "Error: -dependents given, but no dylib path specified\n");
-            usage();
-            exit(1);
-        }
-    }
-
-    const DyldSharedCache* dyldCache = nullptr;
-    if ( sharedCachePath != nullptr ) {
-        dyldCache = mapCacheFile(sharedCachePath);
-        // mapCacheFile prints an error if something goes wrong, so just return in that case.
-        if ( dyldCache == nullptr )
-            return 1;
-    }
-    else {
-#if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101300)
-        size_t cacheLength;
-        dyldCache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLength);
-#endif
-        if (dyldCache == nullptr) {
-            fprintf(stderr, "Could not get in-memory shared cache\n");
-            return 1;
-        }
-        if ( options.mode == modeObjCClasses ) {
-            fprintf(stderr, "Cannot use -objc-info with a live cache.  Please run with a path to an on-disk cache file\n");
-            return 1;
-        }
-    }
-
-    if ( options.mode == modeSlideInfo || options.mode == modeVerboseSlideInfo ) {
-        const dyld_cache_header* header = &dyldCache->header;
-        if ( header->slideInfoOffset == 0 ) {
-            fprintf(stderr, "Error: dyld shared cache does not contain slide info\n");
-            exit(1);
-        }
-        const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)((char*)dyldCache + header->mappingOffset);
-        const dyld_cache_mapping_info* dataMapping = &mappings[1];
-        uint64_t dataStartAddress = dataMapping->address;
-        uint64_t dataSize = dataMapping->size;
-
-        const dyld_cache_slide_info* slideInfoHeader = dyldCache->slideInfo();
-        printf("slide info version=%d\n", slideInfoHeader->version);
-        if ( slideInfoHeader->version == 1 ) {
-            printf("toc_count=%d, data page count=%lld\n", slideInfoHeader->toc_count, dataSize/4096);
-            const dyld_cache_slide_info_entry* entries = (dyld_cache_slide_info_entry*)((char*)slideInfoHeader + slideInfoHeader->entries_offset);
-            const uint16_t* tocs = (uint16_t*)((char*)slideInfoHeader + slideInfoHeader->toc_offset);
-            for(int i=0; i < slideInfoHeader->toc_count; ++i) {
-                printf("0x%08llX: [% 5d,% 5d] ", dataStartAddress + i*4096, i, tocs[i]);
-                const dyld_cache_slide_info_entry* entry = &entries[tocs[i]];
-                for(int j=0; j < slideInfoHeader->entries_size; ++j)
-                    printf("%02X", entry->bits[j]);
-                printf("\n");
-            }
-        }
-        else if ( slideInfoHeader->version == 2 ) {
-            const dyld_cache_slide_info2* slideInfo = (dyld_cache_slide_info2*)(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 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 = 0x%016llX\n", i, (uint64_t)(pageOffset), (uint64_t)rawValue, (uint64_t)value);
-                        pageOffset += delta;
-                    }
-                };
-                const uint8_t* dataPagesStart = dyldCache->dataRegionStart();
-                if ( start == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) {
-                    printf("page[% 5d]: no rebasing\n", i);
-                }
-                else if ( start & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) {
-                    printf("page[% 5d]: ", i);
-                    int j=(start & 0x3FFF);
-                    bool done = false;
-                    do {
-                        uint16_t aStart = extras[j];
-                        printf("start=0x%04X ", aStart & 0x3FFF);
-                        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 );
-                    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;
-                        rebaseChain(page, pageStartOffset);
-                    }
-                }
-            }
-        }
-        else if ( slideInfoHeader->version == 3 ) {
-            const dyld_cache_slide_info3* slideInfo = (dyld_cache_slide_info3*)(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 uintptr_t authValueAdd = (uintptr_t)(slideInfo->auth_value_add);
-            const uint8_t* dataSegmentStart = dyldCache->dataRegionStart();
-            for (int i=0; i < slideInfo->page_starts_count; ++i) {
-                uint16_t delta = slideInfo->page_starts[i];
-                if ( delta == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE ) {
-                    printf("page[% 5d]: no rebasing\n", i);
-                    continue;
-                }
-
-                printf("page[% 5d]: start=0x%04X\n", i, delta);
-                if ( options.mode != modeVerboseSlideInfo )
-                    continue;
-
-                delta = delta/sizeof(uint64_t); // initial offset is byte based
-                const uint8_t* pageStart = dataSegmentStart + (i * slideInfo->page_size);
-                const dyld_cache_slide_pointer3* loc = (dyld_cache_slide_pointer3*)pageStart;
-                do {
-                    loc += delta;
-                    delta = loc->plain.offsetToNextPointer;
-                    dyld3::MachOLoaded::ChainedFixupPointerOnDisk ptr;
-                    ptr.raw64 = *((uint64_t*)loc);
-                    if ( loc->auth.authenticated ) {
-                        uint64_t target = authValueAdd + loc->auth.offsetFromSharedCacheBase;
-                        uint64_t targetValue = ptr.arm64e.signPointer((void*)loc, target);
-                        printf("    [% 5d + 0x%04llX]: 0x%016llX (JOP: diversity %d, address %s, %s)\n",
-                               i, (uint64_t)((const uint8_t*)loc - pageStart), targetValue,
-                               ptr.arm64e.authBind.diversity, ptr.arm64e.authBind.addrDiv ? "true" : "false",
-                               ptr.arm64e.keyName());
-                    }
-                    else {
-                        uint64_t targetValue = ptr.arm64e.unpackTarget();
-                        printf("    [% 5d + 0x%04llX]: 0x%016llX\n", i, (uint64_t)((const uint8_t*)loc - 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 = dyldCache->dataRegionStart();
-                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);
-                    }
-                }
-            }
-        }
-    }
-    else if ( options.mode == modeInfo ) {
-        const dyld_cache_header* header = &dyldCache->header;
-        printf("uuid: ");
-        if ( header->mappingOffset >= 0x68 ) {
-            const uint8_t* uuid = header->uuid;
-            printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X\n",
-                   uuid[0],  uuid[1],  uuid[2],  uuid[3],
-                   uuid[4],  uuid[5],  uuid[6],  uuid[7],
-                   uuid[8],  uuid[9],  uuid[10], uuid[11],
-                   uuid[12], uuid[13], uuid[14], uuid[15]);
-        }
-        else {
-            printf("n/a\n");
-        }
-        if ( header->mappingOffset >= 0xE0 ) {
-            // HACK until this uses new header
-            uint32_t platform = *((uint32_t*)(((char*)header) + 0xD8));
-            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 )
-                        printf("platform: iOS simulator\n");
-                    else
-                        printf("platform: iOS\n");
-                    break;
-                case 3:
-                    if ( simulator )
-                        printf("platform: tvOS simulator\n");
-                    else
-                        printf("platform: tvOS\n");
-                    break;
-                case 4:
-                    if ( simulator )
-                        printf("platform: watchOS simulator\n");
-                    else
-                        printf("platform: watchOS\n");
-                    break;
-                case 5:
-                    printf("platform: bridgeOS\n");
-                    break;
-                default:
-                    printf("platform: 0x%08X 0x%08X\n", platform, simulator);
-            }
-            printf("built by: %s\n", locallyBuiltCache ? "local machine" : "B&I");
-        }
-        printf("cache type: %s\n", header->cacheType ? "production" : "development");
-        printf("image count: %u\n", header->imagesCount);
-        if ( (header->mappingOffset >= 0x78) && (header->branchPoolsOffset != 0) ) {
-            printf("branch pool count:  %u\n", header->branchPoolsCount);
-        }
-        if ( header->slideInfoSize > 0 ) {
-            uint32_t pageSize            = 0x4000; // fix me for intel
-            uint32_t possibleSlideValues = (uint32_t)(header->maxSlide/pageSize);
-            uint32_t entropyBits         = 32 - __builtin_clz(possibleSlideValues - 1);
-            printf("ASLR entropy: %u-bits\n", entropyBits);
-        }
-        printf("mappings:\n");
-        const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)((char*)dyldCache + header->mappingOffset);
-        for (uint32_t i=0; i < header->mappingCount; ++i) {
-            if ( mappings[i].initProt & VM_PROT_EXECUTE )
-                printf("    __TEXT      %3lluMB,  file offset: 0x%08llX -> 0x%08llX,  address: 0x%08llX -> 0x%08llX\n",
-                       mappings[i].size/(1024*1024), mappings[i].fileOffset, mappings[i].fileOffset + mappings[i].size,
-                       mappings[i].address, mappings[i].address + mappings[i].size);
-            else if ( mappings[i].initProt & VM_PROT_WRITE )
-                printf("    __DATA      %3lluMB,  file offset: 0x%08llX -> 0x%08llX,  address: 0x%08llX -> 0x%08llX\n",
-                       mappings[i].size/(1024*1024), mappings[i].fileOffset, mappings[i].fileOffset + mappings[i].size,
-                       mappings[i].address, mappings[i].address + mappings[i].size);
-            else if ( mappings[i].initProt & VM_PROT_READ )
-                printf("    __LINKEDIT  %3lluMB,  file offset: 0x%08llX -> 0x%08llX,  address: 0x%08llX -> 0x%08llX\n",
-                       mappings[i].size/(1024*1024), mappings[i].fileOffset, mappings[i].fileOffset + mappings[i].size,
-                       mappings[i].address, mappings[i].address + mappings[i].size);
-        }
-        if ( header->codeSignatureOffset != 0 ) {
-            uint64_t size = header->codeSignatureSize;
-            uint64_t csAddr = mappings[header->mappingCount-1].address + mappings[header->mappingCount-1].size;
-            if ( size != 0 )
-                printf("    code sign   %3lluMB,  file offset: 0x%08llX -> 0x%08llX,  address: 0x%08llX -> 0x%08llX\n",
-                       size/(1024*1024), header->codeSignatureOffset, header->codeSignatureOffset + size, csAddr, csAddr + size);
-        }
-        printf("slide info:    %4lluKB,  file offset: 0x%08llX -> 0x%08llX\n",
-               header->slideInfoSize/1024, header->slideInfoOffset, header->slideInfoOffset + header->slideInfoSize);
-        if ( header->localSymbolsOffset != 0 )
-            printf("local symbols:  %3lluMB,  file offset: 0x%08llX -> 0x%08llX\n",
-                   header->localSymbolsSize/(1024*1024), header->localSymbolsOffset, header->localSymbolsOffset + header->localSymbolsSize);
-        if ( (header->mappingOffset >= 0x78) && (header->accelerateInfoSize != 0) )
-            printf("accelerate tab: %3lluKB,                                          address: 0x%08llX -> 0x%08llX\n",
-                   header->accelerateInfoSize/1024, header->accelerateInfoAddr, header->accelerateInfoAddr + header->accelerateInfoSize);
-    }
-    else if ( options.mode == modeAcceleratorInfo ) {
-        const dyld_cache_header* header = &dyldCache->header;
-        if ( (header->mappingOffset < sizeof(dyld_cache_header)) || (header->accelerateInfoSize == 0) ) {
-            printf("no accelerator info\n");
-        }
-        else {
-            const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)((char*)dyldCache + header->mappingOffset);
-            uint64_t aiAddr = header->accelerateInfoAddr;
-            const dyld_cache_accelerator_info* accelInfo = NULL;
-            for (uint32_t i=0; i < header->mappingCount; ++i) {
-                if ( (mappings[i].address <= aiAddr) && (aiAddr < mappings[i].address+mappings[i].size) ) {
-                    uint64_t offset = aiAddr - mappings[i].address + mappings[i].fileOffset;
-                    accelInfo = (dyld_cache_accelerator_info*)((uint8_t*)dyldCache + offset);
-                }
-            }
-            if ( accelInfo == NULL ) {
-                printf("accelerator info not in any mapped range\n");
-            }
-            else {
-                const dyld_cache_image_info* images = (dyld_cache_image_info*)((char*)dyldCache + header->imagesOffset);
-                const dyld_cache_image_info_extra* imagesExtra = (dyld_cache_image_info_extra*)((char*)accelInfo + accelInfo->imagesExtrasOffset);
-                const uint16_t* dependencyArray = (uint16_t*)((char*)accelInfo + accelInfo->depListOffset);
-                const uint16_t* reExportArray = (uint16_t*)((char*)accelInfo + accelInfo->reExportListOffset);
-                printf("extra image info (count=%u):\n", accelInfo->imageExtrasCount);
-                for (uint32_t i=0; i < accelInfo->imageExtrasCount; ++i) {
-                    printf("  image[%3u] %s:\n", i, (char*)dyldCache +images[i].pathFileOffset);
-                    printf("     exports trie: addr=0x%llX, size=0x%08X\n", imagesExtra[i].exportsTrieAddr, imagesExtra[i].exportsTrieSize);
-                    if ( imagesExtra[i].weakBindingsSize )
-                        printf("     weak bind info: addr=0x%llX, size=0x%08X\n", imagesExtra[i].weakBindingsAddr, imagesExtra[i].weakBindingsSize);
-                    printf("     dependents: ");
-                    for (uint32_t d=imagesExtra[i].dependentsStartArrayIndex; dependencyArray[d] != 0xFFFF; ++d) {
-                        uint16_t depIndex = dependencyArray[d];
-                        if ( depIndex & 0x8000 )
-                            printf(" up(%d) ", depIndex & 0x7FFF);
-                        else
-                            printf(" %d ", depIndex);
-                    }
-                    printf("\n");
-                    printf("     re-exports: ");
-                    for (uint32_t r=imagesExtra[i].reExportsStartArrayIndex; reExportArray[r] != 0xFFFF; ++r)
-                        printf(" %d ", reExportArray[r]);
-                    printf("\n");
-                }
-                printf("libdyld.dylib:\n");
-                printf("   __dyld section address: 0x%llX\n", accelInfo->dyldSectionAddr);
-                printf("initializers (count=%u):\n", accelInfo->initializersCount);
-                const dyld_cache_accelerator_initializer* initializers = (dyld_cache_accelerator_initializer*)((char*)accelInfo + accelInfo->initializersOffset);
-                for (uint32_t i=0; i < accelInfo->initializersCount; ++i) {
-                    printf("  image[%3u] 0x%llX\n", initializers[i].imageIndex, mappings[0].address + initializers[i].functionOffset);
-                }
-                printf("DOF sections (count=%u):\n", accelInfo->dofSectionsCount);
-                const dyld_cache_accelerator_dof* dofs = (dyld_cache_accelerator_dof*)((char*)accelInfo + accelInfo->dofSectionsOffset);
-                for (uint32_t i=0; i < accelInfo->dofSectionsCount; ++i) {
-                    printf("  image[%3u] 0x%llX -> 0x%llX\n", dofs[i].imageIndex, dofs[i].sectionAddress, dofs[i].sectionAddress+dofs[i].sectionSize);
-                }
-                printf("bottom up order (count=%u):\n", accelInfo->imageExtrasCount);
-                const uint16_t* bottomUpArray = (uint16_t*)((char*)accelInfo + accelInfo->bottomUpListOffset);
-                for (uint32_t i=0; i < accelInfo->imageExtrasCount; ++i) {
-                    unsigned imageIndex = bottomUpArray[i];
-                    if ( imageIndex < accelInfo->imageExtrasCount )
-                        printf("  image[%3u] %s\n", imageIndex, (char*)dyldCache + images[imageIndex].pathFileOffset);
-                    else
-                        printf("  image[%3u] BAD INDEX\n", imageIndex);
-                }
-                printf("range table (count=%u):\n", accelInfo->rangeTableCount);
-                const dyld_cache_range_entry* rangeTable = (dyld_cache_range_entry*)((char*)accelInfo + accelInfo->rangeTableOffset);
-                for (uint32_t i=0; i < accelInfo->rangeTableCount; ++i) {
-                    const dyld_cache_range_entry& entry = rangeTable[i];
-                    printf("  0x%llX -> 0x%llX %s\n", entry.startAddress, entry.startAddress + entry.size, (char*)dyldCache + images[entry.imageIndex].pathFileOffset);
-                }
-                printf("dylib trie (size=%u):\n", accelInfo->dylibTrieSize);
-                const uint8_t* dylibTrieStart = (uint8_t*)accelInfo + accelInfo->dylibTrieOffset;
-                const uint8_t* dylibTrieEnd = dylibTrieStart + accelInfo->dylibTrieSize;
-                std::vector<DylibIndexTrie::Entry> dylibEntries;
-                if ( !Trie<DylibIndex>::parseTrie(dylibTrieStart, dylibTrieEnd, dylibEntries) )
-                    printf("  malformed dylibs trie\n");
-                for (const DylibIndexTrie::Entry& x : dylibEntries) {
-                    printf("  image[%3u] %s\n", x.info.index, x.name.c_str());
-                }
-            }
-        }
-    }
-    else if ( options.mode == modeTextInfo ) {
-        const dyld_cache_header* header = &dyldCache->header;
-        if ( (header->mappingOffset < sizeof(dyld_cache_header)) || (header->imagesTextCount == 0) ) {
-            printf("no text info\n");
-        }
-        else {
-            const dyld_cache_image_text_info* imagesText = (dyld_cache_image_text_info*)((char*)dyldCache + header->imagesTextOffset);
-            const dyld_cache_image_text_info* imagesTextEnd = &imagesText[header->imagesTextCount];
-            printf("dylib text infos (count=%llu):\n", header->imagesTextCount);
-            for (const dyld_cache_image_text_info* p=imagesText; p < imagesTextEnd; ++p) {
-                printf("   0x%09llX -> 0x%09llX  <", p->loadAddress, p->loadAddress + p->textSegmentSize);
-                for (int i=0; i<16; ++i) {
-                    switch (i) {
-                        case 4:
-                        case 6:
-                        case 8:
-                        case 10:
-                            printf("-");
-                            break;
-                    }
-                    printf("%02X", p->uuid[i]);
-                }
-                printf(">  %s\n", (char*)dyldCache + p->pathOffset);
-            }
-        }
-    }
-    else if ( options.mode == modeLocalSymbols ) {
-        const dyld_cache_header* header = &dyldCache->header;
-        if ( header->localSymbolsOffset == 0 ) {
-            fprintf(stderr, "Error: dyld shared cache does not contain local symbols info\n");
-            exit(1);
-        }
-        const bool is64 = (strstr((char*)dyldCache, "64") != NULL);
-        const dyld_cache_image_info* imageInfos = (dyld_cache_image_info*)((char*)dyldCache + header->imagesOffset);
-        const dyld_cache_local_symbols_info* localsInfo = (dyld_cache_local_symbols_info*)((char*)dyldCache + header->localSymbolsOffset);
-        const uint32_t nlistFileOffset = (uint32_t)(header->localSymbolsOffset + localsInfo->nlistOffset);
-        const uint32_t nlistCount = localsInfo->nlistCount;
-        const uint32_t nlistByteSize = is64 ? nlistCount*16 : nlistCount*12;
-        const uint32_t stringsFileOffset = (uint32_t)(header->localSymbolsOffset + localsInfo->stringsOffset);
-        const uint32_t stringsSize = localsInfo->stringsSize;
-        const uint32_t entriesCount = localsInfo->entriesCount;
-        const dyld_cache_local_symbols_entry* entries = (dyld_cache_local_symbols_entry*)((char*)localsInfo + localsInfo->entriesOffset);
-        printf("local symbols nlist array:  %3uMB,  file offset: 0x%08X -> 0x%08X\n", nlistByteSize/(1024*1024), nlistFileOffset, nlistFileOffset+nlistByteSize);
-        printf("local symbols string pool:  %3uMB,  file offset: 0x%08X -> 0x%08X\n", stringsSize/(1024*1024), stringsFileOffset, stringsFileOffset+stringsSize);
-        printf("local symbols by dylib (count=%d):\n", entriesCount);
-        //const char* stringPool = (char*)dyldCache + stringsFileOffset;
-        for (int i=0; i < entriesCount; ++i) {
-            const char* imageName = (char*)dyldCache + imageInfos[i].pathFileOffset;
-            printf("   nlistStartIndex=%5d, nlistCount=%5d, image=%s\n", entries[i].nlistStartIndex, entries[i].nlistCount, imageName);
-#if 0
-            if ( is64 ) {
-                const nlist_64* symTab = (nlist_64*)((char*)dyldCache + nlistFileOffset);
-                for (int e=0; e < entries[i].nlistCount(); ++e) {
-                    const nlist_64* entry = &symTab[entries[i].nlistStartIndex()+e];
-                    printf("     nlist[%d].str=%d, %s\n", e, entry->n_un.n_strx, &stringPool[entry->n_un.n_strx]);
-                    printf("     nlist[%d].value=0x%0llX\n", e, entry->n_value);
-                }
-            }
-#endif
-        }
-    }
-    else if ( options.mode == modeJSONMap ) {
-        std::string buffer = dyldCache->generateJSONMap("unknown");
-        printf("%s\n", buffer.c_str());
-    }
-    else if ( options.mode == modeJSONDependents ) {
-        std::cout <<  dyldCache->generateJSONDependents();
-    }
-    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 == modeSectionSizes ) {
-        __block std::map<std::string, uint64_t> sectionSizes;
-        dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
-            const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
-            ma->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo &sectInfo, bool malformedSectionRange, bool &stop) {
-                std::string section = std::string(sectInfo.segInfo.segName) + " " + sectInfo.sectName;
-                sectionSizes[section] += sectInfo.sectSize;
-            });
-        });
-        for (const auto& keyAndValue : sectionSizes) {
-            printf("%lld %s\n", keyAndValue.second, keyAndValue.first.c_str());
-        }
-    }
-    else if ( options.mode == modeObjCProtocols ) {
-        if ( dyldCache->objcOpt() == nullptr ) {
-            fprintf(stderr, "Error: could not get optimized objc\n");
-            return 1;
-        }
-        objc_opt::objc_protocolopt2_t* protocols = dyldCache->objcOpt()->protocolopt2();
-        if ( protocols == nullptr ) {
-            fprintf(stderr, "Error: could not get optimized objc protocols\n");
-            return 1;
-        }
-
-        for (uint64_t index = 0; index != protocols->capacity; ++index) {
-            const objc_opt::objc_classheader_t& clshi = protocols->classOffsets()[index];
-            if ( clshi.clsOffset == 0 ) {
-                fprintf(stderr, "[% 5lld]\n", index);
-                continue;
-            }
-            const char* name = (const char*)(((const uint8_t*)protocols) + protocols->offsets()[index]);
-            if ( !clshi.isDuplicate() ) {
-                fprintf(stderr, "[% 5lld] -> (% 8d, % 8d) = %s\n", index, clshi.clsOffset, clshi.hiOffset, name);
-                continue;
-            }
-
-            // class appears in more than one header
-            uint32_t count = clshi.duplicateCount();
-            fprintf(stderr, "[% 5lld] -> duplicates [% 5d..% 5d] = %s\n",
-                    index, clshi.duplicateIndex(), clshi.duplicateIndex() + clshi.duplicateCount() - 1, name);
-
-            const objc_opt::objc_classheader_t *list = &protocols->duplicateOffsets()[clshi.duplicateIndex()];
-            for (uint32_t i = 0; i < count; i++) {
-                fprintf(stderr, "  - [% 5lld] -> (% 8d, % 8d)\n", (uint64_t)(clshi.duplicateIndex() + i), list[i].clsOffset, list[i].hiOffset);
-            }
-        }
-    }
-    else if ( options.mode == modeObjCClasses ) {
-        using dyld3::json::Node;
-        using ObjCClassInfo = dyld3::MachOAnalyzer::ObjCClassInfo;
-        const bool rebased = false;
-
-        // Build a map of class vm addrs to their names so that categories know the
-        // name of the class they are attaching to
-        __block std::map<uint64_t, const char*> classVMAddrToName;
-        __block std::map<uint64_t, const char*> metaclassVMAddrToName;
-        dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
-            const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
-            const uint32_t pointerSize = ma->pointerSize();
-
-            auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
-                                uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
-                                const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
-                dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
-                const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
-                if (classNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
-                    return;
-
-                if (isMetaClass)
-                    metaclassVMAddrToName[classVMAddr] = className;
-                else
-                    classVMAddrToName[classVMAddr] = className;
-            };
-
-            Diagnostics diag;
-            ma->forEachObjCClass(diag, rebased, visitClass);
-        });
-
-        // These are used only for the on-disk binaries we analyze
-        __block std::vector<const char*>        onDiskChainedFixupBindTargets;
-        __block std::map<uint64_t, const char*> onDiskClassVMAddrToName;
-        __block std::map<uint64_t, const char*> onDiskMetaclassVMAddrToName;
-
-        __block Node root;
-        auto makeNode = [](std::string str) -> Node {
-            Node node;
-            node.value = str;
-            return node;
-        };
-
-        auto getProperties = ^(const dyld3::MachOAnalyzer* ma, uint64_t propertiesVMAddr) {
-            __block Node propertiesNode;
-            auto visitProperty = ^(uint64_t propertyVMAddr, const dyld3::MachOAnalyzer::ObjCProperty& property) {
-                // Get the name
-                dyld3::MachOAnalyzer::PrintableStringResult propertyNameResult;
-                const char* propertyName = ma->getPrintableString(property.nameVMAddr, propertyNameResult);
-                if (propertyNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
-                    return;
-
-                // Get the attributes
-                dyld3::MachOAnalyzer::PrintableStringResult propertyAttributesResult;
-                const char* propertyAttributes = ma->getPrintableString(property.attributesVMAddr, propertyAttributesResult);
-                if (propertyAttributesResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
-                    return;
-
-                Node propertyNode;
-                propertyNode.map["name"] = makeNode(propertyName);
-                propertyNode.map["attributes"] = makeNode(propertyAttributes);
-                propertiesNode.array.push_back(propertyNode);
-            };
-            ma->forEachObjCProperty(propertiesVMAddr, rebased, visitProperty);
-            return propertiesNode.array.empty() ? std::optional<Node>() : propertiesNode;
-        };
-
-        auto getClasses = ^(const dyld3::MachOAnalyzer* ma) {
-            Diagnostics diag;
-            const uint32_t pointerSize = ma->pointerSize();
-
-            __block Node classesNode;
-            __block bool skippedPreviousClass = false;
-            auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
-                                uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
-                                const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
-                if (isMetaClass) {
-                    if (skippedPreviousClass) {
-                        // If the class was bad, then skip the meta class too
-                        skippedPreviousClass = false;
-                        return;
-                    }
-                } else {
-                    skippedPreviousClass = true;
-                }
-
-                std::string classType = "-";
-                if (isMetaClass)
-                    classType = "+";
-                dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
-                const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
-                if (classNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint) {
-                    return;
-                }
-
-                const char* superClassName = nullptr;
-                if ( ma->inDyldCache() ) {
-                    if ( objcClass.superclassVMAddr != 0 ) {
-                        if (isMetaClass) {
-                            // If we are root class, then our superclass should actually point to our own class
-                            const uint32_t RO_ROOT = (1<<1);
-                            if ( objcClass.flags(pointerSize) & RO_ROOT ) {
-                                auto it = classVMAddrToName.find(objcClass.superclassVMAddr);
-                                assert(it != classVMAddrToName.end());
-                                superClassName = it->second;
-                            } else {
-                                auto it = metaclassVMAddrToName.find(objcClass.superclassVMAddr);
-                                assert(it != metaclassVMAddrToName.end());
-                                superClassName = it->second;
-                            }
-                        } else {
-                            auto it = classVMAddrToName.find(objcClass.superclassVMAddr);
-                            assert(it != classVMAddrToName.end());
-                            superClassName = it->second;
-                        }
-                    }
-                } else {
-                    // On-disk binary.  Lets crack the chain to work out what we are pointing at
-                    dyld3::MachOAnalyzer::ChainedFixupPointerOnDisk fixup;
-                    fixup.raw64 = objcClass.superclassVMAddr;
-                    assert(!fixup.arm64e.authBind.auth);
-                    if (fixup.arm64e.bind.bind) {
-                        // Bind to another image.  Use the bind table to work out which name to bind to
-                        const char* symbolName = onDiskChainedFixupBindTargets[fixup.arm64e.bind.ordinal];
-                        if (isMetaClass) {
-                            if ( strstr(symbolName, "_OBJC_METACLASS_$_") == symbolName ) {
-                                superClassName = symbolName + strlen("_OBJC_METACLASS_$_");
-                            } else {
-                                // Swift classes don't start with these prefixes so just skip them
-                                if (objcClass.isSwiftLegacy || objcClass.isSwiftStable)
-                                    return;
-                            }
-                        } else {
-                            if ( strstr(symbolName, "_OBJC_CLASS_$_") == symbolName ) {
-                                superClassName = symbolName + strlen("_OBJC_CLASS_$_");
-                            } else {
-                                // Swift classes don't start with these prefixes so just skip them
-                                if (objcClass.isSwiftLegacy || objcClass.isSwiftStable)
-                                    return;
-                            }
-                        }
-                    } else {
-                        // Rebase within this image.
-                        if (isMetaClass) {
-                            auto it = onDiskMetaclassVMAddrToName.find(objcClass.superclassVMAddr);
-                            assert(it != onDiskMetaclassVMAddrToName.end());
-                            superClassName = it->second;
-                        } else {
-                            auto it = onDiskClassVMAddrToName.find(objcClass.superclassVMAddr);
-                            assert(it != onDiskClassVMAddrToName.end());
-                            superClassName = it->second;
-                        }
-                    }
-                }
-
-                // Print the methods on this class
-                __block Node methodsNode;
-                auto visitMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
-                    dyld3::MachOAnalyzer::PrintableStringResult methodNameResult;
-                    const char* methodName = ma->getPrintableString(method.nameVMAddr, methodNameResult);
-                    if (methodNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
-                        return;
-                    methodsNode.array.push_back(makeNode(classType + methodName));
-                };
-                ma->forEachObjCMethod(objcClass.baseMethodsVMAddr(pointerSize), rebased,
-                                      visitMethod);
-
-                std::optional<Node> properties = getProperties(ma, objcClass.basePropertiesVMAddr(pointerSize));
-
-                if (isMetaClass) {
-                    assert(!classesNode.array.empty());
-                    Node& currentClassNode = classesNode.array.back();
-                    assert(currentClassNode.map["className"].value == className);
-                    if (!methodsNode.array.empty()) {
-                        Node& currentMethodsNode = currentClassNode.map["methods"];
-                        currentMethodsNode.array.insert(currentMethodsNode.array.end(),
-                                                        methodsNode.array.begin(),
-                                                        methodsNode.array.end());
-                    }
-                    if (properties.has_value()) {
-                        Node& currentPropertiesNode = currentClassNode.map["properties"];
-                        currentPropertiesNode.array.insert(currentPropertiesNode.array.end(),
-                                                           properties->array.begin(),
-                                                           properties->array.end());
-                    }
-                    return;
-                }
-
-                Node currentClassNode;
-                currentClassNode.map["className"] = makeNode(className);
-                if ( superClassName != nullptr )
-                    currentClassNode.map["superClassName"] = makeNode(superClassName);
-                if (!methodsNode.array.empty())
-                    currentClassNode.map["methods"] = methodsNode;
-                if (properties.has_value())
-                    currentClassNode.map["properties"] = properties.value();
-
-                // We didn't skip this class so mark it as such
-                skippedPreviousClass = false;
-
-                classesNode.array.push_back(currentClassNode);
-            };
-
-            ma->forEachObjCClass(diag, rebased, visitClass);
-            return classesNode.array.empty() ? std::optional<Node>() : classesNode;
-        };
-
-        auto getCategories = ^(const dyld3::MachOAnalyzer* ma) {
-            Diagnostics diag;
-
-            __block Node categoriesNode;
-            auto visitCategory = ^(Diagnostics& diag, uint64_t categoryVMAddr,
-                                   const dyld3::MachOAnalyzer::ObjCCategory& objcCategory) {
-                dyld3::MachOAnalyzer::PrintableStringResult categoryNameResult;
-                const char* categoryName = ma->getPrintableString(objcCategory.nameVMAddr, categoryNameResult);
-                if (categoryNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
-                    return;
-
-                    const char* className = nullptr;
-                    if ( ma->inDyldCache() ) {
-                        auto it = classVMAddrToName.find(objcCategory.clsVMAddr);
-                        assert(it != classVMAddrToName.end());
-                        className = it->second;
-                    } else {
-                        // On-disk binary.  Lets crack the chain to work out what we are pointing at
-                        dyld3::MachOAnalyzer::ChainedFixupPointerOnDisk fixup;
-                        fixup.raw64 = objcCategory.clsVMAddr;
-                        assert(!fixup.arm64e.authBind.auth);
-                        if (fixup.arm64e.bind.bind) {
-                            // Bind to another image.  Use the bind table to work out which name to bind to
-                            const char* symbolName = onDiskChainedFixupBindTargets[fixup.arm64e.bind.ordinal];
-                            if ( strstr(symbolName, "_OBJC_CLASS_$_") == symbolName ) {
-                                className = symbolName + strlen("_OBJC_CLASS_$_");
-                            } else {
-                                // Swift classes don't start with these prefixes so just skip them
-                                // We don't know that this is a Swift class/category though, but skip it anyway
-                                return;
-                            }
-                        } else {
-                            auto it = onDiskClassVMAddrToName.find(objcCategory.clsVMAddr);
-                            if (it == onDiskClassVMAddrToName.end()) {
-                                // This is an odd binary with perhaps a Swift class.  Just skip this entry
-                                return;
-                            }
-                            className = it->second;
-                        }
-                    }
-
-                // Print the instance methods on this category
-                __block Node methodsNode;
-                auto visitInstanceMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
-                    dyld3::MachOAnalyzer::PrintableStringResult methodNameResult;
-                    const char* methodName = ma->getPrintableString(method.nameVMAddr, methodNameResult);
-                    if (methodNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
-                        return;
-                    methodsNode.array.push_back(makeNode(std::string("-") + methodName));
-                };
-                ma->forEachObjCMethod(objcCategory.instanceMethodsVMAddr, rebased,
-                                      visitInstanceMethod);
-
-                // Print the instance methods on this category
-                __block Node classMethodsNode;
-                auto visitClassMethod = ^(uint64_t methodVMAddr, const dyld3::MachOAnalyzer::ObjCMethod& method) {
-                    dyld3::MachOAnalyzer::PrintableStringResult methodNameResult;
-                    const char* methodName = ma->getPrintableString(method.nameVMAddr, methodNameResult);
-                    if (methodNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
-                        return;
-                    methodsNode.array.push_back(makeNode(std::string("+") + methodName));
-                };
-                ma->forEachObjCMethod(objcCategory.classMethodsVMAddr, rebased,
-                                      visitClassMethod);
-
-                Node currentCategoryNode;
-                currentCategoryNode.map["categoryName"] = makeNode(categoryName);
-                currentCategoryNode.map["className"] = makeNode(className);
-                if (!methodsNode.array.empty())
-                    currentCategoryNode.map["methods"] = methodsNode;
-                if (std::optional<Node> properties = getProperties(ma, objcCategory.instancePropertiesVMAddr))
-                    currentCategoryNode.map["properties"] = properties.value();
-
-                categoriesNode.array.push_back(currentCategoryNode);
-            };
-
-            ma->forEachObjCCategory(diag, rebased, visitCategory);
-            return categoriesNode.array.empty() ? std::optional<Node>() : categoriesNode;
-        };
-
-        dyldCache->forEachImage(^(const mach_header *mh, const char *installName) {
-            const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mh;
-
-            Node imageRecord;
-            imageRecord.map["imagePath"] = makeNode(installName);
-            imageRecord.map["imageType"] = makeNode("cache-dylib");
-            std::optional<Node> classes = getClasses(ma);
-            std::optional<Node> categories = getCategories(ma);
-
-            // Skip emitting images with no objc data
-            if (!classes.has_value() && !categories.has_value())
-                return;
-            if (classes.has_value())
-                imageRecord.map["classes"] = classes.value();
-            if (categories.has_value())
-                imageRecord.map["categories"] = categories.value();
-
-            root.array.push_back(imageRecord);
-        });
-
-        FileSystemPhysical fileSystem;
-        dyld3::Platform            platform = dyldCache->platform();
-        const dyld3::GradedArchs&  archs    = dyld3::GradedArchs::forName(dyldCache->archName(), true);
-
-        dyldCache->forEachLaunchClosure(^(const char *executableRuntimePath, const dyld3::closure::LaunchClosure *closure) {
-            Diagnostics diag;
-            char realerPath[MAXPATHLEN];
-            dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, executableRuntimePath, archs, platform, realerPath);
-            const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
-            uint32_t pointerSize = ma->pointerSize();
-
-            // Populate the bind targets for classes from other images
-            onDiskChainedFixupBindTargets.clear();
-            ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
-                onDiskChainedFixupBindTargets.push_back(symbolName);
-            });
-            if ( diag.hasError() )
-                return;
-
-            // Populate the rebase targets for class names
-            onDiskMetaclassVMAddrToName.clear();
-            onDiskClassVMAddrToName.clear();
-            auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
-                                uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
-                                const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
-                dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
-                const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
-                if (classNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
-                    return;
-
-                if (isMetaClass)
-                    onDiskMetaclassVMAddrToName[classVMAddr] = className;
-                else
-                    onDiskClassVMAddrToName[classVMAddr] = className;
-            };
-
-            ma->forEachObjCClass(diag, rebased, visitClass);
-
-            Node imageRecord;
-            imageRecord.map["imagePath"] = makeNode(executableRuntimePath);
-            imageRecord.map["imageType"] = makeNode("executable");
-            std::optional<Node> classes = getClasses(ma);
-            std::optional<Node> categories = getCategories(ma);
-
-            // Skip emitting images with no objc data
-            if (!classes.has_value() && !categories.has_value())
-                return;
-            if (classes.has_value())
-                imageRecord.map["classes"] = classes.value();
-            if (categories.has_value())
-                imageRecord.map["categories"] = categories.value();
-
-            root.array.push_back(imageRecord);
-        });
-
-        dyldCache->forEachDlopenImage(^(const char *runtimePath, const dyld3::closure::Image *image) {
-            Diagnostics diag;
-            char realerPath[MAXPATHLEN];
-            dyld3::closure::LoadedFileInfo loadedFileInfo = dyld3::MachOAnalyzer::load(diag, fileSystem, runtimePath, archs, platform, realerPath);
-            const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)loadedFileInfo.fileContent;
-            uint32_t pointerSize = ma->pointerSize();
-
-            // Populate the bind targets for classes from other images
-            onDiskChainedFixupBindTargets.clear();
-            ma->forEachChainedFixupTarget(diag, ^(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
-                onDiskChainedFixupBindTargets.push_back(symbolName);
-            });
-            if ( diag.hasError() )
-                return;
-
-            // Populate the rebase targets for class names
-            onDiskMetaclassVMAddrToName.clear();
-            onDiskClassVMAddrToName.clear();
-            auto visitClass = ^(Diagnostics& diag, uint64_t classVMAddr,
-                                uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
-                                const dyld3::MachOAnalyzer::ObjCClassInfo& objcClass, bool isMetaClass) {
-                dyld3::MachOAnalyzer::PrintableStringResult classNameResult;
-                const char* className = ma->getPrintableString(objcClass.nameVMAddr(pointerSize), classNameResult);
-                if (classNameResult != dyld3::MachOAnalyzer::PrintableStringResult::CanPrint)
-                    return;
-
-                if (isMetaClass)
-                    onDiskMetaclassVMAddrToName[classVMAddr] = className;
-                else
-                    onDiskClassVMAddrToName[classVMAddr] = className;
-            };
-
-            ma->forEachObjCClass(diag, rebased, visitClass);
-
-            Node imageRecord;
-            imageRecord.map["imagePath"] = makeNode(runtimePath);
-            imageRecord.map["imageType"] = makeNode("non-cache-dylib");
-            std::optional<Node> classes = getClasses(ma);
-            std::optional<Node> categories = getCategories(ma);
-
-            // Skip emitting images with no objc data
-            if (!classes.has_value() && !categories.has_value())
-                return;
-            if (classes.has_value())
-                imageRecord.map["classes"] = classes.value();
-            if (categories.has_value())
-                imageRecord.map["categories"] = categories.value();
-
-            root.array.push_back(imageRecord);
-        });
-
-        dyld3::json::printJSON(root, 0, std::cout);
-    }
-    else if ( options.mode == modeObjCSelectors ) {
-        if ( dyldCache->objcOpt() == nullptr ) {
-            fprintf(stderr, "Error: could not get optimized objc\n");
-            return 1;
-        }
-        const objc_opt::objc_selopt_t* selectors = dyldCache->objcOpt()->selopt();
-        if ( selectors == nullptr ) {
-            fprintf(stderr, "Error: could not get optimized objc selectors\n");
-            return 1;
-        }
-
-        std::vector<const char*> selNames;
-        for (uint64_t index = 0; index != selectors->capacity; ++index) {
-            objc_opt::objc_stringhash_offset_t offset = selectors->offsets()[index];
-            if ( offset == 0 )
-                continue;
-            const char* selName = selectors->getEntryForIndex((uint32_t)index);
-            selNames.push_back(selName);
-        }
-
-        std::sort(selNames.begin(), selNames.end(),
-                  [](const char* a, const char* b) {
-            // Sort by offset, not string value
-            return a < b;
-        });
-
-        auto makeNode = [](std::string str) {
-            dyld3::json::Node node;
-            node.value = str;
-            return node;
-        };
-
-        dyld3::json::Node root;
-        for (const char* selName : selNames) {
-            dyld3::json::Node selNode;
-            selNode.map["selectorName"] = makeNode(selName);
-            selNode.map["offset"] = makeNode(dyld3::json::decimal((uint64_t)selName - (uint64_t)dyldCache));
-
-            root.array.push_back(selNode);
-        }
-
-        dyld3::json::printJSON(root, 0, std::cout);
-    }
-    else if ( options.mode == modeExtract ) {
-        char pathBuffer[PATH_MAX];
-        uint32_t bufferSize = PATH_MAX;
-        if ( _NSGetExecutablePath(pathBuffer, &bufferSize) != 0 ) {
-            fprintf(stderr, "Error: could not get path of program\n");
-            return 1;
-        }
-        char* last = strrchr(pathBuffer, '/');
-        // The bundle is at a different location on device.  Its /usr/lib/dsc_extractor.bundle in the SDK
-        // but /usr/local/lib/dsc_extractor.bundle on device.
-        strcpy(last+1, DSC_BUNDLE_REL_PATH);
-        void* handle = dlopen(pathBuffer, RTLD_LAZY);
-        if ( handle == NULL ) {
-            fprintf(stderr, "Error: dsc_extractor.bundle could not be loaded at %s\n", pathBuffer);
-            return 1;
-        }
-
-        typedef int (*extractor_proc)(const char* shared_cache_file_path, const char* extraction_root_path,
-                                      void (^progress)(unsigned current, unsigned total));
-
-        extractor_proc proc = (extractor_proc)dlsym(handle, "dyld_shared_cache_extract_dylibs_progress");
-        if ( proc == NULL ) {
-            fprintf(stderr, "Error: dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n");
-            return 1;
-        }
-
-        int result = (*proc)(sharedCachePath, options.extractionDir, ^(unsigned c, unsigned total) { } );
-        return result;
-    }
-    else {
-        switch ( options.mode ) {
-            case modeList: {
-                if (options.printInodes) {
-                    dyldCache->forEachImageEntry(^(const char* path, uint64_t mTime, uint64_t inode) {
-                        printf("0x%08llX 0x%08llX ", inode, mTime);
-                        if ( isAlias(path, dyldCache) )
-                            printf("[alias] %s\n", path);
-                        else
-                            printf("%s\n", path);
-                    });
-                } else {
-                    dyldCache->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const unsigned char *dylibUUID, const char *installName, bool &stop) {
-                        if ( options.printVMAddrs )
-                            printf("0x%08llX ", loadAddressUnslid);
-                        if ( options.printUUIDs ) {
-                            const uint8_t* uuid = (uint8_t*)dylibUUID;
-                            printf("<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> ",
-                                   uuid[0],  uuid[1],  uuid[2],  uuid[3],
-                                   uuid[4],  uuid[5],  uuid[6],  uuid[7],
-                                   uuid[8],  uuid[9],  uuid[10], uuid[11],
-                                   uuid[12], uuid[13], uuid[14], uuid[15]);
-                        }
-                        if ( isAlias(installName, dyldCache) )
-                            printf("[alias] %s\n", installName);
-                        else
-                            printf("%s\n", installName);
-                    });
-                }
-                break;
-            }
-            case modeMap: {
-                __block std::map<uint64_t, const char*> dataSegNames;
-                __block std::map<uint64_t, uint64_t>    dataSegEnds;
-                dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
-                    dyld3::MachOFile* mf = (dyld3::MachOFile*)mh;
-                    mf->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
-                        if ( isAlias(installName, dyldCache) )
-                            return;
-                        printf("0x%08llX - 0x%08llX %s %s\n", info.vmAddr, info.vmAddr + info.vmSize, info.segName, installName);
-                        if ( strncmp(info.segName, "__DATA", 6) == 0 ) {
-                            dataSegNames[info.vmAddr] = installName;
-                            dataSegEnds[info.vmAddr] = info.vmAddr + info.vmSize;
-                        }
-                    });
-                });
-                // <rdar://problem/51084507> Enhance dyld_shared_cache_util to show where section alignment added padding
-                uint64_t lastEnd = 0;
-                for (const auto& entry : dataSegEnds) {
-                    uint64_t padding = entry.first - lastEnd;
-                    if ( (padding > 32) && (lastEnd != 0) ) {
-                        printf("0x%08llX - 0x%08llX PADDING %lluKB\n", lastEnd, entry.first, padding/1024);
-                    }
-                    lastEnd = entry.second;
-                }
-                break;
-            }
-            case modeDependencies: {
-                __block bool dependentTargetFound = false;
-                dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
-                    if ( strcmp(options.dependentsOfPath, installName) != 0 )
-                        return;
-                    dependentTargetFound = true;
-
-                    auto printDep = [&options](const char *loadPath, uint32_t compatVersion, uint32_t curVersion) {
-                        if ( options.printDylibVersions ) {
-                            uint32_t compat_vers = compatVersion;
-                            uint32_t current_vers = curVersion;
-                            printf("\t%s", loadPath);
-                            if ( compat_vers != 0xFFFFFFFF ) {
-                                printf("(compatibility version %u.%u.%u, current version %u.%u.%u)\n",
-                                       (compat_vers >> 16),
-                                       (compat_vers >> 8) & 0xff,
-                                       (compat_vers) & 0xff,
-                                       (current_vers >> 16),
-                                       (current_vers >> 8) & 0xff,
-                                       (current_vers) & 0xff);
-                            }
-                            else {
-                                printf("\n");
-                            }
-                        }
-                        else {
-                            printf("\t%s\n", loadPath);
-                        }
-                    };
-
-                    dyld3::MachOFile* mf = (dyld3::MachOFile*)mh;
-
-                    // First print out our dylib and version.
-                    const char* dylibInstallName;
-                    uint32_t currentVersion;
-                    uint32_t compatVersion;
-                    if ( mf->getDylibInstallName(&dylibInstallName, &compatVersion, &currentVersion) ) {
-                        printDep(dylibInstallName, compatVersion, currentVersion);
-                    }
-
-                    // Then the dependent dylibs.
-                    mf->forEachDependentDylib(^(const char *loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
-                        printDep(loadPath, compatVersion, curVersion);
-                    });
-                });
-                if (options.dependentsOfPath && !dependentTargetFound) {
-                    fprintf(stderr, "Error: could not find '%s' in the shared cache at\n  %s\n", options.dependentsOfPath, sharedCachePath);
-                    exit(1);
-                }
-                break;
-            }
-            case modeLinkEdit: {
-                std::map<uint32_t, const char*> pageToContent;
-                auto add_linkedit = [&pageToContent](uint32_t pageStart, uint32_t pageEnd, const char* message) {
-                    for (uint32_t p = pageStart; p <= pageEnd; p += 4096) {
-                        std::map<uint32_t, const char*>::iterator pos = pageToContent.find(p);
-                        if ( pos == pageToContent.end() ) {
-                            pageToContent[p] = strdup(message);
-                        }
-                        else {
-                            const char* oldMessage = pos->second;
-                            char* newMesssage;
-                            asprintf(&newMesssage, "%s, %s", oldMessage, message);
-                            pageToContent[p] = newMesssage;
-                            ::free((void*)oldMessage);
-                        }
-                    }
-                };
-
-                dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
-                    // Filter out symlinks.
-                    if (isAlias(installName, dyldCache))
-                        return;
-                    dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
-                    Diagnostics diag;
-                    dyld3::MachOAnalyzer::LinkEditInfo leInfo;
-                    ma->getLinkEditPointers(diag, leInfo);
-
-                    if (diag.hasError())
-                        return;
-
-                    char message[1000];
-                    const char* shortName = strrchr(installName, '/') + 1;
-                    // add export trie info
-                    if ( leInfo.dyldInfo->export_size != 0 ) {
-                        //printf("export_off=0x%X\n", leInfo.dyldInfo->export_off());
-                        uint32_t exportPageOffsetStart = leInfo.dyldInfo->export_off & (-4096);
-                        uint32_t exportPageOffsetEnd = (leInfo.dyldInfo->export_off + leInfo.dyldInfo->export_size) & (-4096);
-                        sprintf(message, "exports from %s", shortName);
-                        add_linkedit(exportPageOffsetStart, exportPageOffsetEnd, message);
-                    }
-                    // add binding info
-                    if ( leInfo.dyldInfo->bind_size != 0 ) {
-                        uint32_t bindPageOffsetStart = leInfo.dyldInfo->bind_off & (-4096);
-                        uint32_t bindPageOffsetEnd = (leInfo.dyldInfo->bind_off + leInfo.dyldInfo->bind_size) & (-4096);
-                        sprintf(message, "bindings from %s", shortName);
-                        add_linkedit(bindPageOffsetStart, bindPageOffsetEnd, message);
-                    }
-                    // add lazy binding info
-                    if ( leInfo.dyldInfo->lazy_bind_size != 0 ) {
-                        uint32_t lazybindPageOffsetStart = leInfo.dyldInfo->lazy_bind_off & (-4096);
-                        uint32_t lazybindPageOffsetEnd = (leInfo.dyldInfo->lazy_bind_off + leInfo.dyldInfo->lazy_bind_size) & (-4096);
-                        sprintf(message, "lazy bindings from %s", shortName);
-                        add_linkedit(lazybindPageOffsetStart, lazybindPageOffsetEnd, message);
-                    }
-                    // add weak binding info
-                    if ( leInfo.dyldInfo->weak_bind_size != 0 ) {
-                        uint32_t weakbindPageOffsetStart = leInfo.dyldInfo->weak_bind_off & (-4096);
-                        uint32_t weakbindPageOffsetEnd = (leInfo.dyldInfo->weak_bind_off + leInfo.dyldInfo->weak_bind_size) & (-4096);
-                        sprintf(message, "weak bindings from %s", shortName);
-                        add_linkedit(weakbindPageOffsetStart, weakbindPageOffsetEnd, message);
-                    }
-                });
-
-                for (std::map<uint32_t, const char*>::iterator it = pageToContent.begin(); it != pageToContent.end(); ++it) {
-                    printf("0x%08X %s\n", it->first, it->second);
-                }
-                break;
-            }
-            case modeSize: {
-                struct TextInfo {
-                    uint64_t    textSize;
-                    const char* path;
-                };
-                __block std::vector<TextInfo> textSegments;
-                dyldCache->forEachImage(^(const mach_header* mh, const char* installName) {
-                    // Filter out symlinks.
-                    if (isAlias(installName, dyldCache))
-                        return;
-
-                    dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
-                    ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
-                        if ( strcmp(info.segName, "__TEXT") != 0 )
-                            return;
-                        textSegments.push_back({ info.fileSize, installName });
-                    });
-                });
-                std::sort(textSegments.begin(), textSegments.end(), [](const TextInfo& left, const TextInfo& right) {
-                    return (left.textSize > right.textSize);
-                });
-                for (std::vector<TextInfo>::iterator it = textSegments.begin(); it != textSegments.end(); ++it) {
-                    printf(" 0x%08llX  %s\n", it->textSize, it->path);
-                }
-                break;
-            }
-            case modeNone:
-            case modeInfo:
-            case modeSlideInfo:
-            case modeVerboseSlideInfo:
-            case modeAcceleratorInfo:
-            case modeTextInfo:
-            case modeLocalSymbols:
-            case modeJSONMap:
-            case modeJSONDependents:
-            case modeSectionSizes:
-            case modeStrings:
-            case modeObjCProtocols:
-            case modeObjCClasses:
-            case modeObjCSelectors:
-            case modeExtract:
-                break;
-        }
-    }
-    return 0;
-}
-
index 14c753377e2cca3ebdd6b35688976ca7408c3fde..016b31cfa157639d272e3c87df0d53449f0316f0 100644 (file)
@@ -60,15 +60,15 @@ struct TestInfo
 
 + (void)load {
     for (const TestInfo& info : sTests) {
-        [self registerTest:info withEnv:"TEST_DYLD_MODE=2 DYLD_USE_CLOSURES=0" andExtensions:"dyld2"];
-        [self registerTest:info withEnv:"TEST_DYLD_MODE=2 DYLD_USE_CLOSURES=0 DYLD_SHARED_REGION=avoid" andExtensions:"dyld2_nocache"];
-        [self registerTest:info withEnv:"TEST_DYLD_MODE=3 DYLD_USE_CLOSURES=1" andExtensions:"dyld3"];
-        [self registerTest:info withEnv:"TEST_DYLD_MODE=3 DYLD_USE_CLOSURES=1 DYLD_SHARED_REGION=avoid" andExtensions:"dyld3_nocache"];
+        if ( strstr(info.runLine, "run-static") != nullptr ) {
+            [self registerTest:info withEnv:"" andExtensions:"static"];
+        } else {
+            [self registerTest:info withEnv:"DYLD_USE_CLOSURES=0" andExtensions:"dyld2"];
+            [self registerTest:info withEnv:"DYLD_USE_CLOSURES=1" andExtensions:"dyld3"];
+        }
 #if 0
-        [self registerTest:info withEnv:"TEST_DYLD_MODE=2 DYLD_USE_CLOSURES=0 MallocStackLogging=1 MallocDebugReport=none" andExtensions:"dyld2_leaks"];
-        [self registerTest:info withEnv:"TEST_DYLD_MODE=2 DYLD_USE_CLOSURES=0 DYLD_SHARED_REGION=avoid MallocStackLogging=1 MallocDebugReport=none" andExtensions:"dyld2_nocache_leaks"];
-        [self registerTest:info withEnv:"TEST_DYLD_MODE=3 DYLD_USE_CLOSURES=1 MallocStackLogging=1 MallocDebugReport=none" andExtensions:"dyld3_leaks"];
-        [self registerTest:info withEnv:"TEST_DYLD_MODE=3 DYLD_USE_CLOSURES=1 DYLD_SHARED_REGION=avoid MallocStackLogging=1 MallocDebugReport=none" andExtensions:"dyld3_nocache_leaks"];
+        [self registerTest:info withEnv:"DYLD_USE_CLOSURES=0 MallocStackLogging=1 MallocDebugReport=none" andExtensions:"dyld2_leaks"];
+        [self registerTest:info withEnv:"DYLD_USE_CLOSURES=1 MallocStackLogging=1 MallocDebugReport=none" andExtensions:"dyld3_leaks"];
 #endif
     };
 }
@@ -150,24 +150,37 @@ struct TestInfo
         int exitStatus = WEXITSTATUS(status);
         if (exitStatus != 0) {
             NSString *failure = [NSString stringWithFormat:@"Test exited with return code %d\n%s", exitStatus, command];
-            [self recordFailureWithDescription:failure inFile:@__FILE__ atLine:__LINE__ expected:NO];
+            XCTSourceCodeContext *context = [[XCTSourceCodeContext alloc] initWithLocation:[[XCTSourceCodeLocation alloc] initWithFilePath:@__FILE__ lineNumber:__LINE__]];
+            XCTIssue *issue = [[XCTIssue alloc] initWithType:XCTIssueTypeUncaughtException compactDescription:failure detailedDescription:NULL sourceCodeContext:context associatedError:NULL attachments:[[NSArray alloc] init]];
+            [self recordIssue: issue];
             return;
         }
         if (!output) {
             NSString *failure = [NSString stringWithFormat:@"Test did not write any data to stdout\n%s", command];
-            [self recordFailureWithDescription:failure inFile:@__FILE__ atLine:__LINE__ expected:NO];
+            XCTSourceCodeContext *context = [[XCTSourceCodeContext alloc] initWithLocation:[[XCTSourceCodeLocation alloc] initWithFilePath:@__FILE__ lineNumber:__LINE__]];
+            XCTIssue *issue = [[XCTIssue alloc] initWithType:XCTIssueTypeUncaughtException compactDescription:failure detailedDescription:NULL sourceCodeContext:context associatedError:NULL attachments:[[NSArray alloc] init]];
+            [self recordIssue: issue];
             return;
         }
         NSError *error = nil;
         NSDictionary *dict = [NSPropertyListSerialization propertyListWithData:(NSData *)output options:NSPropertyListImmutable format:nil error:&error];
         if (!dict) {
             NSString *failure = [NSString stringWithFormat:@"Could not convert stdout \"%@\" to property list. Got Error %@\n%s", output, error, command];
-            [self recordFailureWithDescription:failure inFile:@__FILE__ atLine:__LINE__ expected:NO];
+            XCTSourceCodeContext *context = [[XCTSourceCodeContext alloc] initWithLocation:[[XCTSourceCodeLocation alloc] initWithFilePath:@__FILE__ lineNumber:__LINE__]];
+            XCTIssue *issue = [[XCTIssue alloc] initWithType:XCTIssueTypeUncaughtException compactDescription:failure detailedDescription:NULL sourceCodeContext:context associatedError:NULL attachments:[[NSArray alloc] init]];
+            [self recordIssue: issue];
             return;
         }
+
+        if (dict[@"LOGS"]) {
+            NSLog(@"LOGS:\n%@",[NSString stringWithFormat:@"%@\n", dict[@"LOGS"]]);
+        }
+
         if (![dict[@"PASS"] boolValue]) {
             NSString *failure = [NSString stringWithFormat:@"%@\n%s", dict[@"INFO"], command];
-            [self recordFailureWithDescription:failure inFile:dict[@"FILE"] atLine:[dict[@"LINE"] unsignedIntegerValue] expected:NO];
+            XCTSourceCodeContext *context = [[XCTSourceCodeContext alloc] initWithLocation:[[XCTSourceCodeLocation alloc] initWithFilePath:dict[@"FILE"] lineNumber:(NSInteger)dict[@"LINE"]]];
+            XCTIssue *issue = [[XCTIssue alloc] initWithType:XCTIssueTypeUncaughtException compactDescription:failure detailedDescription:NULL sourceCodeContext:context associatedError:NULL attachments:[[NSArray alloc] init]];
+            [self recordIssue: issue];
             return;
         }
     });
index dbadce357c1cd5f769287033ca56db79904042df..86e9efab620413f1bb2c36bbbc83a0893b70529d 100644 (file)
@@ -84,6 +84,9 @@ ImageLoader::ImageLoader(const char* path, unsigned int libCount)
        fPathOwnedByImage(false), fIsReferencedDownward(false), 
        fWeakSymbolsBound(false)
 {
+#if __x86_64__
+       fAotPath = NULL;
+#endif
        if ( fPath != NULL )
                fPathHash = hash(fPath);
        if ( libCount > 512 )
@@ -103,6 +106,10 @@ ImageLoader::~ImageLoader()
                delete [] fRealPath;
        if ( fPathOwnedByImage && (fPath != NULL) ) 
                delete [] fPath;
+#if __x86_64__
+       if ( fAotPath != NULL )
+               delete [] fAotPath;
+#endif
 }
 
 void ImageLoader::setFileInfo(dev_t device, ino_t inode, time_t modDate)
@@ -473,7 +480,7 @@ 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__
+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
        static bool sVmAccountingSuspended = false;
        if ( suspend == sVmAccountingSuspended )
                return;
@@ -508,7 +515,7 @@ void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool pr
 
        uint64_t t1 = mach_absolute_time();
        context.clearAllDepths();
-       this->recursiveUpdateDepth(context.imageCount());
+       this->updateDepth(context.imageCount());
 
        __block uint64_t t2, t3, t4, t5;
        {
@@ -662,7 +669,19 @@ void ImageLoader::markedUsedRecursive(const std::vector<DynamicReference>& dynam
        
 }
 
-unsigned int ImageLoader::recursiveUpdateDepth(unsigned int maxDepth)
+unsigned int ImageLoader::updateDepth(unsigned int maxDepth)
+{
+       STACK_ALLOC_ARRAY(ImageLoader*, danglingUpwards, maxDepth);
+       unsigned int depth = this->recursiveUpdateDepth(maxDepth, danglingUpwards);
+       for (auto& danglingUpward : danglingUpwards) {
+               if ( danglingUpward->fDepth != 0)
+                       continue;
+               danglingUpward->recursiveUpdateDepth(maxDepth, danglingUpwards);
+       }
+       return depth;
+}
+
+unsigned int ImageLoader::recursiveUpdateDepth(unsigned int maxDepth, dyld3::Array<ImageLoader*>& danglingUpwards)
 {
        // the purpose of this phase is to make the images sortable such that 
        // in a sort list of images, every image that an image depends on
@@ -675,17 +694,29 @@ unsigned int ImageLoader::recursiveUpdateDepth(unsigned int maxDepth)
                unsigned int minDependentDepth = maxDepth;
                for(unsigned int i=0; i < libraryCount(); ++i) {
                        ImageLoader* dependentImage = libImage(i);
-                       if ( (dependentImage != NULL) && !libIsUpward(i) ) {
-                               unsigned int d = dependentImage->recursiveUpdateDepth(maxDepth);
-                               if ( d < minDependentDepth )
-                                       minDependentDepth = d;
+                       if ( dependentImage != NULL ) {
+                               if ( libIsUpward(i) ) {
+                                       if ( dependentImage->fDepth == 0) {
+                                               if ( !danglingUpwards.contains(dependentImage) )
+                                                       danglingUpwards.push_back(dependentImage);
+                                       }
+                               } else {
+                                       unsigned int d = dependentImage->recursiveUpdateDepth(maxDepth, danglingUpwards);
+                                       if ( d < minDependentDepth )
+                                               minDependentDepth = d;
+                               }
+                       }
+                       // <rdar://problem/60878811> make sure need to re-bind propagates up
+                       if ( dependentImage != NULL ) {
+                               if ( fAllLibraryChecksumsAndLoadAddressesMatch && !dependentImage->fAllLibraryChecksumsAndLoadAddressesMatch ) {
+                                       fAllLibraryChecksumsAndLoadAddressesMatch = false;
+                               }
                        }
                }
-       
                // make me less deep then all my dependents
                fDepth = minDependentDepth - 1;
+
        }
-       
        return fDepth;
 }
 
@@ -797,22 +828,21 @@ void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool prefli
                // tell each to load its dependents
                for(unsigned int i=0; i < libraryCount(); ++i) {
                        ImageLoader* dependentImage = libImage(i);
-                       if ( dependentImage != NULL ) { 
+                       if ( dependentImage != NULL ) {
                                dependentImage->recursiveLoadLibraries(context, preflightOnly, thisRPaths, libraryInfos[i].name);
                        }
                }
-               
                // do deep prebind check
                if ( fAllLibraryChecksumsAndLoadAddressesMatch ) {
                        for(unsigned int i=0; i < libraryCount(); ++i){
                                ImageLoader* dependentImage = libImage(i);
-                               if ( dependentImage != NULL ) { 
+                               if ( dependentImage != NULL ) {
                                        if ( !dependentImage->allDependentLibrariesAsWhenPreBound() )
                                                fAllLibraryChecksumsAndLoadAddressesMatch = false;
                                }
                        }
                }
-               
+
                // free rpaths (getRPaths() malloc'ed each string)
                for(std::vector<const char*>::iterator it=rpathsFromThisImage.begin(); it != rpathsFromThisImage.end(); ++it) {
                        const char* str = *it;
@@ -910,11 +940,11 @@ void ImageLoader::recursiveMakeDataReadOnly(const LinkContext& context)
 
 void ImageLoader::recursiveBindWithAccounting(const LinkContext& context, bool forceLazysBound, bool neverUnload)
 {
-       this->recursiveBind(context, forceLazysBound, neverUnload);
+       this->recursiveBind(context, forceLazysBound, neverUnload, nullptr);
        vmAccountingSetSuspended(context, false);
 }
 
-void ImageLoader::recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload)
+void ImageLoader::recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload, const ImageLoader* parent)
 {
        // Normally just non-lazy pointers are bound immediately.
        // The exceptions are:
@@ -928,11 +958,15 @@ void ImageLoader::recursiveBind(const LinkContext& context, bool forceLazysBound
                        // bind lower level libraries first
                        for(unsigned int i=0; i < libraryCount(); ++i) {
                                ImageLoader* dependentImage = libImage(i);
-                               if ( dependentImage != NULL )
-                                       dependentImage->recursiveBind(context, forceLazysBound, neverUnload);
+                               if ( dependentImage != NULL )  {
+                                       const ImageLoader* reExportParent = nullptr;
+                                       if ( libReExported(i) )
+                                               reExportParent = this;
+                                       dependentImage->recursiveBind(context, forceLazysBound, neverUnload, reExportParent);
+                               }
                        }
                        // bind this image
-                       this->doBind(context, forceLazysBound); 
+                       this->doBind(context, forceLazysBound, parent);
                        // mark if lazys are also bound
                        if ( forceLazysBound || this->usablePrebinding(context) )
                                fAllLazyPointersBound = true;
@@ -1010,7 +1044,7 @@ void ImageLoader::weakBind(const LinkContext& context)
                        new (&context.weakDefMap) dyld3::Map<const char*, std::pair<const ImageLoader*, uintptr_t>, ImageLoader::HashCString, ImageLoader::EqualCString>();
                        context.weakDefMapInitialized = true;
                }
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
          // only do alternate algorithm for dlopen(). Use traditional algorithm for launch
          if ( !context.linkingMainExecutable ) {
                  // Don't take the memory hit of weak defs on the launch path until we hit a dlopen with more weak symbols to bind
@@ -1030,8 +1064,8 @@ void ImageLoader::weakBind(const LinkContext& context)
 
                                  Diagnostics diag;
                                  const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)image->machHeader();
-                                 ma->forEachWeakDef(diag, ^(const char *symbolName, uintptr_t imageOffset, bool isFromExportTrie) {
-                                         uintptr_t targetAddr = (uintptr_t)ma + imageOffset;
+                                 ma->forEachWeakDef(diag, ^(const char *symbolName, uint64_t imageOffset, bool isFromExportTrie) {
+                                         uintptr_t targetAddr = (uintptr_t)ma + (uintptr_t)imageOffset;
                                          if ( isFromExportTrie ) {
                                                  // Avoid duplicating the string if we already have the symbol name
                                                  if ( context.weakDefMap.find(symbolName) != context.weakDefMap.end() )
@@ -1053,8 +1087,8 @@ void ImageLoader::weakBind(const LinkContext& context)
                                  continue;
                          Diagnostics diag;
                          const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)image->machHeader();
-                         ma->forEachWeakDef(diag, ^(const char *symbolName, uintptr_t imageOffset, bool isFromExportTrie) {
-                                 uintptr_t targetAddr = (uintptr_t)ma + imageOffset;
+                         ma->forEachWeakDef(diag, ^(const char *symbolName, uint64_t imageOffset, bool isFromExportTrie) {
+                                 uintptr_t targetAddr = (uintptr_t)ma + (uintptr_t)imageOffset;
                                  if ( isFromExportTrie ) {
                                          // Avoid duplicating the string if we already have the symbol name
                                          if ( context.weakDefMap.find(symbolName) != context.weakDefMap.end() )
@@ -1133,7 +1167,7 @@ void ImageLoader::weakBind(const LinkContext& context)
                }
          }
          else
-#endif // __MAC_OS_X_VERSION_MIN_REQUIRED
+#endif // TARGET_OS_OSX
          {
                // make symbol iterators for each
                ImageLoader::CoalIterator iterators[count];
@@ -1297,7 +1331,7 @@ void ImageLoader::weakBindOld(const LinkContext& context)
 
        // don't need to do any coalescing if only one image has overrides, or all have already been done
        if ( (countOfImagesWithWeakDefinitionsNotInSharedCache > 0) && (countNotYetWeakBound > 0) ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
          // only do alternate algorithm for dlopen(). Use traditional algorithm for launch
          if ( !context.linkingMainExecutable ) {
                // for all images that need weak binding
@@ -1358,7 +1392,7 @@ void ImageLoader::weakBindOld(const LinkContext& context)
                }
          }
          else
-#endif // __MAC_OS_X_VERSION_MIN_REQUIRED
+#endif // TARGET_OS_OSX
          {
                // make symbol iterators for each
                ImageLoader::CoalIterator iterators[count];
@@ -1881,11 +1915,25 @@ intptr_t ImageLoader::read_sleb128(const uint8_t*& p, const uint8_t* end)
                bit += 7;
        } while (byte & 0x80);
        // sign extend negative numbers
-       if ( (byte & 0x40) != 0 )
+       if ( ((byte & 0x40) != 0) && (bit < 64) )
                result |= (~0ULL) << bit;
        return (intptr_t)result;
 }
 
+void ImageLoader::forEachReExportDependent( void (^callback)(const ImageLoader*, bool& stop)) const
+{
+       bool stop = false;
+       for (unsigned int i=0; i < libraryCount(); ++i) {
+               if ( libReExported(i) ) {
+                       if ( ImageLoader* dependentImage = libImage(i) ) {
+                               callback(dependentImage, stop);
+                       }
+               }
+               if (stop)
+                       break;
+       }
+}
+
 
 VECTOR_NEVER_DESTRUCTED_IMPL(ImageLoader::InterposeTuple);
 VECTOR_NEVER_DESTRUCTED_IMPL(ImagePair);
index 70107dca44351ab1ba7534b7373242c11fe15dcb..0bcc93ab949b9a0d4a08772c47866103e104775c 100644 (file)
@@ -71,7 +71,7 @@
 #define LOG_BINDINGS 0
 
 
-#if __IPHONE_OS_VERSION_MIN_REQUIRED          
+#if TARGET_OS_IPHONE
        #define SPLIT_SEG_SHARED_REGION_SUPPORT 0
        #define SPLIT_SEG_DYLIB_SUPPORT                 0
        #define PREBOUND_IMAGE_SUPPORT                  __arm__
@@ -90,7 +90,7 @@
        #define PREBOUND_IMAGE_SUPPORT                  __i386__
        #define TEXT_RELOC_SUPPORT                              __i386__
        #define SUPPORT_OLD_CRT_INITIALIZATION  __i386__
-       #define SUPPORT_LC_DYLD_ENVIRONMENT             (__i386__ || __x86_64__)
+       #define SUPPORT_LC_DYLD_ENVIRONMENT             1
        #define SUPPORT_VERSIONED_PATHS                 1
        #define SUPPORT_CLASSIC_MACHO                   1
        #define SUPPORT_ZERO_COST_EXCEPTIONS    1
@@ -319,8 +319,9 @@ public:
                bool                    bindFlat;
                bool                    linkingMainExecutable;
                bool                    startedInitializingMainExecutable;
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
                bool                    iOSonMac;
+               // dyld doesn't build for driverKit, so we use the macOS define to control whether driverKit is supported
                bool                    driverKit;
 #endif
                bool                    verboseOpts;
@@ -617,7 +618,7 @@ public:
                                                                                // 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                                            recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload, const ImageLoader* parent);
        void                                                            recursiveBindWithAccounting(const LinkContext& context, bool forceLazysBound, bool neverUnload);
        void                                                            recursiveRebaseWithAccounting(const LinkContext& context);
        void                                                            weakBind(const LinkContext& context);
@@ -647,6 +648,8 @@ public:
        void                                                            setNeverUnload() { fNeverUnload = true; fLeaveMapped = true; }
        void                                                            setNeverUnloadRecursive();
        
+       void                                                            forEachReExportDependent( void (^callback)(const ImageLoader*, bool& stop)) const;
+
        bool                                                            isReferencedDownward() { return fIsReferencedDownward; }
 
        virtual void                                            recursiveMakeDataReadOnly(const LinkContext& context);
@@ -752,7 +755,8 @@ protected:
                                                // To link() an image, its dependent libraries are loaded, it is rebased, bound, and initialized.
                                                // These methods do the above, exactly once, and it the right order
        virtual void            recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath);
-       virtual unsigned        recursiveUpdateDepth(unsigned int maxDepth);
+       virtual unsigned        recursiveUpdateDepth(unsigned int maxDepth, dyld3::Array<ImageLoader*>& danglingUpwards);
+       virtual unsigned        updateDepth(unsigned int maxDepth);
        virtual void            recursiveRebase(const LinkContext& context);
        virtual void            recursiveApplyInterposing(const LinkContext& context);
        virtual void            recursiveGetDOFSections(const LinkContext& context, std::vector<DOFInfo>& dofs);
@@ -769,7 +773,7 @@ protected:
        virtual void                            doRebase(const LinkContext& context) = 0;
        
                                                                // do any symbolic fix ups in this image
-       virtual void                            doBind(const LinkContext& context, bool forceLazysBound) = 0;
+       virtual void                            doBind(const LinkContext& context, bool forceLazysBound, const ImageLoader* reExportParent) = 0;
        
                                                                // called later via API to force all lazy pointer to be bound
        virtual void                            doBindJustLazies(const LinkContext& context) = 0;
@@ -844,6 +848,9 @@ public:
 protected:
        static std::vector<InterposeTuple>      fgInterposingTuples;
        
+#if __x86_64__
+       const char*                 fAotPath;
+#endif
        const char*                                     fPath;
        const char*                                     fRealPath;
        dev_t                                           fDevice;
@@ -899,6 +906,7 @@ private:
        static_assert(sizeof(sizeOfData) == 8, "Bad data size");
 
        static uint16_t                         fgLoadOrdinal;
+
 };
 
 
index 1768df8a738fa866fdeb9028f5c6d83b97329fc7..f2fee4226bebf09ae9c287af4f7f265b0dd6ffb0 100644 (file)
@@ -64,7 +64,7 @@ extern "C" long __stack_chk_guard;
 
 #define LIBSYSTEM_DYLIB_PATH                     "/usr/lib/libSystem.B.dylib"
 #define LIBDYLD_DYLIB_PATH                       "/usr/lib/system/libdyld.dylib"
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
   #define DRIVERKIT_LIBSYSTEM_DYLIB_PATH  "/System/DriverKit/usr/lib/libSystem.dylib"
   #define DRIVERKIT_LIBDYLD_DYLIB_PATH           "/System/DriverKit/usr/lib/system/libdyld.dylib"
 #endif
@@ -127,7 +127,7 @@ fSegmentsCount(segCount), fIsSplitSeg(false), fInSharedCache(false),
 
 }
 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
 static uintptr_t pageAlign(uintptr_t value)
 {
        return (value + 4095) & (-4096);
@@ -148,8 +148,6 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat
 
        const uint32_t cmd_count = mh->ncmds;
        const uint32_t sizeofcmds = mh->sizeofcmds;
-       if ( sizeofcmds > (MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE-sizeof(macho_header)) )
-               dyld::throwf("malformed mach-o: load commands size (%u) > %u", sizeofcmds, MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE);
        if ( cmd_count > (sizeofcmds/sizeof(load_command)) )
                dyld::throwf("malformed mach-o: ncmds (%u) too large to fit in sizeofcmds (%u)", cmd_count, sizeofcmds);
        const struct load_command* const startCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header));
@@ -197,7 +195,7 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat
                                break;
                        case LC_SEGMENT_COMMAND:
                                segCmd = (struct macho_segment_command*)cmd;
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
                                // rdar://problem/19617624 allow unmapped segments on OSX (but not iOS)
                                if ( ((segCmd->filesize) > pageAlign(segCmd->vmsize)) && (segCmd->vmsize != 0) )
 #else
@@ -349,7 +347,7 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat
                                        throw "malformed mach-o image: LC_DYSYMTAB size wrong";
                                dynSymbTabCmd = (dysymtab_command*)cmd;
                                break;
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
                        // <rdar://problem/26797345> error when loading iOS Simulator mach-o binary into macOS process
                        case LC_VERSION_MIN_WATCHOS:
                        case LC_VERSION_MIN_TVOS:
@@ -510,7 +508,7 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat
                        if ( context.strictMachORequired || (symTabCmd->stroff + symTabCmd->strsize > ((linkeditFileOffsetEnd + 4095) & (-4096))) )
                                throw "malformed mach-o image: symbol strings overrun __LINKEDIT";
                }
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
                if ( (symTabCmd->symoff % sizeof(void*)) != 0 ) {
                        // <rdar://53723577> allow old malformed plugins in new app
                        if ( sdkVersion((mach_header*)mh) >= DYLD_PACKED_VERSION(10,15,0) )
@@ -672,7 +670,7 @@ 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 !__MAC_OS_X_VERSION_MIN_REQUIRED
+       #if !TARGET_OS_OSX
                        // <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());
@@ -763,7 +761,7 @@ void ImageLoaderMachO::parseLoadCmds(const LinkContext& context)
                                {
                                        const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
                                        const bool isTextSeg = (strcmp(seg->segname, "__TEXT") == 0);
-               #if __i386__ && __MAC_OS_X_VERSION_MIN_REQUIRED
+               #if __i386__ && TARGET_OS_OSX
                                        const bool isObjCSeg = (strcmp(seg->segname, "__OBJC") == 0);
                                        if ( isObjCSeg )
                                                fNotifyObjC = true;
@@ -787,7 +785,7 @@ void ImageLoaderMachO::parseLoadCmds(const LinkContext& context)
                                                else if ( isTextSeg && (strcmp(sect->sectname, "__unwind_info") == 0) )
                                                        fUnwindInfoSectionOffset = (uint32_t)((uint8_t*)sect - fMachOData);
 
-               #if __i386__ && __MAC_OS_X_VERSION_MIN_REQUIRED
+               #if __i386__ && TARGET_OS_OSX
                                                else if ( isObjCSeg ) {
                                                        if ( strcmp(sect->sectname, "__image_info") == 0 ) {
                                                                const uint32_t* imageInfo = (uint32_t*)(sect->addr + fSlide);
@@ -801,7 +799,7 @@ void ImageLoaderMachO::parseLoadCmds(const LinkContext& context)
                                                }
                #else
                                                else if ( isDataSeg && (strncmp(sect->sectname, "__objc_imageinfo", 16) == 0) ) {
-                       #if __MAC_OS_X_VERSION_MIN_REQUIRED
+                       #if TARGET_OS_OSX
                                                        const uint32_t* imageInfo = (uint32_t*)(sect->addr + fSlide);
                                                        uint32_t flags = imageInfo[1];
                                                        if ( (flags & 4) && (((macho_header*)fMachOData)->filetype != MH_EXECUTE) )
@@ -1139,7 +1137,7 @@ void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* cod
                disableCoverageCheck();
        }
        else {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
                // <rdar://problem/13622786> ignore code signatures in binaries built with pre-10.9 tools
                if ( this->sdkVersion() < DYLD_PACKED_VERSION(10,9,0) ) {
                        disableCoverageCheck();
@@ -1191,7 +1189,7 @@ void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* cod
 
 void ImageLoaderMachO::validateFirstPages(const struct linkedit_data_command* codeSigCmd, int fd, const uint8_t *fileData, size_t lenFileData, off_t offsetInFat, const LinkContext& context)
 {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
        // rdar://problem/21839703> 15A226d: dyld crashes in mageLoaderMachO::validateFirstPages during dlopen() after encountering an mmap failure
        // We need to ignore older code signatures because they will be bad.
        if ( this->sdkVersion() < DYLD_PACKED_VERSION(10,9,0) ) {
@@ -1346,8 +1344,13 @@ void* ImageLoaderMachO::getEntryFromLC_MAIN() const
                        entry_point_command* mainCmd = (entry_point_command*)cmd;
                        void* entry = (void*)(mainCmd->entryoff + (char*)fMachOData);
                        // <rdar://problem/8543820&9228031> verify entry point is in image
-                       if ( this->containsAddress(entry) )
+                       if ( this->containsAddress(entry) ) {
+#if __has_feature(ptrauth_calls)
+                               // start() calls the result pointer as a function pointer so we need to sign it.
+                               return __builtin_ptrauth_sign_unauthenticated(entry, 0, 0);
+#endif
                                return entry;
+                       }
                        else
                                throw "LC_MAIN entryoff is out of range";
                }
@@ -1376,13 +1379,6 @@ void* ImageLoaderMachO::getEntryFromLC_UNIXTHREAD() const
                        // <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);
@@ -1415,10 +1411,9 @@ bool ImageLoaderMachO::needsAddedLibSystemDepency(unsigned int libCount, const m
                                {
                                const dylib_command* dylibID = (dylib_command*)cmd;
                                const char* installPath = (char*)cmd + dylibID->dylib.name.offset;
-                               // It is OK for OS dylibs (libSystem or libmath or Rosetta shims) to have no dependents
+                               // It is OK for OS dylibs (libSystem or libmath) to have no dependents
                                // but all other dylibs must depend on libSystem for initialization to initialize libSystem first
-                               // <rdar://problem/6497528> rosetta circular dependency spew
-                               isNonOSdylib = ( (strncmp(installPath, "/usr/lib/", 9) != 0) && (strncmp(installPath, "/System/DriverKit/usr/lib/", 26) != 0) && (strncmp(installPath, "/usr/libexec/oah/Shims", 9) != 0) );
+                               isNonOSdylib = ( (strncmp(installPath, "/usr/lib/", 9) != 0) && (strncmp(installPath, "/System/DriverKit/usr/lib/", 26) != 0) );
                                // if (isNonOSdylib) dyld::log("ImageLoaderMachO::needsAddedLibSystemDepency(%s)\n", installPath);
                                }
                                break;
@@ -1544,7 +1539,7 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vector<const c
                                                strlcpy(newPath, *rp, PATH_MAX);
                                                strlcat(newPath, path, PATH_MAX);
                                                struct stat stat_buf;
-                                               if ( stat(newPath, &stat_buf) != -1 ) {
+                                               if ( dyld3::stat(newPath, &stat_buf) != -1 ) {
                                                        // dyld::log("combined DYLD_ROOT_PATH and LC_RPATH: %s\n", newPath);
                                                        paths.push_back(strdup(newPath));
                                                }
@@ -2079,7 +2074,7 @@ extern "C" int _dyld_func_lookup(const char* name, void** address);
 
 static const char* libDyldPath(const ImageLoader::LinkContext& context)
 {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
        if ( context.driverKit )
                return DRIVERKIT_LIBDYLD_DYLIB_PATH;
        else
@@ -2282,7 +2277,7 @@ void ImageLoaderMachO::doImageInit(const LinkContext& context)
 
 static const char* libSystemPath(const ImageLoader::LinkContext& context)
 {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
        if ( context.driverKit )
                return DRIVERKIT_LIBSYSTEM_DYLIB_PATH;
        else
@@ -2494,13 +2489,13 @@ void ImageLoaderMachO::printStatisticsDetails(unsigned int imageCount, const Ini
        dyld::log("total images using weak symbols:  %u\n", fgImagesRequiringCoalescing);
 }
 
-
-intptr_t ImageLoaderMachO::assignSegmentAddresses(const LinkContext& context)
+intptr_t ImageLoaderMachO::assignSegmentAddresses(const LinkContext& context, size_t extraAllocationSize)
 {
        // preflight and calculate slide if needed
        const bool inPIE = (fgNextPIEDylibAddress != 0);
        intptr_t slide = 0;
        if ( this->segmentsCanSlide() && this->segmentsMustSlideTogether() ) {
+               intptr_t segmentReAlignSlide = 0;
                bool needsToSlide = false;
                bool imageHasPreferredLoadAddress = segHasPreferredLoadAddress(0);
                uintptr_t lowAddr = (unsigned long)(-1);
@@ -2518,23 +2513,54 @@ intptr_t ImageLoaderMachO::assignSegmentAddresses(const LinkContext& context)
                                lowAddr = segLow;
                        if ( segHigh > highAddr )
                                highAddr = segHigh;
-                               
+
+#if defined(__x86_64__) && !TARGET_OS_SIMULATOR
+                       // For Cambria on Aruba systems (16k page size), realign the image so the first segment ends on a 16k boundry.
+                       // FIXME: this can be removed when Aruba dev systems are no longer supported.
+                       if ( dyld::isTranslated() && vm_page_size == 0x4000 && i == 0 && segLow == 0 ) {
+                               const uintptr_t segHighPageOffset = segHigh & vm_page_mask;
+                               if ( segHighPageOffset > 0 ) {
+                                       // Adjust the slide to make the first segment end on a page boundry.
+                                       needsToSlide = true;
+                                       segmentReAlignSlide = vm_page_size - segHighPageOffset;
+
+                                       if (context.verboseMapping) {
+                                               dyld::log("dyld: Image %s first segment(%s) does not end on a page boundry [0x%lx, 0x%lx) adding 0x%lx to slide to realign\n", getPath(), segName(i), segLow, segHigh, segmentReAlignSlide);
+                                       }
+                               }
+                       }
+#endif
                        if ( needsToSlide || !imageHasPreferredLoadAddress || inPIE || !reserveAddressRange(segPreferredLoadAddress(i), segSize(i)) )
                                needsToSlide = true;
                }
                if ( needsToSlide ) {
                        // find a chunk of address space to hold all segments
-                       uintptr_t addr = reserveAnAddressRange(highAddr-lowAddr, context);
-                       slide = addr - lowAddr;
+                       size_t size = highAddr-lowAddr+segmentReAlignSlide;
+                       uintptr_t addr = reserveAnAddressRange(size+extraAllocationSize, context);
+                       slide = addr - lowAddr + segmentReAlignSlide;
+               } else if ( extraAllocationSize ) {
+                       if (!reserveAddressRange(highAddr, extraAllocationSize)) {
+                               throw "failed to reserve space for aot";
+                       }
                }
        } 
        else if ( ! this->segmentsCanSlide() ) {
+               uintptr_t highAddr = 0;
                for(unsigned int i=0, e=segmentCount(); i < e; ++i) {
+                       const uintptr_t segLow = segPreferredLoadAddress(i);
+                       const uintptr_t segHigh = dyld_page_round(segLow + segSize(i));
+
+                       if ( segHigh > highAddr )
+                               highAddr = segHigh;
+
                        if ( (strcmp(segName(i), "__PAGEZERO") == 0) && (segFileSize(i) == 0) && (segPreferredLoadAddress(i) == 0) )
                                continue;
                        if ( !reserveAddressRange(segPreferredLoadAddress(i), segSize(i)) )
                                dyld::throwf("can't map unslidable segment %s to 0x%lX with size 0x%lX", segName(i), segPreferredLoadAddress(i), segSize(i));
                }
+               if (extraAllocationSize) {
+                       dyld::throwf("binaries with non-slidable segments don't support aot: %s", this->getPath());
+               }
        }
        else {
                throw "mach-o does not support independently sliding segments";
@@ -2576,12 +2602,23 @@ bool ImageLoaderMachO::reserveAddressRange(uintptr_t start, size_t length)
        return true;
 }
 
-
-
 void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context)
 {
+       uint64_t extra_allocation_size = 0;
+
+#if defined(__x86_64__) && !TARGET_OS_SIMULATOR
+       if (dyld::isTranslated()) {
+               fAotPath = new char[PATH_MAX];
+               int ret = syscall(0x7000001, fd, this->getPath(), &extra_allocation_size, fAotPath, PATH_MAX);
+               if (ret != 0) {
+                       delete fAotPath;
+                       fAotPath = nullptr;
+               }
+       }
+#endif
+
        // find address range for image
-       intptr_t slide = this->assignSegmentAddresses(context);
+       intptr_t slide = this->assignSegmentAddresses(context, extra_allocation_size);
        if ( context.verboseMapping ) {
                if ( offsetInFat != 0 )
                        dyld::log("dyld: Mapping %s (slice offset=%llu)\n", this->getPath(), (unsigned long long)offsetInFat);
@@ -2599,10 +2636,24 @@ void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInF
                dyld::log("dyld: Speculatively read offset=0x%08llX, len=0x%08llX, path=%s\n", offsetInFat, lenInFat, this->getPath());
 
        // map in all segments
+       uintptr_t baseAddress = (unsigned long)(-1);
+       uintptr_t endAddress = 0;
+       uintptr_t mappedMachHeaderAddress = 0;
        for(unsigned int i=0, e=segmentCount(); i < e; ++i) {
                vm_offset_t fileOffset = (vm_offset_t)(segFileOffset(i) + offsetInFat);
                vm_size_t size = segFileSize(i);
                uintptr_t requestedLoadAddress = segPreferredLoadAddress(i) + slide;
+               const uintptr_t segmentEnd = dyld_page_round(requestedLoadAddress + segSize(i));
+
+               if ( requestedLoadAddress < baseAddress )
+                       baseAddress = requestedLoadAddress;
+               if ( segmentEnd > endAddress )
+                       endAddress = segmentEnd;
+
+               if (segFileOffset(i) == 0 && segFileSize(i) != 0) {
+                       mappedMachHeaderAddress = requestedLoadAddress;
+               }
+
                int protection = 0;
                if ( !segUnaccessible(i) ) {
                        if ( segExecutable(i) )
@@ -2649,6 +2700,24 @@ void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInF
                                (protection & PROT_READ) ? 'r' : '.',  (protection & PROT_WRITE) ? 'w' : '.',  (protection & PROT_EXEC) ? 'x' : '.' );
        }
 
+#if defined(__x86_64__) && !TARGET_OS_SIMULATOR
+       if (dyld::isTranslated() && extra_allocation_size != 0) {
+               const struct mach_header* aot_load_address;
+               dyld_aot_image_info aot_image_info = {};
+               int ret = syscall(0x7000002, this->getPath(), mappedMachHeaderAddress, endAddress, &aot_load_address, &aot_image_info.aotImageSize, aot_image_info.aotImageKey);
+               if (ret == 0) {
+                       extern void addAotImagesToAllAotImages(uint32_t aotInfoCount, const dyld_aot_image_info aotInfo[]);
+
+                       // fill in the aot load address, at this point the cambria trap has filled in
+                       // the image size and image key fields
+                       aot_image_info.aotLoadAddress = aot_load_address;
+                       aot_image_info.x86LoadAddress = (struct mach_header*)baseAddress;
+
+                       addAotImagesToAllAotImages(1, &aot_image_info);
+               }
+       }
+#endif
+
        // update slide to reflect load location                        
        this->setSlide(slide);
 }
@@ -2656,7 +2725,7 @@ void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInF
 void ImageLoaderMachO::mapSegments(const void* memoryImage, uint64_t imageLen, const LinkContext& context)
 {
        // find address range for image
-       intptr_t slide = this->assignSegmentAddresses(context);
+       intptr_t slide = this->assignSegmentAddresses(context, 0);
        if ( context.verboseMapping )
                dyld::log("dyld: Mapping memory %p\n", memoryImage);
        // map in all segments
@@ -2674,24 +2743,52 @@ void ImageLoaderMachO::mapSegments(const void* memoryImage, uint64_t imageLen, c
        this->setSlide(slide);
        // set R/W permissions on all segments at slide location
        for(unsigned int i=0, e=segmentCount(); i < e; ++i) {
-               segProtect(i, context);         
+               segProtect(i, context);
        }
 }
 
+static vm_prot_t protectionForSegIndex(const ImageLoaderMachO* image, unsigned int segIndex)
+{
+       if ( image->segUnaccessible(segIndex) )
+               return 0;
+       vm_prot_t protection = 0;
+       if ( image->segExecutable(segIndex) )
+               protection |= PROT_EXEC;
+       if ( image->segReadable(segIndex) )
+               protection |= PROT_READ;
+       if ( image->segWriteable(segIndex) )
+               protection |= PROT_WRITE;
+       return protection;
+}
+
 
 void ImageLoaderMachO::segProtect(unsigned int segIndex, const ImageLoader::LinkContext& context)
 {
-       vm_prot_t protection = 0;
-       if ( !segUnaccessible(segIndex) ) {
-               if ( segExecutable(segIndex) )
-                       protection   |= PROT_EXEC;
-               if ( segReadable(segIndex) )
-                       protection   |= PROT_READ;
-               if ( segWriteable(segIndex) )
-                       protection   |= PROT_WRITE;
-       }
+       vm_prot_t protection = protectionForSegIndex(this, segIndex);
        vm_address_t addr = segActualLoadAddress(segIndex);
        vm_size_t size = segSize(segIndex);
+
+#if defined(__x86_64__) && !TARGET_OS_SIMULATOR
+       if ( dyld::isTranslated() ) {
+               // <rdar://problem/60543794> can't vm_protect non-16KB segments
+               if ( (segIndex > 0) && ((addr & 0x3FFF) != 0) ) {
+                       // overlaps previous segment
+                       vm_prot_t prevProt = protectionForSegIndex(this, segIndex-1);
+                       if ( (protection & prevProt) != prevProt ) {
+                               // previous had more bits, so we need to not apply new permissions to the overlap
+                               vm_size_t overlap = 0x4000 - (addr & 0x3FFF);
+                               addr += overlap;
+                               if ( size >= overlap )
+                                       size -= overlap;
+                               else if ( size < overlap )
+                                       size = 0;
+                       }
+                       if ( size == 0 )
+                               return;
+               }
+       }
+#endif
+
        const bool setCurrentPermissions = false;
        kern_return_t r = vm_protect(mach_task_self(), addr, size, setCurrentPermissions, protection);
        if ( r != KERN_SUCCESS ) {
index 867f4b3b3eb8834c8f58dedf9c923ebf70d08ee8..cf8325be2d8be8d762e5f774438608fff4fbe9bf 100644 (file)
@@ -198,7 +198,7 @@ protected:
        virtual void            getRPaths(const LinkContext& context, std::vector<const char*>&) const;
        virtual bool            getUUID(uuid_t) const;
        virtual void            doRebase(const LinkContext& context);
-       virtual void            doBind(const LinkContext& context, bool forceLazysBound) = 0;
+       virtual void            doBind(const LinkContext& context, bool forceLazysBound, const ImageLoader* reExportParent) = 0;
        virtual void            doBindJustLazies(const LinkContext& context) = 0;
        virtual bool            doInitialization(const LinkContext& context);
        virtual void            doGetDOFSections(const LinkContext& context, std::vector<ImageLoader::DOFInfo>& dofs);
@@ -230,7 +230,7 @@ protected:
                        bool            segIsReadOnlyImport(unsigned int) const;
 #endif
                        bool            segIsReadOnlyData(unsigned int) const;
-                       intptr_t        assignSegmentAddresses(const LinkContext& context);
+                       intptr_t        assignSegmentAddresses(const LinkContext& context, size_t extraAllocationSize);
                        uintptr_t       reserveAnAddressRange(size_t length, const ImageLoader::LinkContext& context);
                        bool            reserveAddressRange(uintptr_t start, size_t length);
                        void            mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context);
index 03d2ffe28baac360079b5725c08a8f947d27587c..c2986daa07d4e74d1be3d4cfdfdf14035ff23d66 100644 (file)
@@ -1852,7 +1852,7 @@ void ImageLoaderMachOClassic::initializeLazyStubs(const LinkContext& context)
 #endif // __i386__
 
 
-void ImageLoaderMachOClassic::doBind(const LinkContext& context, bool forceLazysBound)
+void ImageLoaderMachOClassic::doBind(const LinkContext& context, bool forceLazysBound, const ImageLoader* reExportParent)
 {
        CRSetCrashLogMessage2(this->getPath());
 #if __i386__
index 4196c44bd7ba33d81ba474d50a109fd6d4c4dac8..ec63e8f00247b5a08f41813610eff6b3798d3126 100644 (file)
@@ -54,7 +54,7 @@ public:
        virtual bool                                            libReExported(unsigned int) const;
        virtual bool                                            libIsUpward(unsigned int) const;
        virtual void                                            setLibImage(unsigned int, ImageLoader*, bool, bool);
-       virtual void                                            doBind(const LinkContext& context, bool forceLazysBound);
+       virtual void                                            doBind(const LinkContext& context, bool forceLazysBound, const ImageLoader* reExportParent);
        virtual void                                            doBindJustLazies(const LinkContext& context);
        virtual uintptr_t                                       doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context);
        virtual uintptr_t                                       doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)());
index 1e01ed17cce5ce599c3f3ebe3b45715cb7548da5..b60350b070b7b78d2c0d351bf1c22c0d84fe8f73 100644 (file)
@@ -135,7 +135,7 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(cons
                const char* installName = image->getInstallPath();
                if ( (installName != NULL) && (strcmp(installName, path) == 0) && (path[0] == '/') )
                        image->setPathUnowned(installName);
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
                // <rdar://problem/6563887> app crashes when libSystem cannot be found
                else if ( (installName != NULL) && (strcmp(path, "/usr/lib/libgcc_s.1.dylib") == 0) && (strcmp(installName, "/usr/lib/libSystem.B.dylib") == 0) )
                        image->setPathUnowned("/usr/lib/libSystem.B.dylib");
@@ -317,7 +317,7 @@ bool ImageLoaderMachOCompressed::libReExported(unsigned int libIndex) const
 bool ImageLoaderMachOCompressed::libIsUpward(unsigned int libIndex) const
 {
        const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t)));
-       // re-export flag is second bit
+       // upward flag is second bit
        return ((images[libIndex] & 2) != 0);
 }      
 
@@ -765,7 +765,7 @@ uintptr_t ImageLoaderMachOCompressed::resolveTwolevel(const LinkContext& context
        extern const mach_header __dso_handle;
        uint32_t dyldMinOS = ImageLoaderMachO::minOSVersion(&__dso_handle);
        if ( imageMinOS > dyldMinOS ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
                const char* msg = dyld::mkstringf(" (which was built for Mac OS X %d.%d)", imageMinOS >> 16, (imageMinOS >> 8) & 0xFF);
 #else
                const char* msg = dyld::mkstringf(" (which was built for iOS %d.%d)", imageMinOS >> 16, (imageMinOS >> 8) & 0xFF);
@@ -799,7 +799,7 @@ uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const
                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);
+               symbolAddress = this->resolveWeak(context, symbolName, weak_import, runResolver, targetImage);
        }
        else {
                if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) {
@@ -825,8 +825,14 @@ uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const
                                symbolAddress = 0;
                        }
                        else {
-                               dyld::throwf("can't resolve symbol %s in %s because dependent dylib #%ld could not be loaded",
-                                       symbolName, this->getPath(), libraryOrdinal);
+                               // Try get the path from the load commands
+                               if ( const char* depPath = libPath((unsigned int)libraryOrdinal-1) ) {
+                                       dyld::throwf("can't resolve symbol %s in %s because dependent dylib %s could not be loaded",
+                                                                symbolName, this->getPath(), depPath);
+                               } else {
+                                       dyld::throwf("can't resolve symbol %s in %s because dependent dylib #%ld could not be loaded",
+                                                                symbolName, this->getPath(), libraryOrdinal);
+                               }
                        }
                }
                else {
@@ -876,7 +882,7 @@ void ImageLoaderMachOCompressed::throwBadBindingAddress(uintptr_t address, uintp
 }
 
 
-void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLazysBound)
+void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLazysBound, const ImageLoader* reExportParent)
 {
        CRSetCrashLogMessage2(this->getPath());
 
@@ -957,11 +963,28 @@ void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLa
                // 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);
                uint32_t imageIndex = dyldCacheOverrideImageNum - (uint32_t)context.dyldCache->cachedDylibsImageArray()->startImageNum();
+               //dyld::log("doBind() found override of %s\n", this->getPath());
                context.dyldCache->forEachPatchableExport(imageIndex, ^(uint32_t cacheOffsetOfImpl, const char* exportName) {
                        uintptr_t newImpl = 0;
-                       const ImageLoader* foundIn;
-                       this->findExportedSymbolAddress(context, exportName, NULL, 0, false, &foundIn, &newImpl);
-                       patchCacheUsesOf(context, overriddenImage, cacheOffsetOfImpl, exportName, newImpl);
+                       const ImageLoader* foundIn = nullptr;
+                       if ( this->findExportedSymbolAddress(context, exportName, NULL, 0, false, &foundIn, &newImpl) ) {
+                               //dyld::log("   patchCacheUsesOf(%s) found in %s\n", exportName, foundIn->getPath());
+                               patchCacheUsesOf(context, overriddenImage, cacheOffsetOfImpl, exportName, newImpl);
+                       }
+                       else {
+                               // <rdar://problem/59196856> allow patched impls to move between re-export sibling dylibs
+                               if ( reExportParent != nullptr ) {
+                                       reExportParent->forEachReExportDependent(^(const ImageLoader* reExportedDep, bool& stop) {
+                                               uintptr_t siblingImpl = 0;
+                                               const ImageLoader* foundInSibling = nullptr;
+                                               if ( reExportedDep->findExportedSymbolAddress(context, exportName, NULL, 0, false, &foundInSibling, &siblingImpl) ) {
+                                                       stop = true;
+                                                       //dyld::log("   patchCacheUsesOf(%s) found in sibling %s\n", exportName, foundInSibling->getPath());
+                                                       patchCacheUsesOf(context, overriddenImage, cacheOffsetOfImpl, exportName, siblingImpl);
+                                               }
+                                       });
+                               }
+                       }
                });
        }
 
@@ -1040,11 +1063,12 @@ void ImageLoaderMachOCompressed::registerInterposing(const LinkContext& context)
                                                return;
                                        uint64_t rebaseTargetRuntimeOffset;
                                        uint32_t bindOrdinal;
+                                       int64_t  ptrAddend;
                                        if ( fixupLoc->isRebase(pointerFormat, 0, rebaseTargetRuntimeOffset) ) {
                                                //dyld::log("interpose rebase at fixup at %p to 0x%0llX\n", fixupLoc, rebaseTargetRuntimeOffset);
                                                lastRebaseTarget = (uintptr_t)(fMachOData+rebaseTargetRuntimeOffset);
                                        }
-                               else if ( fixupLoc->isBind(pointerFormat, bindOrdinal) ) {
+                               else if ( fixupLoc->isBind(pointerFormat, bindOrdinal, ptrAddend) ) {
                                                //dyld::log("interpose bind fixup at %p to bind ordinal %d\n", fixupLoc, bindOrdinal);
                                                __block uint32_t targetBindIndex = 0;
                                                ma->forEachChainedFixupTarget(diag, ^(int libraryOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop) {
@@ -1132,8 +1156,8 @@ void ImageLoaderMachOCompressed::registerInterposing(const LinkContext& context)
                                                }
                                                ImageLoader::fgInterposingTuples.push_back(tuple);
                                        }
-                               }, ^(const char* symbolName){ },
-                                  ^() { });
+                               }, ^(const char* symbolName){
+                               });
                        }
                }
        });
@@ -1167,6 +1191,13 @@ void ImageLoaderMachOCompressed::makeDataReadOnly() const
                        if ( segIsReadOnlyData(i) ) {
                                uintptr_t start = segActualLoadAddress(i);
                                uintptr_t size = segSize(i);
+       #if defined(__x86_64__) && !TARGET_OS_SIMULATOR
+                               if ( dyld::isTranslated() ) {
+                                       // <rdar://problem/48325338> can't mprotect non-16KB segments
+                                       if ( ((size & 0x3FFF) != 0) || ((start & 0x3FFF) != 0) )
+                                               continue;
+                               }
+       #endif
                                ::mprotect((void*)start, size, PROT_READ);
                                //dyld::log("make read-only 0x%09lX -> 0x%09lX\n", (long)start, (long)(start+size));
                        }
@@ -2060,7 +2091,7 @@ void ImageLoaderMachOCompressed::updateOptimizedLazyPointers(const LinkContext&
 
 void ImageLoaderMachOCompressed::registerEncryption(const encryption_info_command* encryptCmd, const LinkContext& context)
 {
-#if __arm__ || __arm64__
+#if (__arm__ || __arm64__) && !TARGET_OS_SIMULATOR
        if ( encryptCmd == NULL )
                return;
        // fMachOData not set up yet, need to manually find mach_header
index 6f8959f8d35ca9185a40b4d86c70b65aebaaea9b..9232e29158aad443dc121a5353709636b1c0273c 100644 (file)
@@ -57,7 +57,7 @@ public:
        virtual bool                                            libReExported(unsigned int) const;
        virtual bool                                            libIsUpward(unsigned int) const;
        virtual void                                            setLibImage(unsigned int, ImageLoader*, bool, bool);
-       virtual void                                            doBind(const LinkContext& context, bool forceLazysBound);
+       virtual void                                            doBind(const LinkContext& context, bool forceLazysBound, const ImageLoader* reExportParent);
        virtual void                                            doBindJustLazies(const LinkContext& context);
        virtual uintptr_t                                       doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context);
        virtual uintptr_t                                       doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)());
index 71374b0349254e83c1e783b1b70c8ca07f6ace42..5978ac38ad062b9b346cad4da09f9c561d9510cc 100644 (file)
@@ -196,7 +196,7 @@ bool ImageLoaderMegaDylib::hasDylib(const char* path, unsigned* index) const
 {
        const uint8_t* imageNode = ImageLoader::trieWalk(_dylibsTrieStart, _dylibsTrieEnd, path);
        if ( imageNode == NULL ) {
-  #if __MAC_OS_X_VERSION_MIN_REQUIRED
+  #if TARGET_OS_OSX
                // not all symlinks are recorded as aliases in accelerator tables
                if ( (strncmp(path, "/usr/lib/", 9) == 0) || (strncmp(path, "/System/Library/", 16) == 0) ) {
                        char resolvedPath[PATH_MAX];
@@ -573,7 +573,7 @@ void ImageLoaderMegaDylib::recursiveLoadLibraries(const LinkContext& context, bo
        recursiveMarkLoaded(context, index);
 }
 
-unsigned int ImageLoaderMegaDylib::recursiveUpdateDepth(unsigned int maxDepth)
+unsigned int ImageLoaderMegaDylib::updateDepth(unsigned int maxDepth)
 {
        setDepth(maxDepth);
        return maxDepth;
@@ -946,7 +946,7 @@ void ImageLoaderMegaDylib::recursiveInitialization(const LinkContext& context, m
        }
 }
 
-void ImageLoaderMegaDylib::recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload)
+void ImageLoaderMegaDylib::recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload, const ImageLoader* parent)
 {
        markAllbound(context);
 }
index 081e95b1be5a398d7ca3e352535d027d5855b8f5..56df1c40642eeff12b730f314ac5172e45f895a8 100644 (file)
@@ -162,9 +162,9 @@ protected:
 #endif
 
        virtual void                                            recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath);
-       virtual unsigned                                        recursiveUpdateDepth(unsigned int maxDepth);
+       virtual unsigned                                        updateDepth(unsigned int maxDepth);
        virtual void                                            recursiveRebase(const LinkContext& context) {  }
-       virtual void                                            recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload);
+       virtual void                                            recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload, const ImageLoader* parent);
        virtual void                                            recursiveApplyInterposing(const LinkContext& context);
        virtual void                                            recursiveMakeDataReadOnly(const LinkContext& context) {}
        virtual void                                            recursiveGetDOFSections(const LinkContext& context, std::vector<DOFInfo>& dofs) { }
@@ -174,7 +174,7 @@ protected:
        virtual void                                            doGetDependentLibraries(DependentLibraryInfo libs[]) { unreachable(); }
        virtual LibraryInfo                                     doGetLibraryInfo(const LibraryInfo& requestorInfo) { return requestorInfo; }
        virtual void                                            doRebase(const LinkContext& context) { unreachable(); }
-       virtual void                                            doBind(const LinkContext& context, bool forceLazysBound) { unreachable(); }
+       virtual void                                            doBind(const LinkContext& context, bool forceLazysBound, const ImageLoader* reExportParent) { unreachable(); }
        virtual void                                            doBindJustLazies(const LinkContext& context) { unreachable(); }
        virtual void                                            doGetDOFSections(const LinkContext& context, std::vector<DOFInfo>& dofs) { unreachable(); }
        virtual void                                            doInterpose(const LinkContext& context) { unreachable(); }
index 68585b21b1de5361a0c1940fe842f250c1c0214f..65caa13f0cb78139084549c3396ef60ddc6ee19a 100644 (file)
@@ -41,6 +41,7 @@
 #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 <sys/fsgetpath.h>
 #include <System/sys/content_protection.h>
 
+#define SUPPORT_LOGGING_TO_CONSOLE !(__i386__ || __x86_64__ || TARGET_OS_SIMULATOR)
+#if SUPPORT_LOGGING_TO_CONSOLE
+#include <paths.h> // for logging to console
+#endif
+
+#if !TARGET_OS_SIMULATOR
+
+// The comm page is being renamed, so set our define to the new value if the old
+// value is missing
+#ifndef _COMM_PAGE_DYLD_SYSTEM_FLAGS
+
+#ifndef _COMM_PAGE_DYLD_FLAGS
+#error Must define _COMM_PAGE_DYLD_FLAGS or _COMM_PAGE_DYLD_SYSTEM_FLAGS
+#endif
+
+#define _COMM_PAGE_DYLD_SYSTEM_FLAGS _COMM_PAGE_DYLD_FLAGS
+
+#endif
+
+#endif
+
 #if TARGET_OS_SIMULATOR
        enum {
                AMFI_DYLD_INPUT_PROC_IN_SIMULATOR = (1 << 0),
@@ -124,6 +146,8 @@ extern "C" int __fork();
 #include "ClosureFileSystemPhysical.h"
 #include "FileUtils.h"
 #include "BootArgs.h"
+#include "Defines.h"
+#include "RootsChecker.h"
 
 #ifndef MH_HAS_OBJC
   #define MH_HAS_OBJC                  0x40000000
@@ -150,7 +174,7 @@ extern "C" ssize_t __sendto(int, const void *, size_t, int, const struct sockadd
        #define macho_section                   section
 #endif
 
-
+#define DYLD_CLOSURE_XATTR_NAME "com.apple.dyld"
 
 #define CPU_TYPE_MASK 0x00FFFFFF       /* complement of CPU_ARCH_MASK */
 
@@ -158,6 +182,7 @@ extern "C" ssize_t __sendto(int, const void *, size_t, int, const struct sockadd
 /* implemented in dyld_gdb.cpp */
 extern void resetAllImages();
 extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]);
+extern void addAotImagesToAllAotImages(uint32_t aotInfoCount, const dyld_aot_image_info aotInfo[]);
 extern void removeImageFromAllImages(const mach_header* mh);
 extern void addNonSharedCacheImageUUID(const dyld_uuid_info& info);
 extern const char* notifyGDB(enum dyld_image_states state, uint32_t infoCount, const dyld_image_info info[]);
@@ -271,7 +296,7 @@ static void*                                                sSingleHandlers[7][3];
 static void*                                           sBatchHandlers[7][3];
 static ImageLoader*                                    sLastImageByAddressCache;
 static EnvironmentVariables                    sEnv;
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
 static const char*                                     sFrameworkFallbackPaths[] = { "$HOME/Library/Frameworks", "/Library/Frameworks", "/Network/Library/Frameworks", "/System/Library/Frameworks", NULL };
 static const char*                                     sLibraryFallbackPaths[] = { "$HOME/lib", "/usr/local/lib", "/usr/lib", NULL };
 static const char*                                     sRestrictedFrameworkFallbackPaths[] = { "/System/Library/Frameworks", NULL };
@@ -315,7 +340,7 @@ static _dyld_objc_notify_mapped             sNotifyObjCMapped;
 static _dyld_objc_notify_init          sNotifyObjCInit;
 static _dyld_objc_notify_unmapped      sNotifyObjCUnmapped;
 
-#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR
+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
 static bool                                                    sForceStderr = false;
 #endif
 
@@ -327,10 +352,17 @@ static bool                                                       sDisableAcceleratorTables = true;
 #endif
 
 bool                                                           gUseDyld3 = false;
+static uint32_t                                                sLaunchModeUsed = 0;
 static bool                                                    sSkipMain = false;
-static void                       (*sEntryOveride)() = nullptr;
+static void                       (*sEntryOverride)() = nullptr;
 static bool                                                    sJustBuildClosure = false;
+#if !TARGET_OS_SIMULATOR
 static bool                                                    sLogClosureFailure = false;
+#endif
+static bool                                            sKeysDisabled = false;
+static bool                                                    sOnlyPlatformArm64e = false; // arm64e binaries can only be loaded if they are part of the OS
+
+static dyld3::RootsChecker                     sRootsChecker;
 
 enum class ClosureMode {
        // Unset means we haven't provided an env variable or boot-arg to explicitly choose a mode
@@ -342,13 +374,39 @@ enum class ClosureMode {
        // -force_dyld2=1 env variable or an internal cache on iOS
        Off,
        // PreBuiltOnly means only use a shared cache closure and don't try build a new one
-       PreBuiltOnly
+       PreBuiltOnly,
+};
+
+enum class ClosureKind {
+       unset,
+       full,
+       minimal,
 };
 
 static ClosureMode                                     sClosureMode = ClosureMode::Unset;
+static ClosureKind                                     sClosureKind = ClosureKind::unset;
 static bool                                                    sForceInvalidSharedCacheClosureFormat = false;
 static uint64_t                                                launchTraceID = 0;
 
+// These flags are the values in the 64-bit _COMM_PAGE_DYLD_SYSTEM_FLAGS entry
+// Note we own this and can write it from PID 1
+enum CommPageFlags : uint64_t {
+    None                                = 0,
+
+       // The boot args can set the low 32-bits of the comm page.  We'll reserve the high 32-bits
+       // for runtime (launchd) set values.
+       CommPageBootArgMask                                     = 0xFFFFFFFF,
+
+       // Are the simulator support dylibs definitely roots when launchd scanned them
+       libsystemKernelIsRoot                           = 1ULL << 32,
+       libsystemPlatformIsRoot                         = 1ULL << 33,
+       libsystemPThreadIsRoot                          = 1ULL << 34,
+
+       // Is the file system writable, ie, could the simulator support dylibs be written
+       // later, after PID 1
+       fileSystemCanBeModified                         = 1ULL << 35
+};
+
 //
 // The MappedRanges structure is used for fast address->image lookups.
 // The table is only updated when the dyld lock is held, so we don't
@@ -546,7 +604,7 @@ static void socket_syslogv(int priority, const char* format, va_list list)
 
 void vlog(const char* format, va_list list)
 {
-#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR
+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
        // <rdar://problem/25965832> log to console when running iOS app from Xcode
        if ( !sLogToFile && !sForceStderr && useSyslog() )
 #else
@@ -581,6 +639,21 @@ void warn(const char* format, ...)
        va_end(list);
 }
 
+void logToConsole(const char* format, ...) {
+#if SUPPORT_LOGGING_TO_CONSOLE
+    int cfd = open(_PATH_CONSOLE, O_WRONLY|O_NOCTTY);
+    if (cfd == -1)
+        return;
+
+    va_list list;
+    va_start(list, format);
+    _simple_vdprintf(cfd, format, list);
+    va_end(list);
+
+    close(cfd);
+#endif
+}
+
 #else
        extern void vlog(const char* format, va_list list);
 #endif // !TARGET_OS_SIMULATOR 
@@ -618,7 +691,7 @@ private:
 FileOpener::FileOpener(const char* path)
  : fd(-1)
 {
-       fd = my_open(path, O_RDONLY, 0);
+       fd = dyld3::open(path, O_RDONLY, 0);
 }
 
 FileOpener::~FileOpener()
@@ -675,7 +748,7 @@ static void registerDOFs(const std::vector<ImageLoader::DOFInfo>& dofs)
 
 static void    unregisterDOF(int registrationID)
 {
-       int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR);
+       int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR, 0);
        if ( fd < 0 ) {
                dyld::warn("can't open /dev/" DTRACEMNR_HELPER " to unregister dtrace DOF section\n");
        }
@@ -835,8 +908,11 @@ static void notifyMonitoringDyld(bool unloading, unsigned imageCount, const stru
                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 ) {
+
+               unsigned totalSize = (sizeof(struct dyld_process_info_notify_header) + entriesSize + pathsSize + 127) & -128;   // align
+               // The reciever has a fixed buffer of DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE, whcih needs to hold both the message and a trailer.
+               // If the total size exceeds that we need to fragment the message.
+               if ( (totalSize + MAX_TRAILER_SIZE) > 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;
@@ -867,9 +943,9 @@ static void notifyMonitoringDyld(bool unloading, unsigned imageCount, const stru
                        ++entries;
                }
                if (unloading) {
-                       sendMessage(slot, DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID, totalSize, (mach_msg_header_t*)buffer, totalSize+MAX_TRAILER_SIZE);
+                       sendMessage(slot, DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID, totalSize, (mach_msg_header_t*)buffer, totalSize);
                } else {
-                       sendMessage(slot, DYLD_PROCESS_INFO_NOTIFY_LOAD_ID, totalSize, (mach_msg_header_t*)buffer, totalSize+MAX_TRAILER_SIZE);
+                       sendMessage(slot, DYLD_PROCESS_INFO_NOTIFY_LOAD_ID, totalSize, (mach_msg_header_t*)buffer, totalSize);
                }
        }
 }
@@ -927,7 +1003,9 @@ static void notifySingle(dyld_image_states state, const ImageLoader* image, Imag
        }
        if ( state == dyld_image_state_mapped ) {
                // <rdar://problem/7008875> Save load addr + UUID for images from outside the shared cache
-               if ( !image->inSharedCache() ) {
+               // <rdar://problem/50432671> Include UUIDs for shared cache dylibs in all image info when using private mapped shared caches
+               if (!image->inSharedCache()
+                       || (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion)) {
                        dyld_uuid_info info;
                        if ( image->getUUID(info.imageUUID) ) {
                                info.imageLoadAddress = image->machHeader();
@@ -1181,7 +1259,7 @@ static void notifyBatch(dyld_image_states state, bool preflightOnly)
        notifyBatchPartial(state, false, NULL, preflightOnly, false);
 }
 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
 static
 void coresymbolication_load_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh)
 {
@@ -1477,7 +1555,7 @@ void removeImage(ImageLoader* image)
        if ( gLinkContext.weakDefMapInitialized && image->hasCoalescedExports() && (image->getState() >= dyld_image_state_bound) ) {
                Diagnostics diag;
                const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)image->machHeader();
-               ma->forEachWeakDef(diag, ^(const char *symbolName, uintptr_t imageOffset, bool isFromExportTrie) {
+               ma->forEachWeakDef(diag, ^(const char *symbolName, uint64_t imageOffset, bool isFromExportTrie) {
                        auto it = gLinkContext.weakDefMap.find(symbolName);
                        assert(it != gLinkContext.weakDefMap.end());
                        it->second = { nullptr, 0 };
@@ -1849,7 +1927,7 @@ static void       appendParsedColonList(const char* list, const char* mainExecutableDi
        }
 }
 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
 static void paths_expand_roots(const char **paths, const char *key, const char *val)
 {
 //     assert(val != NULL);
@@ -1998,13 +2076,13 @@ void processDyldEnvironmentVariable(const char* key, const char* value, const ch
        }
        else if ( strcmp(key, "DYLD_PRINT_STATISTICS") == 0 ) {
                sEnv.DYLD_PRINT_STATISTICS = true;
-#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR
+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
                // <rdar://problem/26614838> DYLD_PRINT_STATISTICS no longer logs to xcode console for device apps
                sForceStderr = true;
 #endif
        }
        else if ( strcmp(key, "DYLD_PRINT_TO_STDERR") == 0 ) {
-#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR
+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
                // <rdar://problem/26633440> DYLD_PRINT_STATISTICS no longer logs to xcode console for device apps
                sForceStderr = true;
 #endif
@@ -2107,7 +2185,7 @@ void processDyldEnvironmentVariable(const char* key, const char* value, const ch
 #endif
 #if !TARGET_OS_SIMULATOR
        else if ( (strcmp(key, "DYLD_PRINT_TO_FILE") == 0) && (mainExecutableDir == NULL) && gLinkContext.allowEnvVarsSharedCache ) {
-               int fd = open(value, O_WRONLY | O_CREAT | O_APPEND, 0644);
+               int fd = dyld3::open(value, O_WRONLY | O_CREAT | O_APPEND, 0644);
                if ( fd != -1 ) {
                        sLogfile = fd;
                        sLogToFile = true;
@@ -2220,7 +2298,7 @@ static void checkVersionedPaths()
 #endif 
 
 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
 //
 // For security, setuid programs ignore DYLD_* environment variables.
 // Additionally, the DYLD_* enviroment variables are removed
@@ -2272,7 +2350,7 @@ static void pruneEnvironmentVariables(const char* envp[], const char*** applep)
 
 static void defaultUninitializedFallbackPaths(const char* envp[])
 {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
        if ( !gLinkContext.allowClassicFallbackPaths ) {
                sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = sRestrictedFrameworkFallbackPaths;
                sEnv.DYLD_FALLBACK_LIBRARY_PATH   = sRestrictedLibraryFallbackPaths;
@@ -2440,7 +2518,7 @@ static void getHostInfo(const macho_header* mainExecutableMH, uintptr_t mainExec
 
 static void checkSharedRegionDisable(const dyld3::MachOLoaded* mainExecutableMH, uintptr_t mainExecutableSlide)
 {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
        // if main executable has segments that overlap the shared region,
        // then disable using the shared region
        if ( mainExecutableMH->intersectsRange(SHARED_REGION_BASE, SHARED_REGION_SIZE) ) {
@@ -2454,6 +2532,9 @@ static void checkSharedRegionDisable(const dyld3::MachOLoaded* mainExecutableMH,
                gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion;
        }
 #endif
+#endif
+#if TARGET_OS_SIMULATOR
+       gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion;
 #endif
        // iOS cannot run without shared region
 }
@@ -2655,34 +2736,29 @@ static const cpu_subtype_t kARM[kARM_RowCount][9] = {
 };
 #endif
 
-#if __arm64__
+#if __ARM64_ARCH_8_32__
 //
-//     ARM64 sub-type lists
+//    arm64_32 sub-type lists
 //
-const int kARM64_RowCount = 2;
-static const cpu_subtype_t kARM64[kARM64_RowCount][4] = {
-
-       // armv64e can run: 64e, 64
-       {  CPU_SUBTYPE_ARM64E, CPU_SUBTYPE_ARM64_V8, CPU_SUBTYPE_ARM64_ALL, CPU_SUBTYPE_END_OF_LIST },
-
-       // armv64 can run: 64
-       {  CPU_SUBTYPE_ARM64_V8, CPU_SUBTYPE_ARM64_ALL, CPU_SUBTYPE_END_OF_LIST },
-};
-
-#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 },
-};
+       static const cpu_subtype_t kARM64_32[] = { CPU_SUBTYPE_ARM64_32_V8, CPU_SUBTYPE_END_OF_LIST };
 #endif
-       
+
+#if __arm64__ && __LP64__
+//
+//     arm64[e] sub-type handing
+//
+       #if __arm64e__
+               // arm64e with keys on
+               static const cpu_subtype_t kARM64e[]        = { CPU_SUBTYPE_ARM64E, CPU_SUBTYPE_END_OF_LIST };
+               // arm64 or arm64e with keys off
+               static const cpu_subtype_t kARM64eKeysOff[] = { CPU_SUBTYPE_ARM64E, CPU_SUBTYPE_ARM64_V8, CPU_SUBTYPE_ARM64_ALL, CPU_SUBTYPE_END_OF_LIST };
+       #else
+               // arm64 main binary
+               static const cpu_subtype_t kARM64[] = { CPU_SUBTYPE_ARM64_V8, CPU_SUBTYPE_ARM64_ALL, CPU_SUBTYPE_END_OF_LIST };
+       #endif // __arm64e__
 #endif
 
+
 #if __x86_64__
 //      
 //     x86_64 sub-type lists
@@ -2713,21 +2789,20 @@ static const cpu_subtype_t* findCPUSubtypeList(cpu_type_t cpu, cpu_subtype_t sub
                        break;
 #endif
 #if __arm64__
+       #if __LP64__
                case CPU_TYPE_ARM64:
-                       for (int i=0; i < kARM64_RowCount ; ++i) {
-                               if ( kARM64[i][0] == subtype )
-                                       return kARM64[i];
-                       }
+               #if __arm64e__
+                       return ( sKeysDisabled ? kARM64eKeysOff : kARM64e);
+               #else
+                       return kARM64;
+               #endif
                        break;
+       #endif
 
-#if __ARM64_ARCH_8_32__
+       #if !__LP64__
                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
+                       return kARM64_32;
+       #endif
 
 #endif
 #if __x86_64__
@@ -2743,18 +2818,26 @@ static const cpu_subtype_t* findCPUSubtypeList(cpu_type_t cpu, cpu_subtype_t sub
 }
 
 
-
-
 // scan fat table-of-contents for best most preferred subtype
-static bool fatFindBestFromOrderedList(cpu_type_t cpu, const cpu_subtype_t list[], const fat_header* fh, uint64_t* offset, uint64_t* len)
+static bool fatFindBestFromOrderedList(cpu_type_t cpu, const cpu_subtype_t list[], const fat_header* fh, int fd, uint64_t* offset, uint64_t* len)
 {
        const fat_arch* const archs = (fat_arch*)(((char*)fh)+sizeof(fat_header));
        for (uint32_t subTypeIndex=0; list[subTypeIndex] != CPU_SUBTYPE_END_OF_LIST; ++subTypeIndex) {
                for(uint32_t fatIndex=0; fatIndex < OSSwapBigToHostInt32(fh->nfat_arch); ++fatIndex) {
-                       if ( ((cpu_type_t)OSSwapBigToHostInt32(archs[fatIndex].cputype) == cpu) 
-                               && (list[subTypeIndex] == (cpu_subtype_t)OSSwapBigToHostInt32(archs[fatIndex].cpusubtype)) ) {
-                               *offset = OSSwapBigToHostInt32(archs[fatIndex].offset);
-                               *len = OSSwapBigToHostInt32(archs[fatIndex].size);
+                       cpu_type_t    sliceCpuType    = OSSwapBigToHostInt32(archs[fatIndex].cputype);
+                       cpu_subtype_t sliceCpuSubType = OSSwapBigToHostInt32(archs[fatIndex].cpusubtype) & ~CPU_SUBTYPE_MASK;
+                       uint64_t      sliceOffset     = OSSwapBigToHostInt32(archs[fatIndex].offset);
+                       uint64_t      sliceLen        = OSSwapBigToHostInt32(archs[fatIndex].size);
+                       if ( (sliceCpuType == cpu) && ((list[subTypeIndex] & ~CPU_SUBTYPE_MASK) == sliceCpuSubType) ) {
+#if TARGET_OS_OSX && __has_feature(ptrauth_calls)
+                               if ( sOnlyPlatformArm64e && (sliceCpuType == CPU_TYPE_ARM64) && (sliceCpuSubType == CPU_SUBTYPE_ARM64E) ) {
+                                       // if we can only load arm64e slices that are platform binaries, skip over slices that are not
+                                       if ( !dyld3::MachOAnalyzer::sliceIsOSBinary(fd, sliceOffset, sliceLen) )
+                                               continue;
+                               }
+#endif
+                               *offset = sliceOffset;
+                               *len    = sliceLen;
                                return true;
                        }
                }
@@ -2762,6 +2845,7 @@ static bool fatFindBestFromOrderedList(cpu_type_t cpu, const cpu_subtype_t list[
        return false;
 }
 
+#if !TARGET_OS_OSX || !__has_feature(ptrauth_calls)
 // scan fat table-of-contents for exact match of cpu and cpu-sub-type
 static bool fatFindExactMatch(cpu_type_t cpu, cpu_subtype_t subtype, const fat_header* fh, uint64_t* offset, uint64_t* len)
 {
@@ -2776,6 +2860,7 @@ static bool fatFindExactMatch(cpu_type_t cpu, cpu_subtype_t subtype, const fat_h
        }
        return false;
 }
+#endif
 
 // scan fat table-of-contents for image with matching cpu-type and runs-on-all-sub-types
 static bool fatFindRunsOnAllCPUs(cpu_type_t cpu, const fat_header* fh, uint64_t* offset, uint64_t* len)
@@ -2884,7 +2969,7 @@ static bool fatValidate(const fat_header* fh)
 // each optimized for a different cpu-sub-type (e.g G3 or G5).
 // This routine picks the optimal sub-image.
 //
-static bool fatFindBest(const fat_header* fh, uint64_t* offset, uint64_t* len)
+static bool fatFindBest(const fat_header* fh, uint64_t* offset, uint64_t* len, int fd=-1)
 {
        if ( !fatValidate(fh) )
                return false;
@@ -2900,13 +2985,17 @@ static bool fatFindBest(const fat_header* fh, uint64_t* offset, uint64_t* len)
        
                // use ordered list to find best sub-image in fat file
                if ( subTypePreferenceList != NULL ) {
-                       if ( fatFindBestFromOrderedList(cpu, subTypePreferenceList, fh, offset, len) )
+                       if ( fatFindBestFromOrderedList(cpu, subTypePreferenceList, fh, fd, offset, len) )
                                return true;
                }
-
+#if TARGET_OS_OSX && __has_feature(ptrauth_calls)
+               // don't use fallbacks for macOS arm64e to ensure only compatible binaries are loaded
+               return false;
+#else
                // if running cpu is not in list, try for an exact match
                if ( fatFindExactMatch(cpu, sHostCPUsubtype, fh, offset, len) )
                        return true;
+#endif
        }
        
        // running on an uknown cpu, can only load generic code
@@ -2925,12 +3014,21 @@ static bool fatFindBest(const fat_header* fh, uint64_t* offset, uint64_t* len)
 #endif
 }
 
+#if defined(__x86_64__) && !TARGET_OS_SIMULATOR
+#ifndef kIsTranslated
+   #define kIsTranslated  0x4000000000000000ULL
+#endif
+bool isTranslated()
+{
+       return ((*(uint64_t*)_COMM_PAGE_CPU_CAPABILITIES64) & kIsTranslated);
+}
+#endif
 
 
 //
 // This is used to validate if a non-fat (aka thin or raw) mach-o file can be used
 // on the current processor. //
-bool isCompatibleMachO(const uint8_t* firstPage, const char* path)
+bool isCompatibleMachO(const uint8_t* firstPage, const char* path, int fd=-1, uint64_t sliceOffset=0, uint64_t sliceLen=-1)
 {
 #if CPU_SUBTYPES_SUPPORTED
        // It is deemed compatible if any of the following are true:
@@ -2941,22 +3039,31 @@ bool isCompatibleMachO(const uint8_t* firstPage, const char* path)
        if ( mh->magic == sMainExecutableMachHeader->magic ) {
                if ( mh->cputype == sMainExecutableMachHeader->cputype ) {
                        if ( mh->cputype == sHostCPU ) {
+                               const cpu_subtype_t mhCPUSubtype = mh->cpusubtype & ~CPU_SUBTYPE_MASK;
                                // get preference ordered list of subtypes that this machine can use
                                const cpu_subtype_t* subTypePreferenceList = findCPUSubtypeList(mh->cputype, sHostCPUsubtype);
                                if ( subTypePreferenceList != NULL ) {
                                        // if image's subtype is in the list, it is compatible
                                        for (const cpu_subtype_t* p = subTypePreferenceList; *p != CPU_SUBTYPE_END_OF_LIST; ++p) {
-                                               if ( *p == mh->cpusubtype )
+                                               if ( *p == mhCPUSubtype ) {
+       #if TARGET_OS_OSX && __has_feature(ptrauth_calls)
+                                                       if ( mhCPUSubtype == CPU_SUBTYPE_ARM64E ) {
+                                                               if ( !sOnlyPlatformArm64e || dyld3::MachOAnalyzer::sliceIsOSBinary(fd, sliceOffset, sliceLen) )
+                                                                       return true;
+                                                       }
+                                                       else
+       #endif
                                                        return true;
+                                               }
                                        }
                                        // have list and not in list, so not compatible
-                                       throwf("incompatible cpu-subtype: 0x%08X in %s", mh->cpusubtype, path);
+                                       throwf("incompatible cpu-subtype: 0x%08X in %s", mhCPUSubtype, path);
                                }
                                // unknown cpu sub-type, but if exact match for current subtype then ok to use
-                               if ( mh->cpusubtype == sHostCPUsubtype ) 
+                               if ( mhCPUSubtype == sHostCPUsubtype )
                                        return true;
                        }
-                       
+
                        // cpu type has no ordered list of subtypes
                        switch (mh->cputype) {
                                case CPU_TYPE_I386:
@@ -2987,13 +3094,13 @@ bool isCompatibleMachO(const uint8_t* firstPage, const char* path)
 static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, uintptr_t slide, const char* path)
 {
        // try mach-o loader
-       if ( isCompatibleMachO((const uint8_t*)mh, path) ) {
+//     if ( isCompatibleMachO((const uint8_t*)mh, path) ) {
                ImageLoader* image = ImageLoaderMachO::instantiateMainExecutable(mh, slide, path, gLinkContext);
                addImage(image);
                return (ImageLoaderMachO*)image;
-       }
+//     }
        
-       throw "main executable not a known format";
+//     throw "main executable not a known format";
 }
 
 #if SUPPORT_ACCELERATE_TABLES
@@ -3111,7 +3218,7 @@ static bool isSimulatorBinary(const uint8_t* firstPages, const char* path)
                                    (strcmp(path, "/usr/lib/system/libsystem_pthread_debug.dylib") == 0) ||
                                    (strcmp(path, "/sbin/launchd_sim_trampoline") == 0) ||
                                    (strcmp(path, "/usr/sbin/iokitsimd") == 0) ||
-                                   (strcmp(path, "/usr/lib/system/host/liblaunch_sim.dylib") == 0))
+                                       (strcmp(path, "/usr/lib/system/host/liblaunch_sim.dylib") == 0))
                                        return true;
                                return false;
                        case LC_BUILD_VERSION:
@@ -3124,10 +3231,6 @@ static bool isSimulatorBinary(const uint8_t* firstPages, const char* path)
                                        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) ||
@@ -3149,31 +3252,6 @@ static bool isSimulatorBinary(const uint8_t* firstPages, const char* path)
 }
 #endif
 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-static bool iOSonMacDenied(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-deny-list.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)
@@ -3187,7 +3265,6 @@ 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
@@ -3207,7 +3284,7 @@ static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char*
        if ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
                if ( OSSwapBigToHostInt32(fileStartAsFat->nfat_arch) > ((4096 - sizeof(fat_header)) / sizeof(fat_arch)) )
                        throwf("fat header too large: %u entries", OSSwapBigToHostInt32(fileStartAsFat->nfat_arch));
-               if ( fatFindBest(fileStartAsFat, &fileOffset, &fileLength) ) {
+               if ( fatFindBest(fileStartAsFat, &fileOffset, &fileLength, fd) ) {
                        if ( (fileOffset+fileLength) > (uint64_t)(stat_buf.st_size) )
                                throwf("truncated fat file.  file length=%llu, but needed slice goes to %llu", stat_buf.st_size, fileOffset+fileLength);
                        if (pread(fd, firstPages, 4096, fileOffset) != 4096)
@@ -3222,7 +3299,7 @@ static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char*
        if ( shortPage ) 
                throw "file too short";
 
-       if ( isCompatibleMachO(firstPages, path) ) {
+       if ( isCompatibleMachO(firstPages, path, fd, fileOffset, fileLength) ) {
 
                // only MH_BUNDLE, MH_DYLIB, and some MH_EXECUTE can be dynamically loaded
                const mach_header* mh = (mach_header*)firstPages;
@@ -3236,60 +3313,45 @@ static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char*
                }
 
                uint32_t headerAndLoadCommandsSize = sizeof(macho_header) + mh->sizeofcmds;
-               if ( headerAndLoadCommandsSize > MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE )
-                       throwf("malformed mach-o: load commands size (%u) > %u", headerAndLoadCommandsSize, MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE);
-
                if ( headerAndLoadCommandsSize > fileLength )
                        dyld::throwf("malformed mach-o: load commands size (%u) > mach-o file size (%llu)", headerAndLoadCommandsSize, fileLength);
 
-               if ( headerAndLoadCommandsSize > 4096 ) {
+               vm_address_t vmAllocatedFirstPages = 0;
+               if ( headerAndLoadCommandsSize > MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE ) {
+                       if ( ::vm_allocate(mach_task_self(), &vmAllocatedFirstPages, headerAndLoadCommandsSize, VM_FLAGS_ANYWHERE) == 0 ) {
+                               if ( ::pread(fd, (void*)vmAllocatedFirstPages, headerAndLoadCommandsSize, fileOffset) != headerAndLoadCommandsSize )
+                                       throwf("pread of all load commands failed: %d", errno);
+                               mh = (mach_header*)vmAllocatedFirstPages;
+                       }
+                       else {
+                               throwf("malformed mach-o: load commands size (%u) > %u", headerAndLoadCommandsSize, MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE);
+                       }
+               }
+               else if ( headerAndLoadCommandsSize > 4096 ) {
                        // read more pages
                        unsigned readAmount = headerAndLoadCommandsSize - 4096;
                        if ( pread(fd, &firstPages[4096], readAmount, fileOffset+4096) != readAmount )
                                throwf("pread of extra load commands past 4KB failed: %d", errno);
                }
 
-#if TARGET_OS_SIMULATOR        
-               // <rdar://problem/14168872> dyld_sim should restrict loading osx binaries
-               if ( !isSimulatorBinary(firstPages, path) ) {
-       #if TARGET_OS_WATCH
-                       throw "mach-o, but not built for watchOS simulator";
-       #elif TARGET_OS_TV
-                       throw "mach-o, but not built for tvOS simulator";
-       #else
-                       throw "mach-o, but not built for iOS simulator";
-       #endif
-               }
-#endif
-
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-               if ( gLinkContext.iOSonMac ) {
-                       const dyld3::MachOFile* mf = (dyld3::MachOFile*)firstPages;
-                       bool supportsiOSMac = mf->supportsPlatform(dyld3::Platform::iOSMac);
-                       if ( !supportsiOSMac && iOSonMacDenied(path) ) {
-                               throw "mach-o, but not built for UIKitForMac";
-                       }
-               }
-               else if ( gLinkContext.driverKit ) {
-                       const dyld3::MachOFile* mf = (dyld3::MachOFile*)firstPages;
-                       bool isDriverKitDylib = mf->supportsPlatform(dyld3::Platform::driverKit);
-                       if ( !isDriverKitDylib ) {
-                               throw "mach-o, but not built for driverkit";
-                       }
+               if ( !((dyld3::MachOFile*)mh)->loadableIntoProcess((dyld3::Platform)gProcessInfo->platform, path) ) {
+                       throwf("mach-o, but not built for platform %s", dyld3::MachOFile::platformName((dyld3::Platform)gProcessInfo->platform));
                }
-#endif
 
-#if __arm64e__
-               if ( (sMainExecutableMachHeader->cpusubtype == CPU_SUBTYPE_ARM64E) && (mh->cpusubtype != CPU_SUBTYPE_ARM64E) )
+#if __has_feature(ptrauth_calls)
+               if ( !sKeysDisabled && ((sMainExecutableMachHeader->cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) && ((mh->cpusubtype & ~CPU_SUBTYPE_MASK) != CPU_SUBTYPE_ARM64E) )
                        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);
+                       image = ImageLoaderMachO::instantiateFromFile(path, fd, (uint8_t*)mh, headerAndLoadCommandsSize, fileOffset, fileLength, stat_buf, gLinkContext);
                        timer.setData4((uint64_t)image->machHeader());
                }
-               
+
+               if ( vmAllocatedFirstPages != 0 )
+                       ::vm_deallocate(mach_task_self(), (vm_address_t)vmAllocatedFirstPages, headerAndLoadCommandsSize);
+
                // validate
                return checkandAddImage(image, context);
        }
@@ -3356,6 +3418,8 @@ static bool isFileRelativePath(const char* path)
        return false;
 }
 
+static ImageLoader* loadPhase5check(const char* path, const char* orgPath, const LoadContext& context);
+
 
 // try to open file
 static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector<const char*>* exceptions)
@@ -3391,13 +3455,6 @@ static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const
 #else
 
        auto findSharedCacheImage = ^() {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-               if ( gLinkContext.iOSonMac ) {
-                       // On iOSMac, we are also running with DYLD_ROOT_PATH set, but want to look up the orgPath
-                       if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, orgPath, &shareCacheResults) )
-                               return true;
-               }
-#endif
                return dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, path, &shareCacheResults);
        };
 
@@ -3413,7 +3470,7 @@ static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const
                // if RTLD_NOLOAD, do nothing if not already loaded
                if ( context.dontLoad ) {
                        // <rdar://33412890> possible that there is an override of cache
-                       if ( my_stat(path, &statBuf) == 0 ) {
+                       if ( dyld3::stat(path, &statBuf) == 0 ) {
                                ImageLoader* imageLoader = findLoadedImage(statBuf);
                                if ( imageLoader != NULL )
                                        return imageLoader;
@@ -3423,7 +3480,7 @@ static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const
                bool useCache = false;
                if ( shareCacheResults.image == nullptr ) {
                        // HACK to support old caches
-                       existsOnDisk = ( my_stat(path, &statBuf) == 0 );
+                       existsOnDisk = ( dyld3::stat(path, &statBuf) == 0 );
                        didStat = true;
                        statErrNo = errno;
                        useCache = !existsOnDisk;
@@ -3432,44 +3489,58 @@ static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const
                        // <rdar://problem/7014995> zero out stat buffer so mtime, etc are zero for items from the shared cache
                        bzero(&statBuf, sizeof(statBuf));
                        if ( shareCacheResults.image->overridableDylib() ) {
-                               existsOnDisk = ( my_stat(path, &statBuf) == 0 );
-                               didStat = true;
+                               existsOnDisk = ( dyld3::stat(path, &statBuf) == 0 );
                                statErrNo = errno;
                                if ( sSharedCacheLoadInfo.loadAddress->header.dylibsExpectedOnDisk ) {
+                                       // old style macOS with dylibs on disk
                                        uint64_t expectedINode;
                                        uint64_t expectedMtime;
                                        if ( shareCacheResults.image->hasFileModTimeAndInode(expectedINode, expectedMtime) ) {
+                                               // if dylib found has same inode/mtime as one in cache, use one in cache
                                                if ( (expectedMtime == statBuf.st_mtime) && (expectedINode == statBuf.st_ino) )
                                                        useCache = true;
                                        }
                                }
                                else {
-                                       if ( !existsOnDisk )
+                                       // MRM style where dylibs are not on disk
+                                       if ( !existsOnDisk ) {
+                                               // looking at path where dylib should be, and we expect it to not be there but rather in the cache
+                                               // Its possible we are looking at a deleted symlink path.  For example, we are trying to open .../AppKit but
+                                               // there's already a loaded root of .../Versions/C/AppKit.  That used to work when the symlink was on-disk as
+                                               // we'd realpath to find the shared cache path.  Now we record the aliases in the cache and delete the symlinks.
+                                               const char* pathInSharedCache = shareCacheResults.image->path();
+                                               if ( strcmp(path, pathInSharedCache) != 0 ) {
+                                                       ImageLoader* imageLoader = loadPhase5check(pathInSharedCache, orgPath, context);
+                                                       if ( imageLoader != NULL )
+                                                               return imageLoader;
+                                               }
+                                               useCache = true;
+                                       }
+                                       else if ( !sRootsChecker.onDiskFileIsRoot(path, sSharedCacheLoadInfo.loadAddress,
+                                                                                                                         shareCacheResults.image, nullptr, statBuf.st_ino, statBuf.st_mtime) ) {
+                                               // we found a file on disk, at the same path as the dyld cache has a dylib and it is one of the magic three
                                                useCache = true;
+                                       }
                                }
                        }
                        else {
+                               // we are trying to override a dylib in the cache that does not allow overrides, ignore override and use cache
                                useCache = true;
                        }
                }
                if ( useCache ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-                       if ( gLinkContext.iOSonMac ) {
-                               const dyld3::MachOFile* mf = (dyld3::MachOFile*)shareCacheResults.mhInCache;
-                               bool supportsiOSMac = mf->supportsPlatform(dyld3::Platform::iOSMac);
-                               if ( !supportsiOSMac && iOSonMacDenied(path) ) {
-                                       throw "mach-o, but not built for UIKitForMac";
-                               }
-                       }
-#endif
-                       ImageLoader* imageLoader = ImageLoaderMachO::instantiateFromCache((macho_header*)shareCacheResults.mhInCache, shareCacheResults.pathInCache, shareCacheResults.slideInCache, statBuf, gLinkContext);
+                       const dyld3::MachOFile* cacheDylibMH = (dyld3::MachOFile*)shareCacheResults.mhInCache;
+                       if ( !cacheDylibMH->loadableIntoProcess((dyld3::Platform)gProcessInfo->platform, path) )
+                               throwf("mach-o, but not built for platform %s", dyld3::MachOFile::platformName((dyld3::Platform)gProcessInfo->platform));
+
+                       ImageLoader* imageLoader = ImageLoaderMachO::instantiateFromCache((macho_header*)cacheDylibMH, shareCacheResults.pathInCache, shareCacheResults.slideInCache, statBuf, gLinkContext);
                        return checkandAddImage(imageLoader, context);
                }
        }
 
        // not in cache or cache not usable
        if ( !didStat ) {
-               existsOnDisk = ( my_stat(path, &statBuf) == 0 );
+               existsOnDisk = ( dyld3::stat(path, &statBuf) == 0 );
                statErrNo = errno;
        }
        if ( existsOnDisk ) {
@@ -3748,8 +3819,18 @@ static ImageLoader* loadPhase2(const char* path, const char* orgPath, const Load
                                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) )
+                                       if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, path, &shareCacheResults) && (shareCacheResults.image != nullptr) ) {
+                                               image->setOverridesCachedDylib(shareCacheResults.image->imageNum());
+                                       }
+#if SUPPORT_ROOT_PATH
+                                       else if ( (gLinkContext.rootPaths != nullptr)
+                                                          && dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, orgPath, &shareCacheResults)
+                                                          && (shareCacheResults.image != nullptr) ) {
+                                               // DYLD_ROOT_PATH, ie, iOSMac, also needs to check if the original path is overridden
+                                               // as the root prefix has been applied to 'path', but the framework path searches without a root path prefix
                                                image->setOverridesCachedDylib(shareCacheResults.image->imageNum());
+                                       }
+#endif
                                        return image;
                                }
                        }
@@ -3772,11 +3853,21 @@ static ImageLoader* loadPhase2(const char* path, const char* orgPath, const Load
                                image = loadPhase2cache(libpath, orgPath, context, cacheIndex, exceptions);
                        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;
-                       }
+                           dyld3::SharedCacheFindDylibResults shareCacheResults;
+                           if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, path, &shareCacheResults) && (shareCacheResults.image != nullptr) ) {
+                                   image->setOverridesCachedDylib(shareCacheResults.image->imageNum());
+                           }
+#if SUPPORT_ROOT_PATH
+                               else if ( (gLinkContext.rootPaths != nullptr)
+                                                  && dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, orgPath, &shareCacheResults)
+                                                  && (shareCacheResults.image != nullptr) ) {
+                                   // DYLD_ROOT_PATH, ie, iOSMac, also needs to check if the original path is overridden
+                                   // as the root prefix has been applied to 'path', but the library path searches without a root path prefix
+                                   image->setOverridesCachedDylib(shareCacheResults.image->imageNum());
+                           }
+#endif
+                           return image;
+                  }
                }
        }
        return NULL;
@@ -3851,7 +3942,7 @@ 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
+#if TARGET_OS_OSX
        // handle macOS dylibs dlopen()ing versioned path which needs to map to flat path in mazipan simulator
        if ( gLinkContext.iOSonMac && strstr(path, ".framework/Versions/")) {
                uintptr_t sourceOffset = 0;
@@ -3949,6 +4040,11 @@ ImageLoader* load(const char* path, const LoadContext& context, unsigned& cacheI
                if ( !gSharedCacheOverridden && !image->inSharedCache() && image->isDylib() && dyld3::MachOFile::isSharedCacheEligiblePath(path) && inSharedCache(path) ) {
                        gSharedCacheOverridden = true;
                }
+               // <rdar://problem/59327556> if file loaded via symlink to a root of something in dyld cache, mark it as an override
+               dyld3::SharedCacheFindDylibResults shareCacheResults;
+               if ( !image->inSharedCache() && dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, image->getRealPath(), &shareCacheResults) && (shareCacheResults.image != nullptr) )
+                       image->setOverridesCachedDylib(shareCacheResults.image->imageNum());
+               
                return image;
        }
        else if ( exceptions.size() == 0 ) {
@@ -3979,19 +4075,20 @@ ImageLoader* load(const char* path, const LoadContext& context, unsigned& cacheI
 
 
 
-static void mapSharedCache()
+static void mapSharedCache(uintptr_t mainExecutableSlide)
 {
        dyld3::SharedCacheOptions opts;
        opts.cacheDirOverride   = sSharedCacheOverrideDir;
        opts.forcePrivate               = (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion);
-
-
 #if __x86_64__ && !TARGET_OS_SIMULATOR
        opts.useHaswell                 = sHaswell;
 #else
        opts.useHaswell                 = false;
 #endif
        opts.verbose                    = gLinkContext.verboseMapping;
+    // <rdar://problem/32031197> respect -disable_aslr boot-arg
+    // <rdar://problem/56299169> kern.bootargs is now blocked
+       opts.disableASLR                = (mainExecutableSlide == 0) && dyld3::internalInstall(); // infer ASLR is off if main executable is not slid
        loadDyldCache(opts, &sSharedCacheLoadInfo);
 
        // update global state
@@ -4004,7 +4101,7 @@ static void mapSharedCache()
                dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_SHARED_CACHE_A, sSharedCacheLoadInfo.path, (const uuid_t *)&dyld::gProcessInfo->sharedCacheUUID[0], {0,0}, {{ 0, 0 }}, (const mach_header *)sSharedCacheLoadInfo.loadAddress);
        }
 
-//#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR
+//#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
 // RAM disk booting does not have shared cache yet
 // Don't make lack of a shared cache fatal in that case
 //     if ( sSharedCacheLoadInfo.loadAddress == nullptr ) {
@@ -4188,6 +4285,24 @@ void halt(const char* message)
                strlcat(error_string, sSharedCacheLoadInfo.errorMessage, sizeof(error_string));
                strlcat(error_string, "\n", sizeof(error_string));
                strlcat(error_string, message, sizeof(error_string));
+       } else if ( dyld::gProcessInfo->errorKind == DYLD_EXIT_REASON_DYLIB_MISSING ) {
+               // If a dylib is missing, but we have the cache, print the cache UUID to make it easier
+               // to see what might have gone wrong
+               if ( sSharedCacheLoadInfo.loadAddress == nullptr ) {
+                       strlcpy(error_string, "dyld: No shared cache present\n", sizeof(error_string));
+               } else {
+                       uuid_t cacheUUID;
+                       sSharedCacheLoadInfo.loadAddress->getUUID(cacheUUID);
+                       uuid_string_t uuidStr;
+                       uuid_unparse_upper(cacheUUID, uuidStr);
+
+                       strlcpy(error_string, "dyld: Using shared cache: ", sizeof(error_string));
+                       strlcat(error_string, uuidStr, sizeof(error_string));
+                       strlcat(error_string, "\n", sizeof(error_string));
+               }
+
+               dyld::log("dyld: %s\n", message);
+               strlcat(error_string, message, sizeof(error_string));
        }
        else {
                dyld::log("dyld: %s\n", message);
@@ -4731,7 +4846,7 @@ static void setContext(const macho_header* mainExecutableMH, int argc, const cha
 // Its presences means that the binary wants to have DYLD ignore
 // DYLD_ environment variables.
 //
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
 static bool hasRestrictedSegment(const macho_header* mh)
 {
        const uint32_t cmd_count = mh->ncmds;
@@ -4762,7 +4877,7 @@ static bool hasRestrictedSegment(const macho_header* mh)
 }
 #endif
 
-#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR
+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
 static bool isFairPlayEncrypted(const macho_header* mh)
 {
        const uint32_t cmd_count = mh->ncmds;
@@ -5091,18 +5206,21 @@ static void configureProcessRestrictions(const macho_header* mainExecutableMH, c
        uint64_t amfiInputFlags = 0;
 #if TARGET_OS_SIMULATOR
        amfiInputFlags |= AMFI_DYLD_INPUT_PROC_IN_SIMULATOR;
-#elif __MAC_OS_X_VERSION_MIN_REQUIRED
+#elif TARGET_OS_OSX
        if ( hasRestrictedSegment(mainExecutableMH) )
                amfiInputFlags |= AMFI_DYLD_INPUT_PROC_HAS_RESTRICT_SEG;
-#elif __IPHONE_OS_VERSION_MIN_REQUIRED
+#elif TARGET_OS_IPHONE
        if ( isFairPlayEncrypted(mainExecutableMH) )
                amfiInputFlags |= AMFI_DYLD_INPUT_PROC_IS_ENCRYPTED;
 #endif
        uint64_t amfiOutputFlags = 0;
        const char* amfiFake = nullptr;
-       if ( dyld3::internalInstall() && dyld3::BootArgs::enableDyldTestMode() ) {
+       if constexpr(BUILD_FOR_TESTING == 1) {
+               amfiFake = _simple_getenv(envp, "DYLD_AMFI_FAKE");
+       } else if ( dyld3::internalInstall() && dyld3::BootArgs::enableDyldTestMode() ) {
                amfiFake = _simple_getenv(envp, "DYLD_AMFI_FAKE");
        }
+
        if ( amfiFake != nullptr ) {
                amfiOutputFlags = hexToUInt64(amfiFake, nullptr);
        }
@@ -5120,7 +5238,7 @@ static void configureProcessRestrictions(const macho_header* mainExecutableMH, c
 #endif
        }
        else {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
                // support chrooting from old kernel
                bool isRestricted = false;
                bool libraryValidation = false;
@@ -5157,15 +5275,15 @@ static void configureProcessRestrictions(const macho_header* mainExecutableMH, c
 // called by _dyld_register_driverkit_main()
 void setMainEntry(void (*main)())
 {
-       if ( sEntryOveride == nullptr )
-               sEntryOveride = main;
+       if ( sEntryOverride == nullptr )
+               sEntryOverride = main;
        else
                halt("_dyld_register_driverkit_main() may only be called once");
 }
 
 bool processIsRestricted()
 {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
        return !gLinkContext.allowEnvVarsPath;
 #else
        return false;
@@ -5204,7 +5322,7 @@ void notifyKernelAboutImage(const struct macho_header* mh, const char* fileInfo)
        fsobj_id_t fsobj_id = {0};
        if (endptr != nullptr) {
                fsobj_id_scalar = hexToUInt64(endptr+1, &endptr);
-               fsobj_id = *reinterpret_cast<fsobj_id_t *>(&tmp);
+               fsobj_id = *reinterpret_cast<fsobj_id_t *>(&fsobj_id_scalar);
        }
        const uint32_t cmd_count = mh->ncmds;
        const struct load_command* const cmds = (struct load_command*)((char*)mh + sizeof(macho_header));
@@ -5226,10 +5344,10 @@ void notifyKernelAboutImage(const struct macho_header* mh, const char* fileInfo)
        }
 }
 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
 static void* getProcessInfo() { return dyld::gProcessInfo; }
 static const SyscallHelpers sSysCalls = {
-               12,
+               13,
                // added in version 1
                &open,
                &close, 
@@ -5299,7 +5417,10 @@ static const SyscallHelpers sSysCalls = {
                // Add in version 12
                &mach_msg_destroy,
                &mach_port_construct,
-               &mach_port_destruct
+               &mach_port_destruct,
+               // Add in version 13
+               &fstat,
+               &vm_copy
 };
 
 __attribute__((noinline))
@@ -5342,7 +5463,7 @@ static const char* useSimulatorDyld(int fd, const macho_header* mainExecutableMH
                if ( pread(fd, firstPage, 4096, fileOffset) != 4096 )
                        return "pread(dyld_sim) failed";
        }
-       else if ( !isCompatibleMachO(firstPage, dyldPath) ) {
+       else if ( !isCompatibleMachO(firstPage, dyldPath, fd, fileOffset, fileLength) ) {
                return "dyld_sim is not compatible with the loaded process, likely due to architecture mismatch";
        }
        
@@ -5474,34 +5595,22 @@ static const char* useSimulatorDyld(int fd, const macho_header* mainExecutableMH
        }
        close(fd);
 
-       // walk newly mapped dyld_sim __TEXT load commands to find entry point
+       // Walk newly mapped dyld_sim load commands to find entry point
        uintptr_t entry = 0;
-       cmd = (struct load_command*)(((char*)loadAddress)+sizeof(macho_header));
-       const uint32_t count = ((macho_header*)(loadAddress))->ncmds;
-       for (uint32_t i = 0; i < count; ++i) {
-               if (cmd->cmd == LC_UNIXTHREAD) {
-               #if __i386__
-                       const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16);
-                       // entry point must be in first segment
-                       if ( registers->__eip < firstSeg->vmaddr )
-                               return "dyld_sim entry point not in __TEXT segment";
-                       if ( registers->__eip > (firstSeg->vmaddr + firstSeg->vmsize) )
-                               return "dyld_sim entry point not in __TEXT segment";
-                       entry = (registers->__eip + loadAddress - preferredLoadAddress);
-               #elif __x86_64__
-                       const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16);
-                       // entry point must be in first segment
-                       if ( registers->__rip < firstSeg->vmaddr )
-                               return "dyld_sim entry point not in __TEXT segment";
-                       if ( registers->__rip > (firstSeg->vmaddr + firstSeg->vmsize) )
-                               return "dyld_sim entry point not in __TEXT segment";
-                       entry = (registers->__rip + loadAddress - preferredLoadAddress);
-               #endif
-               }
-               cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
-       }
-       if ( entry == 0 )
+       bool unusedUsesCRT = false;
+       uint64_t entryOffset = 0;
+       if ( !((dyld3::MachOAnalyzer*)loadAddress)->getEntry(entryOffset, unusedUsesCRT) ) {
                return "dyld_sim entry not found";
+       }
+
+       // Translate the load address by the entry offset in order to get the runtime address.
+       entry = (uintptr_t)loadAddress;
+       entry += entryOffset;
+
+#if __arm64e__
+       // It's necessary to sign the entry pointer.
+       entry = (uint64_t)__builtin_ptrauth_sign_unauthenticated((void*)entry, ptrauth_key_asia, 0);
+#endif
 
        // notify debugger that dyld_sim is loaded
        dyld_image_info info;
@@ -5517,7 +5626,7 @@ static const char* useSimulatorDyld(int fd, const macho_header* mainExecutableMH
        fsobj.fid_objno = (uint32_t)inode;
        fsobj.fid_generation = (uint32_t)(inode>>32);
        fsid.val[0] = sb.st_dev;
-       dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, dyldPath, (const uuid_t *)&uuidCmd->uuid[0], fsobj, fsid, (const mach_header *)mh);
+       dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, dyldPath, (const uuid_t *)&uuidCmd->uuid[0], fsobj, fsid, (const mach_header *)loadAddress);
 
        const char** appleParams = apple;
 
@@ -5654,33 +5763,6 @@ static bool closureValid(const dyld3::closure::LaunchClosure* mainClosure, const
                                return false;
                        }
                }
-#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 ) {
-                       // If we got a boot UUID then we should have also recorded it in the closure
-                       if ( expectedBootUUID == nullptr) {
-                               // The closure didn't have a UUID but now we do.  This isn't valid.
-                               if ( gLinkContext.verboseWarnings )
-                                       dyld::log("dyld: closure %p missing boot-UUID\n", mainClosure);
-                               return false;
-                       } else if ( strcmp(expectedBootUUID, actualBootSessionUUID) != 0 ) {
-                               if ( gLinkContext.verboseWarnings )
-                                       dyld::log("dyld: closure %p built in different boot context\n", mainClosure);
-                               return false;
-                       }
-               } else {
-                       // We didn't get a UUID now, which is ok so long as the closure also doesn't have one.
-                       if ( expectedBootUUID != nullptr) {
-                               if ( gLinkContext.verboseWarnings )
-                                       dyld::log("dyld: closure %p has boot-UUID\n", mainClosure);
-                               return false;
-                       }
-               }
-#endif
        }
 
        // verify all mach-o files have not changed since closure was built
@@ -5690,7 +5772,7 @@ static bool closureValid(const dyld3::closure::LaunchClosure* mainClosure, const
                __block uint64_t expectedMtime;
                if ( image->hasFileModTimeAndInode(expectedInode, expectedMtime) ) {
                        struct stat statBuf;
-                       if ( ::stat(image->path(), &statBuf) == 0 ) {
+                       if ( dyld3::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());
@@ -5710,7 +5792,7 @@ static bool closureValid(const dyld3::closure::LaunchClosure* mainClosure, const
                return false;
 
        // verify cdHash of main executable is same as recorded in closure
-       const dyld3::closure::Image* mainImage = mainClosure->images()->imageForNum(mainClosure->topImage());
+       const dyld3::closure::Image* mainImage = mainClosure->topImage();
 
        __block bool foundCDHash = false;
        __block bool foundValidCDHash = false;
@@ -5794,7 +5876,7 @@ static bool closureValid(const dyld3::closure::LaunchClosure* mainClosure, const
        // verify files that are supposed to be missing actually are missing
        mainClosure->forEachMustBeMissingFile(^(const char* path, bool& stop) {
                struct stat statBuf;
-               if ( ::stat(path, &statBuf) == 0 ) {
+               if ( dyld3::stat(path, &statBuf) == 0 ) {
                        stop = true;
                        foundFileThatInvalidatesClosure = true;
                        if ( gLinkContext.verboseWarnings )
@@ -5805,7 +5887,7 @@ static bool closureValid(const dyld3::closure::LaunchClosure* mainClosure, const
        // verify files that are supposed to exist are there with the
        mainClosure->forEachSkipIfExistsFile(^(const dyld3::closure::LaunchClosure::SkippedFile &file, bool &stop) {
                struct stat statBuf;
-               if ( ::stat(file.path, &statBuf) == 0 ) {
+               if ( dyld3::stat(file.path, &statBuf) == 0 ) {
                        if ( (statBuf.st_mtime != file.mtime) || (statBuf.st_ino != file.inode) ) {
                                if ( gLinkContext.verboseWarnings )
                                        dyld::log("dyld: closure %p not used because mtime/inode for '%s' has changed since closure was built\n", mainClosure, file.path);
@@ -5851,9 +5933,12 @@ static bool dolog(const char* format, ...)
 static bool launchWithClosure(const dyld3::closure::LaunchClosure* mainClosure,
                                                          const DyldSharedCache* dyldCache,
                                                          const dyld3::MachOLoaded* mainExecutableMH, uintptr_t mainExecutableSlide,
-                                                         int argc, const char* argv[], const char* envp[], const char* apple[],
-                                                         uintptr_t* entry, uintptr_t* startGlue)
+                                                         int argc, const char* argv[], const char* envp[], const char* apple[], Diagnostics& diag,
+                                                         uintptr_t* entry, uintptr_t* startGlue, bool* closureOutOfDate, bool* recoverable)
 {
+       *closureOutOfDate = false;
+       *recoverable      = true;
+
        // 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();
@@ -5866,19 +5951,23 @@ static bool launchWithClosure(const dyld3::closure::LaunchClosure* mainClosure,
 
        // allocate space for Array<LoadedImage>
        STACK_ALLOC_ARRAY(dyld3::LoadedImage, allImages, mainClosure->initialLoadCount());
+       STACK_ALLOC_ARRAY(dyld3::LoadedImage, noImages, 1);
 
        // Get the pre-optimized Objective-C so that we can bind the selectors
        const dyld3::closure::ObjCSelectorOpt*                                  selectorOpt = nullptr;
        dyld3::Array<dyld3::closure::Image::ObjCSelectorImage>  selectorImages;
        mainClosure->selectorHashTable(selectorImages, selectorOpt);
 
-       __block dyld3::Loader loader({}, allImages, dyldCache, imagesArrays,
-                                                                selectorOpt, selectorImages,
+       __block dyld3::Loader loader(noImages, allImages, dyldCache, imagesArrays,
+                                                                selectorOpt, selectorImages, sRootsChecker,
+                                                                (dyld3::Platform)gProcessInfo->platform,
                                                                 (gLinkContext.verboseLoading ? &dolog : &nolog),
                                                                 (gLinkContext.verboseMapping ? &dolog : &nolog),
                                                                 (gLinkContext.verboseBind    ? &dolog : &nolog),
-                                                                (gLinkContext.verboseDOF     ? &dolog : &nolog));
-       dyld3::closure::ImageNum mainImageNum = mainClosure->topImage();
+                                                                (gLinkContext.verboseDOF     ? &dolog : &nolog),
+                                                                (sClosureKind == ClosureKind::minimal),
+                                                                (dyld3::LaunchErrorInfo*)&gProcessInfo->errorKind);
+       dyld3::closure::ImageNum mainImageNum = mainClosure->topImageNum();
        mainClosureImages->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
                if ( image->imageNum() == mainImageNum ) {
                        // add main executable (which is already mapped by kernel) to list
@@ -5896,13 +5985,32 @@ static bool launchWithClosure(const dyld3::closure::LaunchClosure* mainClosure,
 
        // recursively load all dependents and fill in allImages array
        bool someCacheImageOverridden = false;
-       Diagnostics diag;
        loader.completeAllDependents(diag, someCacheImageOverridden);
        if ( diag.noError() )
-               loader.mapAndFixupAllImages(diag, dyld3::Loader::dtraceUserProbesEnabled());
+               loader.mapAndFixupAllImages(diag, dyld3::Loader::dtraceUserProbesEnabled(), false, closureOutOfDate, recoverable);
        if ( diag.hasError() ) {
                if ( gLinkContext.verboseWarnings )
                        dyld::log("dyld: %s\n", diag.errorMessage());
+               if ( !*recoverable ) {
+                       // we won't make it to libDyldEntry, so the image list will never be set up
+                       // hack together an image list here so crash reports show the binaries involved
+                       __block unsigned loadImageCount = 0;
+                       loader.forEachImage(^(const dyld3::LoadedImage& li, bool& stop) {
+                               ++loadImageCount;
+                       });
+                       dyld_image_info* tempArray = new dyld_image_info[loadImageCount];
+                       __block unsigned i = 0;
+                       loader.forEachImage(^(const dyld3::LoadedImage& li, bool& stop) {
+                               tempArray[i].imageFilePath    = li.image()->path();
+                               tempArray[i].imageLoadAddress = li.loadedAddress();
+                               tempArray[i].imageFileModDate = 0;
+                               ++i;
+                       });
+                       dyld::gProcessInfo->infoArray        = tempArray;
+                       dyld::gProcessInfo->infoArrayCount   = loadImageCount;
+                       dyld::gProcessInfo->initialImageCount= loadImageCount;
+                       dyld::gProcessInfo->infoArrayChangeTimestamp = mach_absolute_time();
+               }
                return false;
        }
 
@@ -5917,8 +6025,8 @@ static bool launchWithClosure(const dyld3::closure::LaunchClosure* mainClosure,
        const dyld3::LibDyldEntryVector* libDyldEntry = (dyld3::LibDyldEntryVector*)loader.resolveTarget(dyldEntry);
 
        // send info on all images to libdyld.dylb
-       libDyldEntry->setVars(mainExecutableMH, argc, argv, envp, apple);
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+       libDyldEntry->setVars(mainExecutableMH, argc, argv, envp, apple, sKeysDisabled, sOnlyPlatformArm64e);
+#if TARGET_OS_OSX
        uint32_t progVarsOffset;
        if ( mainClosure->hasProgramVars(progVarsOffset) ) {
                if ( libDyldEntry->vectorVersion >= 8 ) {
@@ -5950,6 +6058,10 @@ static bool launchWithClosure(const dyld3::closure::LaunchClosure* mainClosure,
        if ( libDyldEntry->vectorVersion > 3 )
                libDyldEntry->setLogFunction(&dyld::vlog);
 #endif
+       if ( libDyldEntry->vectorVersion >= 9 )
+               libDyldEntry->setLaunchMode(sLaunchModeUsed);
+
+
        libDyldEntry->setOldAllImageInfo(gProcessInfo);
        dyld3::LoadedImage* libSys = loader.findImage(mainClosure->libSystemImageNum());
        libDyldEntry->setInitialImageList(mainClosure, dyldCache, sSharedCacheLoadInfo.path, allImages, *libSys);
@@ -5961,11 +6073,14 @@ static bool launchWithClosure(const dyld3::closure::LaunchClosure* mainClosure,
        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);
        }
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
        if ( gLinkContext.driverKit ) {
-               *entry = (uintptr_t)sEntryOveride;
+               if (libDyldEntry->vectorVersion >= 10)
+                       *entry = (uintptr_t)libDyldEntry->getDriverkitMain();
                if ( *entry == 0 )
                        halt("no entry point registered");
+               if ( sClosureKind != ClosureKind::minimal )
+                       halt("driverkit process should run with minimal closures");
                *startGlue = (uintptr_t)(libDyldEntry->startFunc);
        }
        else
@@ -5978,6 +6093,10 @@ static bool launchWithClosure(const dyld3::closure::LaunchClosure* mainClosure,
                        // set entry to "main" function in program
                        *startGlue = (uintptr_t)(libDyldEntry->startFunc);
                        *entry     = loader.resolveTarget(progEntry);
+#if __has_feature(ptrauth_calls)
+                       // start() calls the result pointer as a function pointer so we need to sign it.
+                       *entry = (uintptr_t)__builtin_ptrauth_sign_unauthenticated((void*)*entry, 0, 0);
+#endif
                }
                else if ( mainClosure->startEntry(progEntry) ) {
                        // old style app linked with crt1.o
@@ -5994,29 +6113,17 @@ static bool launchWithClosure(const dyld3::closure::LaunchClosure* mainClosure,
 }
 
 
-static const char* getTempDir(const char* envp[])
-{
-       if (const char* tempDir = _simple_getenv(envp, "TMPDIR"))
-               return tempDir;
-
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-       return "/private/tmp/";
-#else
-       return "/private/var/tmp/";
-#endif
-}
-
 static const dyld3::closure::LaunchClosure* mapClosureFile(const char* closurePath)
 {
        struct stat statbuf;
-       if ( ::stat(closurePath, &statbuf) == -1 )
+       if ( dyld3::stat(closurePath, &statbuf) == -1 )
                return nullptr;
 
        // check for tombstone file
        if ( statbuf.st_size == 0 )
                return nullptr;
 
-       int fd = ::open(closurePath, O_RDONLY);
+       int fd = dyld3::open(closurePath, O_RDONLY, 0);
        if ( fd < 0 )
                return nullptr;
 
@@ -6036,10 +6143,11 @@ static bool needsDyld2ErrorMessage(const char* msg)
        return false;
 }
 
-
 // Note: buildLaunchClosure calls halt() if there is an error building the closure
 static const dyld3::closure::LaunchClosure* buildLaunchClosure(const uint8_t* mainExecutableCDHash,
-                                                                                                                          const dyld3::closure::LoadedFileInfo& mainFileInfo, const char* envp[])
+                                                                                                                          const dyld3::closure::LoadedFileInfo& mainFileInfo,
+                                                                                                                          const char* envp[],
+                                                                                                                          const dyld3::Array<uint8_t>& bootToken)
 {
        const dyld3::MachOLoaded* mainExecutableMH = (const dyld3::MachOLoaded*)mainFileInfo.fileContent;
        dyld3::closure::PathOverrides pathOverrides;
@@ -6053,24 +6161,30 @@ static const dyld3::closure::LaunchClosure* buildLaunchClosure(const uint8_t* ma
        }
 
        char closurePath[PATH_MAX];
-       dyld3::closure::ClosureBuilder::LaunchErrorInfo* errorInfo = (dyld3::closure::ClosureBuilder::LaunchErrorInfo*)&gProcessInfo->errorKind;
+       bool canSaveClosureToDisk = !bootToken.empty() && dyld3::closure::LaunchClosure::buildClosureCachePath(mainFileInfo.path, envp, true, closurePath);
+       dyld3::LaunchErrorInfo* errorInfo = (dyld3::LaunchErrorInfo*)&gProcessInfo->errorKind;
+       const dyld3::GradedArchs& archs = dyld3::GradedArchs::forCurrentOS(sKeysDisabled, sOnlyPlatformArm64e);
        dyld3::closure::FileSystemPhysical fileSystem;
-       const dyld3::GradedArchs& archs = dyld3::GradedArchs::forCurrentOS(mainExecutableMH);
        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,
-                                                                                  archs, pathOverrides, atPathHanding, gLinkContext.allowEnvVarsPath, errorInfo);
+       dyld3::closure::ClosureBuilder builder(dyld3::closure::kFirstLaunchClosureImageNum, fileSystem, sRootsChecker, sSharedCacheLoadInfo.loadAddress, true,
+                                                                                  archs, pathOverrides, atPathHanding, gLinkContext.allowEnvVarsPath, errorInfo, (dyld3::Platform)gProcessInfo->platform);
        if (sForceInvalidSharedCacheClosureFormat)
                builder.setDyldCacheInvalidFormatVersion();
+       if (sClosureKind == ClosureKind::minimal)
+               builder.makeMinimalClosures();
+       else if ( canSaveClosureToDisk )
+               builder.setCanSkipEncodingRebases(); // <rdar://problem/56172089> large iOS apps with massive number of rebases can overflow 16MB closure file limit
        if ( !gLinkContext.allowInterposing )
                builder.disableInterposing();
+
        const dyld3::closure::LaunchClosure* result = builder.makeLaunchClosure(mainFileInfo, gLinkContext.allowInsertFailures);
        if ( builder.diagnostics().hasError() ) {
                const char* errMsg = builder.diagnostics().errorMessage();
                // let apps with this error fallback to dyld2 mode
                if ( needsDyld2ErrorMessage(errMsg) ) {
-                       if ( dyld3::closure::LaunchClosure::buildClosureCachePath(mainFileInfo.path, closurePath, getTempDir(envp), true) ) {
+                       if ( canSaveClosureToDisk ) {
                                // create empty file as a tombstone to not keep trying
-                               int fd = ::open(closurePath, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
+                               int fd = dyld3::open(closurePath, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
                                if ( fd != -1 ) {
                                        ::fchmod(fd, S_IRUSR);
                                        ::close(fd);
@@ -6097,8 +6211,24 @@ static const dyld3::closure::LaunchClosure* buildLaunchClosure(const uint8_t* ma
                        dyld::log("dyld: somehow just built closure is invalid\n");
                return nullptr;
        }
-       // try to atomically save closure to disk to speed up next launch
-       if ( dyld3::closure::LaunchClosure::buildClosureCachePath(mainFileInfo.path, closurePath, getTempDir(envp), true) ) {
+
+       // write closure file but only if we have boot-token
+       if ( canSaveClosureToDisk ) {
+               if ( const dyld3::closure::LaunchClosure* existingClosure = mapClosureFile(closurePath) ) {
+                       if ( (existingClosure->size() == result->size()) && (memcmp(existingClosure, result, result->size()) == 0) ) {
+                               // closure file already exists and has same content, so re-use file by altering boot-token
+                               ::chmod(closurePath, S_IRUSR|S_IWUSR); // file has to be writable to alter attributes
+                               // handle both attribute size change and missing attribute
+                               if ( ::setxattr(closurePath, DYLD_CLOSURE_XATTR_NAME, bootToken.begin(), bootToken.count(), 0, XATTR_REPLACE) != 0 )
+                                       ::setxattr(closurePath, DYLD_CLOSURE_XATTR_NAME, bootToken.begin(), bootToken.count(), 0, 0);
+                               ::chmod(closurePath, S_IRUSR);
+                               result->deallocate();
+                               if ( gLinkContext.verboseWarnings )
+                                       dyld::log("dyld: reusing previous boot %s closure %p (size=%lu) for %s\n", existingClosure->topImage()->variantString(), existingClosure, existingClosure->size(), sExecPath);
+                               return existingClosure;
+                       }
+               }
+               // make new file
                char closurePathTemp[PATH_MAX];
                strlcpy(closurePathTemp, closurePath, PATH_MAX);
                int mypid = getpid();
@@ -6111,20 +6241,22 @@ static const dyld3::closure::LaunchClosure* buildLaunchClosure(const uint8_t* ma
                putHexByte(mypid, s);
                *s = '\0';
                strlcat(closurePathTemp, pidBuf, PATH_MAX);
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-               int fd = ::open(closurePathTemp, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
+#if TARGET_OS_OSX
+               int fd = dyld3::open(closurePathTemp, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
 #else
                int fd = ::open_dprotected_np(closurePathTemp, O_WRONLY|O_CREAT, PROTECTION_CLASS_D, 0, S_IRUSR|S_IWUSR);
 #endif
                if ( fd != -1 ) {
                        ::ftruncate(fd, result->size());
                        ::write(fd, result, result->size());
+                       ::fsetxattr(fd, DYLD_CLOSURE_XATTR_NAME, bootToken.begin(), bootToken.count(), 0, 0);
                        ::fchmod(fd, S_IRUSR);
                        ::close(fd);
                        ::rename(closurePathTemp, closurePath);
                        // free built closure and mmap file() to reduce dirty memory
                        result->deallocate();
                        result = mapClosureFile(closurePath);
+                       sLaunchModeUsed |= DYLD_LAUNCH_MODE_CLOSURE_SAVED_TO_FILE;
                }
                else if ( gLinkContext.verboseWarnings ) {
                        dyld::log("could not save closure (errno=%d) to: %s\n", errno, closurePathTemp);
@@ -6132,19 +6264,35 @@ static const dyld3::closure::LaunchClosure* buildLaunchClosure(const uint8_t* ma
        }
 
        if ( gLinkContext.verboseWarnings )
-               dyld::log("dyld: just built closure %p (size=%lu) for %s\n", result, result->size(), sExecPath);
+               dyld::log("dyld: just built %s closure %p (size=%lu) for %s\n", result->topImage()->variantString(), result, result->size(), sExecPath);
 
        return result;
 }
 
 static const dyld3::closure::LaunchClosure* findCachedLaunchClosure(const uint8_t* mainExecutableCDHash,
                                                                                                                                    const dyld3::closure::LoadedFileInfo& mainFileInfo,
-                                                                                                                                       const char* envp[])
+                                                                                                                                       const char* envp[],
+                                                                                                                                       const dyld3::Array<uint8_t>& bootToken)
 {
+       // get path to where closure file will be store for this program
        char closurePath[PATH_MAX];
-       // build base path of $TMPDIR/dyld/<prog-name>-
-       if ( !dyld3::closure::LaunchClosure::buildClosureCachePath(mainFileInfo.path, closurePath, getTempDir(envp), false) )
+       if ( !dyld3::closure::LaunchClosure::buildClosureCachePath(mainFileInfo.path, envp, false, closurePath) ) {
+               // if cannot construct path to use/store closure file, then use minimal closures
+               if ( sClosureKind == ClosureKind::unset )
+                       sClosureKind = ClosureKind::minimal;
+               return nullptr;
+       }
+
+       // if file exists, but extended attribute is wrong, ignore file (might be re-used later)
+       if ( bootToken.empty() )
+               return nullptr;
+       uint8_t filesBootToken[bootToken.count()];
+       ssize_t attrSize = ::getxattr(closurePath, DYLD_CLOSURE_XATTR_NAME, filesBootToken, bootToken.count(), 0, 0);
+       if ( attrSize != bootToken.count() )
                return nullptr;
+       if ( memcmp(bootToken.begin(), filesBootToken, bootToken.count()) != 0 )
+               return nullptr;
+
        const dyld3::closure::LaunchClosure* closure = mapClosureFile(closurePath);
        if ( closure == nullptr )
                return nullptr;
@@ -6155,7 +6303,7 @@ static const dyld3::closure::LaunchClosure* findCachedLaunchClosure(const uint8_
        }
 
        if ( gLinkContext.verboseWarnings )
-               dyld::log("dyld: used cached closure %p (size=%lu) for %s\n", closure, closure->size(), sExecPath);
+               dyld::log("dyld: used cached %s closure %p (size=%lu) for %s\n", closure->topImage()->variantString(), closure, closure->size(), sExecPath);
 
        return closure;
 }
@@ -6164,7 +6312,7 @@ static const dyld3::closure::LaunchClosure* findCachedLaunchClosure(const uint8_
 
        
 static ClosureMode getPlatformDefaultClosureMode() {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
 #if __i386__
        // rdar://problem/32701418: Don't use dyld3 for i386 for now.
        return ClosureMode::Off;
@@ -6179,7 +6327,7 @@ static ClosureMode getPlatformDefaultClosureMode() {
                return ClosureMode::On;
        else
                return ClosureMode::Off;
-#endif // __MAC_OS_X_VERSION_MIN_REQUIRED
+#endif // TARGET_OS_OSX
 }
 
 //
@@ -6200,11 +6348,29 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
        //Check and see if there are any kernel flags
        dyld3::BootArgs::setFlags(hexToUInt64(_simple_getenv(apple, "dyld_flags"), nullptr));
 
+#if __has_feature(ptrauth_calls)
+       // Check and see if kernel disabled JOP pointer signing (which lets us load plain arm64 binaries)
+       if ( const char* disableStr = _simple_getenv(apple, "ptrauth_disabled") ) {
+               if ( strcmp(disableStr, "1") == 0 )
+                       sKeysDisabled = true;
+       }
+       else {
+               // needed until kernel passes ptrauth_disabled for arm64 main executables
+               if ( (mainExecutableMH->cpusubtype == CPU_SUBTYPE_ARM64_V8) || (mainExecutableMH->cpusubtype == CPU_SUBTYPE_ARM64_ALL) )
+                       sKeysDisabled = true;
+       }
+#endif
+
     // Grab the cdHash of the main executable from the environment
        uint8_t mainExecutableCDHashBuffer[20];
        const uint8_t* mainExecutableCDHash = nullptr;
-       if ( hexToBytes(_simple_getenv(apple, "executable_cdhash"), 40, mainExecutableCDHashBuffer) )
-               mainExecutableCDHash = mainExecutableCDHashBuffer;
+       if ( const char* mainExeCdHashStr = _simple_getenv(apple, "executable_cdhash") ) {
+               unsigned bufferLenUsed;
+               if ( hexStringToBytes(mainExeCdHashStr, mainExecutableCDHashBuffer, sizeof(mainExecutableCDHashBuffer), bufferLenUsed) )
+                       mainExecutableCDHash = mainExecutableCDHashBuffer;
+       }
+
+       getHostInfo(mainExecutableMH, mainExecutableSlide);
 
 #if !TARGET_OS_SIMULATOR
        // Trace dyld's load
@@ -6220,7 +6386,8 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
 
        // Set the platform ID in the all image infos so debuggers can tell the process type
        // FIXME: This can all be removed once we make the kernel handle it in rdar://43369446
-       if (gProcessInfo->version >= 16) {
+       // The host may not have the platform field in its struct, but there's space for it in the padding, so always set it
+       {
                __block bool platformFound = false;
                ((dyld3::MachOFile*)mainExecutableMH)->forEachSupportedPlatform(^(dyld3::Platform platform, uint32_t minOS, uint32_t sdk) {
                        if (platformFound) {
@@ -6232,7 +6399,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                if (gProcessInfo->platform == (uint32_t)dyld3::Platform::unknown) {
                        // There were no platforms found in the binary. This may occur on macOS for alternate toolchains and old binaries.
                        // It should never occur on any of our embedded platforms.
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
                        gProcessInfo->platform = (uint32_t)dyld3::Platform::macOS;
 #else
                        halt("MH_EXECUTE binaries must specify a minimum supported OS version");
@@ -6240,16 +6407,21 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                }
        }
 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
        // Check to see if we need to override the platform
        const char* forcedPlatform = _simple_getenv(envp, "DYLD_FORCE_PLATFORM");
        if (forcedPlatform) {
-               if (strncmp(forcedPlatform, "6", 1) != 0) {
-                       halt("DYLD_FORCE_PLATFORM is only supported for platform 6");
+               dyld_platform_t forcedPlatformType = 0;
+               if (strncmp(forcedPlatform, "6", 1) == 0) {
+                       forcedPlatformType = PLATFORM_MACCATALYST;
+               } else if (strncmp(forcedPlatform, "2", 1) == 0) {
+                       forcedPlatformType = PLATFORM_IOS;
+               } else  {
+                       halt("DYLD_FORCE_PLATFORM is only supported for platform 2 or 6.");
                }
                const dyld3::MachOFile* mf = (dyld3::MachOFile*)sMainExecutableMachHeader;
                if (mf->allowsAlternatePlatform()) {
-                       gProcessInfo->platform = PLATFORM_IOSMAC;
+                       gProcessInfo->platform = forcedPlatformType;
                }
        }
 
@@ -6260,7 +6432,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                char simDyldPath[PATH_MAX]; 
                strlcpy(simDyldPath, rootPath, PATH_MAX);
                strlcat(simDyldPath, "/usr/lib/dyld_sim", PATH_MAX);
-               int fd = my_open(simDyldPath, O_RDONLY, 0);
+               int fd = dyld3::open(simDyldPath, O_RDONLY, 0);
                if ( fd != -1 ) {
                        const char* errMessage = useSimulatorDyld(fd, mainExecutableMH, simDyldPath, argc, argv, envp, apple, startGlue, &result);
                        if ( errMessage != NULL )
@@ -6286,7 +6458,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
        // <rdar://problem/13868260> Remove interim apple[0] transition code from dyld
        if (!sExecPath) sExecPath = apple[0];
 
-#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_SIMULATOR
+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
        // <rdar://54095622> kernel is not passing a real path for main executable
        if ( strncmp(sExecPath, "/var/containers/Bundle/Application/", 35) == 0 ) {
                if ( char* newPath = (char*)malloc(strlen(sExecPath)+10) ) {
@@ -6317,6 +6489,17 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
        else
                sExecShortName = sExecPath;
 
+#if TARGET_OS_OSX && __has_feature(ptrauth_calls)
+       // on Apple Silicon macOS, only Apple signed ("platform binary") arm64e can be loaded
+       sOnlyPlatformArm64e = true;
+
+       // internal builds, or if boot-arg is set, then non-platform-binary arm64e slices can be run
+       if ( const char* abiMode = _simple_getenv(apple, "arm64e_abi") ) {
+               if ( strcmp(abiMode, "all") == 0 )
+                       sOnlyPlatformArm64e = false;
+       }
+#endif
+
     configureProcessRestrictions(mainExecutableMH, envp);
 
        // Check if we should force dyld3.  Note we have to do this outside of the regular env parsing due to AMFI
@@ -6325,20 +6508,13 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                        if ( strcmp(useClosures, "0") == 0 ) {
                                sClosureMode = ClosureMode::Off;
                        } else if ( strcmp(useClosures, "1") == 0 ) {
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-
-#if __i386__
-                               // don't support dyld3 for 32-bit macOS
-#else
-                               // Also don't support dyld3 for iOSMac right now
-                               if ( gProcessInfo->platform != PLATFORM_IOSMAC ) {
-                                       sClosureMode = ClosureMode::On;
-                               }
-#endif // __i386__
-
-#else
+       #if !__i386__ // don't support dyld3 for 32-bit macOS
                                sClosureMode = ClosureMode::On;
-#endif // __MAC_OS_X_VERSION_MIN_REQUIRED
+                               sClosureKind = ClosureKind::full;
+       #endif
+                       } else if ( strcmp(useClosures, "2") == 0 ) {
+                               sClosureMode = ClosureMode::On;
+                               sClosureKind = ClosureKind::minimal;
                        } else {
                                dyld::warn("unknown option to DYLD_USE_CLOSURES.  Valid options are: 0 and 1\n");
                        }
@@ -6346,7 +6522,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                }
        }
 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
     if ( !gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsSharedCache ) {
                pruneEnvironmentVariables(envp, &apple);
                // set again because envp and apple may have changed or moved
@@ -6358,18 +6534,25 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                checkEnvironmentVariables(envp);
                defaultUninitializedFallbackPaths(envp);
        }
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
-       if ( gProcessInfo->platform == PLATFORM_IOSMAC ) {
-               gLinkContext.rootPaths = parseColonList("/System/iOSSupport", NULL);
-               gLinkContext.iOSonMac = 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;
-       }
-       else if ( ((dyld3::MachOFile*)mainExecutableMH)->supportsPlatform(dyld3::Platform::driverKit) ) {
-               gLinkContext.driverKit = true;
-               gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion;
+#if TARGET_OS_OSX
+       switch (gProcessInfo->platform) {
+#if (TARGET_OS_OSX && TARGET_CPU_ARM64)
+               case PLATFORM_IOS:
+                       sClosureMode = ClosureMode::On; // <rdar://problem/56792308> Run iOS apps on macOS in dyld3 mode
+                       [[clang::fallthrough]];
+#endif
+               case PLATFORM_MACCATALYST:
+                       gLinkContext.rootPaths = parseColonList("/System/iOSSupport", NULL);
+                       gLinkContext.iOSonMac = 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;
+                       break;
+               case PLATFORM_DRIVERKIT:
+                       gLinkContext.driverKit = true;
+                       gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion;
+                       break;
        }
 #endif
        if ( sEnv.DYLD_PRINT_OPTS )
@@ -6382,18 +6565,11 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
 #if !TARGET_OS_SIMULATOR
        if ( _simple_getenv(envp, "DYLD_JUST_BUILD_CLOSURE") != nullptr ) {
 #if TARGET_OS_IPHONE
-               const char* tempDir = getTempDir(envp);
-               if ( (tempDir != nullptr) && (geteuid() != 0) ) {
-                       // Use realpath to prevent something like TMPRIR=/tmp/../usr/bin
-                       char realPath[PATH_MAX];
-                       if ( realpath(tempDir, realPath) != NULL )
-                               tempDir = realPath;
-                       if (strncmp(tempDir, "/private/var/mobile/Containers/", strlen("/private/var/mobile/Containers/")) == 0) {
-                               sJustBuildClosure = true;
-                       }
-               }
+               char tempClosurePath[PATH_MAX];
+               if ( dyld3::closure::LaunchClosure::buildClosureCachePath(sExecPath, envp, false, tempClosurePath) )
+                       sJustBuildClosure = true;
 #endif
-               // If we didn't like the format of TMPDIR, just exit.  We don't want to launch the app as that would bring up the UI
+               // If the env vars for the data contain look wrong, don't want to launch the app as that would bring up the UI
                if (!sJustBuildClosure) {
                        _exit(EXIT_SUCCESS);
                }
@@ -6402,19 +6578,99 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
 
        if ( sJustBuildClosure )
                sClosureMode = ClosureMode::On;
-       getHostInfo(mainExecutableMH, mainExecutableSlide);
 
        // load shared cache
        checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide);
        if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) {
 #if TARGET_OS_SIMULATOR
                if ( sSharedCacheOverrideDir)
-                       mapSharedCache();
+                       mapSharedCache(mainExecutableSlide);
 #else
-               mapSharedCache();
+               mapSharedCache(mainExecutableSlide);
 #endif
        }
 
+#if !TARGET_OS_SIMULATOR
+       if ( getpid() == 1 ) {
+               // Get the value as set by the boot-args
+               uint64_t commPageValue = 0;
+               size_t commPageValueSize = sizeof(commPageValue);
+               if ( sysctlbyname("kern.dyld_flags", &commPageValue, &commPageValueSize, nullptr, 0) != 0 ) {
+                       // Try again with the old name
+                       // TODO: Remove this when we are always on new enough kernels
+                       sysctlbyname("kern.dyld_system_flags", &commPageValue, &commPageValueSize, nullptr, 0);
+               }
+
+               commPageValue &= CommPageBootArgMask;
+               // logToConsole("dyld: got comm page flags 0x%llx\n", commPageValue);
+
+               // If we are PID 1 (launchd) and on macOS, then we should check if the simulator support dylibs
+               // are roots or not.
+               // If they are not roots at launchd time, and the file system is read-only, then we know for sure
+               // they will not be roots later
+#if DYLD_SIMULATOR_ROOTS_SUPPORT
+               bool fileSystemIsWritable = true;
+
+               // logToConsole("dyld: in launchd\n");
+               struct statfs statBuffer;
+               int statResult = statfs("/", &statBuffer);
+               if ( statResult == 0 ) {
+                       if ( !strcmp(statBuffer.f_fstypename, "apfs") ) {
+                               if ( (statBuffer.f_flags & (MNT_RDONLY | MNT_SNAPSHOT)) == (MNT_RDONLY | MNT_SNAPSHOT) ) {
+                                       // logToConsole("dyld: got statfs flags 0x%llx\n", statBuffer.f_flags);
+                                       fileSystemIsWritable = false;
+                               }
+                       }
+               } else {
+                       int error = errno;
+                       logToConsole("dyld: could not stat '/', errno = %d\n", error);
+               }
+
+               // If the file system is read-only, then we can check now whether any of the simulator support
+               // dylibs are roots
+               bool libsystemKernelIsRoot              = false;
+               bool libsystemPlatformIsRoot    = false;
+               bool libsystemPThreadIsRoot     = false;
+               if ( !fileSystemIsWritable && (sSharedCacheLoadInfo.loadAddress != nullptr)) {
+                       dyld3::closure::FileSystemPhysical fileSystem;
+                       libsystemKernelIsRoot   = !dyld3::RootsChecker::uuidMatchesSharedCache("/usr/lib/system/libsystem_kernel.dylib",
+                                                                                                                                                                  &fileSystem, sSharedCacheLoadInfo.loadAddress);
+                       libsystemPlatformIsRoot = !dyld3::RootsChecker::uuidMatchesSharedCache("/usr/lib/system/libsystem_platform.dylib",
+                                                                                                                                                                  &fileSystem, sSharedCacheLoadInfo.loadAddress);
+                       libsystemPThreadIsRoot  = !dyld3::RootsChecker::uuidMatchesSharedCache("/usr/lib/system/libsystem_pthread.dylib",
+                                                                                                                                                                  &fileSystem, sSharedCacheLoadInfo.loadAddress);
+               }
+               commPageValue |= (fileSystemIsWritable ? CommPageFlags::fileSystemCanBeModified : CommPageFlags::None);
+               commPageValue |= (libsystemKernelIsRoot ? CommPageFlags::libsystemKernelIsRoot : CommPageFlags::None);
+               commPageValue |= (libsystemPlatformIsRoot ? CommPageFlags::libsystemPlatformIsRoot : CommPageFlags::None);
+               commPageValue |= (libsystemPThreadIsRoot ? CommPageFlags::libsystemPThreadIsRoot : CommPageFlags::None);
+#endif // DYLD_SIMULATOR_ROOTS_SUPPORT
+
+               logToConsole("dyld: setting comm page to 0x%llx\n", commPageValue);
+               if ( sysctlbyname("kern.dyld_flags", nullptr, 0, &commPageValue, sizeof(commPageValue)) != 0 ) {
+                       // Try again with the old name
+                       // TODO: Remove this when we are always on new enough kernels
+                       sysctlbyname("kern.dyld_system_flags", nullptr, 0, &commPageValue, sizeof(commPageValue));
+               }
+       }
+
+#if DYLD_SIMULATOR_ROOTS_SUPPORT
+       // Set the roots checker to the state from the comm page
+       {
+               uint64_t dyldFlags = *((uint64_t*)_COMM_PAGE_DYLD_SYSTEM_FLAGS);
+               bool fileSystemCanBeModified = dyldFlags & CommPageFlags::fileSystemCanBeModified;
+               bool libsystemKernelIsRoot = dyldFlags & CommPageFlags::libsystemKernelIsRoot;
+               bool libsystemPlatformIsRoot = dyldFlags & CommPageFlags::libsystemPlatformIsRoot;
+               bool libsystemPThreadIsRoot = dyldFlags & CommPageFlags::libsystemPThreadIsRoot;
+               sRootsChecker.setFileSystemCanBeModified(fileSystemCanBeModified);
+               sRootsChecker.setLibsystemKernelIsRoot(libsystemKernelIsRoot);
+               sRootsChecker.setLibsystemPlatformIsRoot(libsystemPlatformIsRoot);
+               sRootsChecker.setLibsystemPThreadIsRoot(libsystemPThreadIsRoot);
+       }
+#endif // DYLD_SIMULATOR_ROOTS_SUPPORT
+
+#endif // !TARGET_OS_SIMULATOR
+
        // If we haven't got a closure mode yet, then check the environment and cache type
        if ( sClosureMode == ClosureMode::Unset ) {
                // First test to see if we forced in dyld2 via a kernel boot-arg
@@ -6434,8 +6690,9 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
 #if !TARGET_OS_SIMULATOR
        if ( sClosureMode == ClosureMode::Off ) {
                if ( gLinkContext.verboseWarnings )
-                       dyld::log("dyld: not using closure because of DYLD_USE_CLOSURES or -force_dyld2=1 override\n");
+                       dyld::log("dyld: not using closures\n");
        } else {
+               sLaunchModeUsed = DYLD_LAUNCH_MODE_USING_CLOSURE;
                const dyld3::closure::LaunchClosure* mainClosure = nullptr;
                dyld3::closure::LoadedFileInfo mainFileInfo;
                mainFileInfo.fileContent = mainExecutableMH;
@@ -6444,7 +6701,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                mainFileInfo.sliceOffset = 0;
                mainFileInfo.sliceLen = -1;
                struct stat mainExeStatBuf;
-               if ( ::stat(sExecPath, &mainExeStatBuf) == 0 ) {
+               if ( dyld3::stat(sExecPath, &mainExeStatBuf) == 0 ) {
                        mainFileInfo.inode = mainExeStatBuf.st_ino;
                        mainFileInfo.mtime = mainExeStatBuf.st_mtime;
                }
@@ -6453,6 +6710,8 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                        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 ( mainClosure != nullptr )
+                               sLaunchModeUsed |= DYLD_LAUNCH_MODE_CLOSURE_FROM_OS;
                }
 
                // We only want to try build a closure at runtime if its an iOS third party binary, or a macOS binary from the shared cache
@@ -6463,17 +6722,32 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                        allowClosureRebuilds = true;
                }
 
-               if ( (mainClosure != nullptr) && !closureValid(mainClosure, mainFileInfo, mainExecutableCDHash, true, envp) )
+               if ( (mainClosure != nullptr) && !closureValid(mainClosure, mainFileInfo, mainExecutableCDHash, true, envp) ) {
                        mainClosure = nullptr;
+                       sLaunchModeUsed &= ~DYLD_LAUNCH_MODE_CLOSURE_FROM_OS;
+               }
+
+               // <rdar://60333505> bootToken is a concat of boot-hash kernel passes down for app and dyld's uuid
+               uint8_t bootTokenBufer[128];
+               unsigned bootTokenBufferLen = 0;
+               if ( const char* bootHashStr = _simple_getenv(apple, "executable_boothash") ) {
+                       if ( hexStringToBytes(bootHashStr, bootTokenBufer, sizeof(bootTokenBufer), bootTokenBufferLen) ) {
+                               if ( ((dyld3::MachOFile*)&__dso_handle)->getUuid(&bootTokenBufer[bootTokenBufferLen]) )
+                                       bootTokenBufferLen += sizeof(uuid_t);
+                       }
+               }
+               dyld3::Array<uint8_t> bootToken(bootTokenBufer, bootTokenBufferLen, bootTokenBufferLen);
 
                // If we didn't find a valid cache closure then try build a new one
                if ( (mainClosure == nullptr) && allowClosureRebuilds ) {
                        // if forcing closures, and no closure in cache, or it is invalid, check for cached closure
                        if ( !sForceInvalidSharedCacheClosureFormat )
-                               mainClosure = findCachedLaunchClosure(mainExecutableCDHash, mainFileInfo, envp);
+                               mainClosure = findCachedLaunchClosure(mainExecutableCDHash, mainFileInfo, envp, bootToken);
                        if ( mainClosure == nullptr ) {
                                // if  no cached closure found, build new one
-                               mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp);
+                               mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp, bootToken);
+                               if ( mainClosure != nullptr )
+                                       sLaunchModeUsed |= DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH;
                        }
                }
 
@@ -6484,22 +6758,29 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                // try using launch closure
                if ( mainClosure != nullptr ) {
                        CRSetCrashLogMessage("dyld3: launch started");
+                       if ( mainClosure->topImage()->fixupsNotEncoded() )
+                               sLaunchModeUsed |= DYLD_LAUNCH_MODE_MINIMAL_CLOSURE;
+                       Diagnostics diag;
+                       bool closureOutOfDate;
+                       bool recoverable;
                        bool launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH,
-                                                                                         mainExecutableSlide, argc, argv, envp, apple, &result, startGlue);
-                       if ( !launched && allowClosureRebuilds ) {
+                                                                                         mainExecutableSlide, argc, argv, envp, apple, diag, &result, startGlue, &closureOutOfDate, &recoverable);
+                       if ( !launched && closureOutOfDate && allowClosureRebuilds ) {
                                // closure is out of date, build new one
-                               mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp);
+                               mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp, bootToken);
                                if ( mainClosure != nullptr ) {
+                                       diag.clearError();
+                                       sLaunchModeUsed |= DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH;
+                                       if ( mainClosure->topImage()->fixupsNotEncoded() )
+                                               sLaunchModeUsed |= DYLD_LAUNCH_MODE_MINIMAL_CLOSURE;
+                                       else
+                                               sLaunchModeUsed &= ~DYLD_LAUNCH_MODE_MINIMAL_CLOSURE;
                                        launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress, (dyld3::MachOLoaded*)mainExecutableMH,
-                                                                                                mainExecutableSlide, argc, argv, envp, apple, &result, startGlue);
+                                                                                                mainExecutableSlide, argc, argv, envp, apple, diag, &result, startGlue, &closureOutOfDate, &recoverable);
                                }
                        }
                        if ( launched ) {
                                gLinkContext.startedInitializingMainExecutable = true;
-#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;
@@ -6508,12 +6789,14 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
                                if ( gLinkContext.verboseWarnings ) {
                                        dyld::log("dyld: unable to use closure %p\n", mainClosure);
                                }
+                               if ( !recoverable )
+                                       halt(diag.errorMessage());
                        }
                }
        }
 #endif // TARGET_OS_SIMULATOR
        // could not use closure info, launch old way
-
+       sLaunchModeUsed = 0;
 
 
        // install gdb notifier
@@ -6542,13 +6825,13 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
 #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_ARM64E)
+               if ((sMainExecutableMachHeader->cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E)
                        sDisableAcceleratorTables = true;
 #endif
                bool mainExcutableAlreadyRebased = false;
                if ( (sSharedCacheLoadInfo.loadAddress != nullptr) && !dylibsCanOverrideCache() && !sDisableAcceleratorTables && (sSharedCacheLoadInfo.loadAddress->header.accelerateInfoAddr != 0) ) {
                        struct stat statBuf;
-                       if ( ::stat(IPHONE_DYLD_SHARED_CACHE_DIR "no-dyld2-accelerator-tables", &statBuf) != 0 )
+                       if ( dyld3::stat(IPHONE_DYLD_SHARED_CACHE_DIR "no-dyld2-accelerator-tables", &statBuf) != 0 )
                                sAllCacheImagesProxy = ImageLoaderMegaDylib::makeImageLoaderMegaDylib(&sSharedCacheLoadInfo.loadAddress->header, sSharedCacheLoadInfo.slide, mainExecutableMH, gLinkContext);
                }
 
@@ -6556,7 +6839,7 @@ reloadAllImages:
 #endif
 
 
-       #if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
                gLinkContext.strictMachORequired = false;
         // <rdar://problem/22805519> be less strict about old macOS mach-o binaries
         ((dyld3::MachOFile*)mainExecutableMH)->forEachSupportedPlatform(^(dyld3::Platform platform, uint32_t minOS, uint32_t sdk) {
@@ -6614,6 +6897,54 @@ reloadAllImages:
                sAllImages.reserve(INITIAL_IMAGE_COUNT);
        #endif
 
+#if defined(__x86_64__) && !TARGET_OS_SIMULATOR
+               if (dyld::isTranslated()) {
+                       struct dyld_all_runtime_info {
+                               uint32_t image_count;
+                               dyld_image_info* images;
+                               uint32_t uuid_count;
+                               dyld_uuid_info* uuids;
+                               uint32_t aot_image_count;
+                               dyld_aot_image_info* aots;
+                               dyld_aot_shared_cache_info aot_cache_info;
+                       };
+
+                       dyld_all_runtime_info* runtime_info;
+                       int ret = syscall(0x7000004, &runtime_info);
+                       if (ret == 0) {
+                               for (int i = 0; i < runtime_info->uuid_count; i++) {
+                                       dyld_image_info image_info = runtime_info->images[i];
+                                       dyld_uuid_info uuid_info = runtime_info->uuids[i];
+
+                                       // add the arm64 cambria runtime to uuid info
+                                       addNonSharedCacheImageUUID(uuid_info);
+
+                                       struct stat sb;
+                                       if (stat(image_info.imageFilePath, &sb) == 0) {
+                                               fsid_t fsid = {{0, 0}};
+                                               fsobj_id_t fsobj = {0};
+                                               ino_t inode = sb.st_ino;
+                                               fsobj.fid_objno = (uint32_t)inode;
+                                               fsobj.fid_generation = (uint32_t)(inode>>32);
+                                               fsid.val[0] = sb.st_dev;
+
+                                               dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, image_info.imageFilePath, &(uuid_info.imageUUID), fsobj, fsid, image_info.imageLoadAddress);
+                                       }
+                               }
+
+                               // add aot images to dyld_all_image_info
+                               addAotImagesToAllAotImages(runtime_info->aot_image_count, runtime_info->aots);
+
+                               // add the arm64 cambria runtime to dyld_all_image_info
+                               addImagesToAllImages(runtime_info->image_count, runtime_info->images);
+
+                               // set the aot shared cache info in dyld_all_image_info
+                               dyld::gProcessInfo->aotSharedCacheBaseAddress = runtime_info->aot_cache_info.cacheBaseAddress;
+                               memcpy(dyld::gProcessInfo->aotSharedCacheUUID, runtime_info->aot_cache_info.cacheUUID, sizeof(uuid_t));
+                       }
+               }
+#endif
+
                // Now that shared cache is loaded, setup an versioned dylib overrides
        #if SUPPORT_VERSIONED_PATHS
                checkVersionedPaths();
@@ -6741,7 +7072,7 @@ reloadAllImages:
                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);
+                               image->recursiveBind(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true, nullptr);
                        }
                }
                
@@ -6768,9 +7099,9 @@ reloadAllImages:
                }
                ARIADNEDBG_CODE(220, 1);
 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
                if ( gLinkContext.driverKit ) {
-                       result = (uintptr_t)sEntryOveride;
+                       result = (uintptr_t)sEntryOverride;
                        if ( result == 0 )
                                halt("no entry point registered");
                        *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
@@ -6793,10 +7124,6 @@ reloadAllImages:
                                *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();
@@ -6823,7 +7150,7 @@ reloadAllImages:
                result = (uintptr_t)&fake_main;
                *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
        }
-       
+
        return result;
 }
 
index cf82ca6bd4d4af94031beda2035db92c63070949..8c763934a40ded3fda32c794adc0970df75354fc 100644 (file)
@@ -123,8 +123,6 @@ namespace dyld {
        extern bool                                     processIsRestricted();
        extern const char*                      getStandardSharedCacheFilePath();
        extern bool                                     hasInsertedOrInterposingLibraries();
-       extern int                                      my_stat(const char* path, struct stat* buf);
-       extern int                                      my_open(const char* path, int flag, int other);
        bool                                            sandboxBlockedOpen(const char* path);
        bool                                            sandboxBlockedMmap(const char* path);
        bool                                            sandboxBlockedStat(const char* path);
@@ -141,4 +139,7 @@ namespace dyld {
        bool                                            isPathInCache(const char* path);
        const char*                                     getPathFromIndex(unsigned cacheIndex);
 #endif
+#if defined(__x86_64__)
+       bool                                            isTranslated();
+#endif
 }
index d76f056a75bd0a5f7bd34f8a913cf164f9bbb44a..e275e4e5e6fd69720f4a521cdbeac7012dea309a 100644 (file)
@@ -109,10 +109,10 @@ 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
-#else
+#if TARGET_OS_OSX
        #define DEPRECATED_APIS_SUPPORTED 1
+#else
+       #define DEPRECATED_APIS_SUPPORTED 0
 #endif
 
 static bool sDynamicInterposing = false;
@@ -179,6 +179,7 @@ static const struct dyld_func dyld_funcs[] = {
     {"__dyld_get_image_vmaddr_slide",                                  (void*)_dyld_get_image_vmaddr_slide },
     {"__dyld_get_image_name",                                                  (void*)_dyld_get_image_name },
     {"__dyld_get_image_slide",                                                 (void*)_dyld_get_image_slide },
+    {"__dyld_get_prog_image_header",                                   (void*)_dyld_get_prog_image_header },
     {"__dyld__NSGetExecutablePath",                                            (void*)_NSGetExecutablePath },
 
        // SPIs
@@ -207,7 +208,7 @@ static const struct dyld_func dyld_funcs[] = {
     {"__dyld_register_for_image_loads",                                        (void*)_dyld_register_for_image_loads },
     {"__dyld_register_for_bulk_image_loads",                   (void*)_dyld_register_for_bulk_image_loads },
     {"__dyld_register_driverkit_main",                                 (void*)_dyld_register_driverkit_main },
-
+    {"__dyld_halt",                                                                            (void*)dyld::halt },
 
 #if DEPRECATED_APIS_SUPPORTED
 #pragma clang diagnostic push
@@ -390,6 +391,14 @@ const char* _dyld_get_image_name(uint32_t image_index)
        return allImagesIndexedPath(image_index);
 }
 
+const struct mach_header* _dyld_get_prog_image_header()
+{
+       if ( dyld::gLogAPIs )
+               dyld::log("%s()\n", __func__);
+       
+       return dyld::mainExecutable()->machHeader();
+}
+
 static const void *stripPointer(const void *ptr) {
 #if __has_feature(ptrauth_calls)
        return __builtin_ptrauth_strip(ptr, ptrauth_key_asia);
@@ -1371,7 +1380,7 @@ bool dlopen_preflight_internal(const char* path, void* callerAddress)
 
        const bool leafName = (strchr(path, '/') == NULL);
        const bool absolutePath = (path[0] == '/');
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
+#if TARGET_OS_IPHONE
        char canonicalPath[PATH_MAX]; 
        // <rdar://problem/7017050> dlopen() not opening frameworks from shared cache with // or ./ in path
        if ( !leafName ) {
@@ -1402,7 +1411,7 @@ bool dlopen_preflight_internal(const char* path, void* callerAddress)
        if ( dyld::inSharedCache(path) )
                return true;
 
-#if __MAC_OS_X_VERSION_MIN_REQUIRED
+#if TARGET_OS_OSX
        // <rdar://problem/47464387> dlopen_preflight() on symlink to image in shared cache leaves it half loaded
        if ( strncmp(path, "/System/Library/", 16) == 0 ) {
                char canonicalPath[PATH_MAX];
@@ -1517,7 +1526,7 @@ void* dlopen_internal(const char* path, int mode, void* callerAddress)
        void* result = NULL;
        const bool leafName = (strchr(path, '/') == NULL);
        const bool absolutePath = (path[0] == '/');
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
+#if TARGET_OS_IPHONE
        char canonicalPath[PATH_MAX]; 
        // <rdar://problem/7017050> dlopen() not opening frameworks from shared cache with // or ./ in path
        if ( !leafName ) {
@@ -1830,7 +1839,7 @@ void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress)
                        // 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_ARM64E) ) {
+                       if ( result && ((dyld::gLinkContext.mainExecutable->machHeader()->cpusubtype  & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) ) {
                                const ImageLoader* symbolImage = image;
                                if (!symbolImage->containsAddress(result)) {
                                        symbolImage = dyld::findImageContainingAddress(result);
@@ -1864,7 +1873,7 @@ void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress)
                        // 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_ARM64E) ) {
+                       if ( result && ((dyld::gLinkContext.mainExecutable->machHeader()->cpusubtype  & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) ) {
                                const ImageLoader* symbolImage = image;
                                if (!symbolImage->containsAddress(result)) {
                                        symbolImage = dyld::findImageContainingAddress(result);
@@ -1910,7 +1919,7 @@ void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress)
                        // 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_ARM64E) ) {
+                       if ( result && ((dyld::gLinkContext.mainExecutable->machHeader()->cpusubtype  & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) ) {
                                const ImageLoader* symbolImage = image;
                                if (!symbolImage->containsAddress(result)) {
                                        symbolImage = dyld::findImageContainingAddress(result);
@@ -1955,7 +1964,7 @@ void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress)
                        // 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_ARM64E) ) {
+                       if ( result && ((dyld::gLinkContext.mainExecutable->machHeader()->cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) ) {
                                const ImageLoader* symbolImage = image;
                                if (!symbolImage->containsAddress(result)) {
                                        symbolImage = dyld::findImageContainingAddress(result);
@@ -2006,7 +2015,7 @@ void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress)
                        // 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_ARM64E) ) {
+                       if ( result && ((dyld::gLinkContext.mainExecutable->machHeader()->cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) ) {
                                const ImageLoader* symbolImage = image;
                                if (!symbolImage->containsAddress(result)) {
                                        symbolImage = dyld::findImageContainingAddress(result);
@@ -2194,7 +2203,8 @@ const void* _dyld_get_shared_cache_range(size_t* length)
     const DyldSharedCache* cache = (DyldSharedCache*)dyld::imMemorySharedCacheHeader();
     if ( cache != nullptr ) {
         const dyld_cache_mapping_info* const mappings = (dyld_cache_mapping_info*)((char*)cache + cache->header.mappingOffset);
-        *length = (size_t)((mappings[2].address + mappings[2].size) - mappings[0].address);
+               const dyld_cache_mapping_info* lastMapping = &mappings[cache->header.mappingCount - 1];
+        *length = (size_t)((lastMapping->address + lastMapping->size) - cache->unslidLoadAddress());
         return cache;
     }
        return nullptr;
index 5579076e37ddf3fba82f95254f4b8f2ee363fa2c..51b36a0a4598da43572d527a103b27b04cc8710f 100644 (file)
@@ -28,6 +28,7 @@
 #include <sys/mman.h>
 #include <execinfo.h>
 
+#include <TargetConditionals.h>
 #include <System/sys/csr.h>
 #include <crt_externs.h>
 #include <Availability.h>
@@ -39,6 +40,7 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <System/sys/codesign.h>
+#include <libc_private.h>
 
 #include <mach-o/dyld_images.h>
 #include <mach-o/dyld.h>
@@ -81,11 +83,55 @@ extern "C" void setLookupFunc(void*);
 
 extern bool gUseDyld3;
 
+
+// <rdar://problem/61161069> libdyld.dylib should use abort_with_payload() for asserts
+VIS_HIDDEN
+void abort_report_np(const char* format, ...)
+{
+    va_list list;
+    const char *str;
+    _SIMPLE_STRING s = _simple_salloc();
+    if ( s != NULL ) {
+        va_start(list, format);
+        _simple_vsprintf(s, format, list);
+        va_end(list);
+        str = _simple_string(s);
+    }
+    else {
+        // _simple_salloc failed, but at least format may have useful info by itself
+        str = format;
+    }
+    if ( gUseDyld3 ) {
+        dyld3::halt(str);
+    }
+    else {
+        void (*p)(const char* msg) __attribute__((__noreturn__));
+        _dyld_func_lookup("__dyld_halt", (void**)&p);
+        p(str);
+    }
+    // halt() doesn't return, so we can't call _simple_sfree
+}
+
+// libc uses assert()
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winvalid-noreturn"
+VIS_HIDDEN
+void __assert_rtn(const char* func, const char* file, int line, const char* failedexpr)
+{
+    if (func == NULL) {
+        abort_report_np("Assertion failed: (%s), file %s, line %d.\n", failedexpr, file, line);
+    } else {
+        abort_report_np("Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr, func, file, line);
+    }
+}
+#pragma clang diagnostic pop
+
+
 // deprecated APIs are still availble on Mac OS X, but not on iPhone OS
-#if __IPHONE_OS_VERSION_MIN_REQUIRED || TARGET_OS_DRIVERKIT
-       #define DEPRECATED_APIS_SUPPORTED 0
-#else
+#if TARGET_OS_OSX
        #define DEPRECATED_APIS_SUPPORTED 1
+#else
+       #define DEPRECATED_APIS_SUPPORTED 0
 #endif
 
 /*
@@ -626,15 +672,10 @@ bool _dyld_get_image_uuid(const struct mach_header* mh, uuid_t uuid)
 }
 
 dyld_platform_t dyld_get_active_platform(void) {
-    if (gUseDyld3) { return dyld3::dyld_get_active_platform(); }
-    if (_dyld_get_all_image_infos()->version >= 16) { return (dyld_platform_t)_dyld_get_all_image_infos()->platform; }
+    if (gUseDyld3)
+        return dyld3::dyld_get_active_platform();
 
-    __block dyld_platform_t result;
-    // FIXME: Remove this once we only care about version 16 or greater all image infos
-    dyld3::dyld_get_image_versions((mach_header*)_NSGetMachExecuteHeader(), ^(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_version) {
-        result = platform;
-    });
-    return result;
+    return (dyld_platform_t)_dyld_get_all_image_infos()->platform;
 }
 
 dyld_platform_t dyld_get_base_platform(dyld_platform_t platform) {
@@ -654,11 +695,11 @@ bool dyld_minos_at_least(const struct mach_header* mh, dyld_build_version_t vers
 }
 
 bool dyld_program_sdk_at_least(dyld_build_version_t version) {
-    return dyld3::dyld_sdk_at_least((mach_header*)_NSGetMachExecuteHeader(),version);
+    return dyld3::dyld_program_sdk_at_least(version);
 }
 
 bool dyld_program_minos_at_least(dyld_build_version_t version) {
-    return dyld3::dyld_minos_at_least((mach_header*)_NSGetMachExecuteHeader(), version);
+    return dyld3::dyld_program_minos_at_least(version);
 }
 
 // Function that walks through the load commands and calls the internal block for every version found
@@ -1006,6 +1047,9 @@ const char* symbol_name,
 void** address,
 NSModule* module)
 {
+       if ( gUseDyld3 )
+               return dyld3::_dyld_lookup_and_bind(symbol_name, address, module);
+
        DYLD_LOCK_THIS_BLOCK;
     static void (*p)(const char*, void** , NSModule*) = NULL;
 
@@ -1264,6 +1308,19 @@ intptr_t _dyld_get_image_slide(const struct mach_header* mh)
        return dyld3::_dyld_get_image_slide(mh);
 }
 
+const struct mach_header *
+_dyld_get_prog_image_header()
+{
+    if ( gUseDyld3 )
+        return dyld3::_dyld_get_prog_image_header();
+
+    DYLD_LOCK_THIS_BLOCK;
+    static const struct mach_header * (*p)(void) = NULL;
+
+    if(p == NULL)
+        _dyld_func_lookup("__dyld_get_prog_image_header", (void**)&p);
+    return p();
+}
 
 #if DEPRECATED_APIS_SUPPORTED
 bool
@@ -1317,7 +1374,7 @@ bool _dyld_all_twolevel_modules_prebound(void)
 #endif // DEPRECATED_APIS_SUPPORTED
 
 
-#include <dlfcn.h>
+#include <dlfcn_private.h>
 #include <stddef.h>
 #include <pthread.h>
 #include <stdlib.h>
@@ -1516,13 +1573,12 @@ int dlclose(void* handle)
        return result;
 }
 
-void* dlopen(const char* path, int mode)
+static void* dlopen_internal(const char* path, int mode, void* callerAddress)
 {
     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));
+        result = dyld3::dlopen_internal(path, mode, callerAddress);
         timer.setData4(result);
         return result;
     }
@@ -1534,7 +1590,7 @@ void* dlopen(const char* path, int mode)
 
     if(p == NULL)
         _dyld_func_lookup("__dyld_dlopen_internal", (void**)&p);
-    result = p(path, mode, __builtin_return_address(0));
+    result = p(path, mode, callerAddress);
     // 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>
@@ -1544,6 +1600,31 @@ void* dlopen(const char* path, int mode)
        return result;
 }
 
+void* dlopen(const char* path, int mode)
+{
+    void* result = dlopen_internal(path, mode, __builtin_return_address(0));
+    if ( result )
+        return result;
+
+
+    return nullptr;
+}
+
+void* dlopen_from(const char* path, int mode, void* addressInCaller)
+{
+#if __has_feature(ptrauth_calls)
+    addressInCaller = __builtin_ptrauth_strip(addressInCaller, ptrauth_key_asia);
+#endif
+    return dlopen_internal(path, mode, addressInCaller);
+}
+
+#if !__i386__
+void* dlopen_audited(const char* path, int mode)
+{
+       return dlopen(path, mode);
+}
+#endif // !__i386__
+
 bool dlopen_preflight(const char* path)
 {
     dyld3::ScopedTimer timer(DBG_DYLD_TIMING_DLOPEN_PREFLIGHT, path, 0, 0);
@@ -1725,6 +1806,32 @@ bool _dyld_shared_cache_is_locally_built()
     return false;
 }
 
+const char* _dyld_shared_cache_real_path(const char* path)
+{
+    const dyld_all_image_infos* allInfo = _dyld_get_all_image_infos();
+    if ( allInfo != nullptr  ) {
+        const DyldSharedCache* cache = (const DyldSharedCache*)(allInfo->sharedCacheBaseAddress);
+        if ( cache != nullptr )
+            return cache->getCanonicalPath(path);
+    }
+    return nullptr;
+}
+
+bool _dyld_shared_cache_contains_path(const char* path)
+{
+    return _dyld_shared_cache_real_path(path) != nullptr;
+}
+
+
+uint32_t _dyld_launch_mode()
+{
+    if ( gUseDyld3 )
+        return dyld3::_dyld_launch_mode();
+
+    // in dyld2 mode all flag bits are zero
+    return 0;
+}
+
 void _dyld_images_for_addresses(unsigned count, const void* addresses[], struct dyld_image_uuid_offset infos[])
 {
     if ( gUseDyld3 )
@@ -1764,9 +1871,9 @@ void _dyld_register_for_bulk_image_loads(void (*func)(unsigned imageCount, const
     return p(func);
 }
 
-bool dyld_need_closure(const char* execPath, const char* tempDir)
+bool dyld_need_closure(const char* execPath, const char* dataContainerRootDir)
 {
-    return dyld3::dyld_need_closure(execPath, tempDir);
+    return dyld3::dyld_need_closure(execPath, dataContainerRootDir);
 }
 
 bool dyld_process_is_restricted()
@@ -1808,6 +1915,13 @@ bool dyld_has_inserted_or_interposing_libraries()
        return p();
 }
 
+bool _dyld_has_fix_for_radar(const char *rdar) {
+    // There is no point in shimming this to dyld3, actual functionality can exist purely in libSystem for
+    // both dyld2 and dyld3.
+    return false;
+}
+
+
 void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count)
 {
        if ( gUseDyld3 )
@@ -1854,13 +1968,13 @@ void _dyld_fork_child()
 static void* mapStartOfCache(const char* path, size_t length)
 {
        struct stat statbuf;
-       if ( ::stat(path, &statbuf) == -1 )
+       if ( dyld3::stat(path, &statbuf) == -1 )
                return NULL;
 
        if ( (size_t)statbuf.st_size < length )
                return NULL;
 
-       int cache_fd = ::open(path, O_RDONLY);
+       int cache_fd = dyld3::open(path, O_RDONLY, 0);
        if ( cache_fd < 0 )
                return NULL;
 
@@ -1893,7 +2007,7 @@ static const dyld_cache_header* findCacheInDirAndMap(const uuid_t cacheUuid, con
                        if ( strlcat(cachePath, entp->d_name, PATH_MAX) >= PATH_MAX )
                                continue;
                        if ( const dyld_cache_header* cacheHeader = (dyld_cache_header*)mapStartOfCache(cachePath, 0x00100000) ) {
-                               if ( ::memcmp(cacheHeader->uuid, cacheUuid, 16) != 0 ) {
+                               if ( (::memcmp(cacheHeader, "dyld_", 5) != 0) || (::memcmp(cacheHeader->uuid, cacheUuid, 16) != 0) ) {
                                        // wrong uuid, unmap and keep looking
                                        ::munmap((void*)cacheHeader, 0x00100000);
                                }
@@ -1926,12 +2040,11 @@ int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extr
        }
        else {
                // look first is default location for cache files
-       #if     __IPHONE_OS_VERSION_MIN_REQUIRED
-               const char* defaultSearchDir = IPHONE_DYLD_SHARED_CACHE_DIR;
-       #else
-               const char* defaultSearchDir = MACOSX_DYLD_SHARED_CACHE_DIR;
-       #endif
-               cacheHeader = findCacheInDirAndMap(cacheUuid, defaultSearchDir);
+    #if TARGET_OS_IPHONE
+               cacheHeader = findCacheInDirAndMap(cacheUuid, IPHONE_DYLD_SHARED_CACHE_DIR);
+    #else
+               cacheHeader = findCacheInDirAndMap(cacheUuid, MACOSX_MRM_DYLD_SHARED_CACHE_DIR);
+    #endif
                // if not there, look in extra search locations
                if ( cacheHeader == NULL ) {
                        for (const char** p = extraSearchDirs; *p != NULL; ++p) {
@@ -1945,7 +2058,7 @@ int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extr
        if ( cacheHeader == NULL )
                return -1;
        
-       if ( cacheHeader->mappingOffset < sizeof(dyld_cache_header) ) {
+       if ( cacheHeader->mappingOffset <= __offsetof(dyld_cache_header, imagesTextOffset) ) {
                // old cache without imagesText array
                if ( needToUnmap )
                        ::munmap((void*)cacheHeader, 0x00100000);
@@ -2049,9 +2162,74 @@ void _dyld_for_each_objc_protocol(const char* protocolName,
 
 void _dyld_register_driverkit_main(void (*mainFunc)(void))
 {
+    if ( gUseDyld3 )
+        return dyld3::_dyld_register_driverkit_main(mainFunc);
+
     static bool (*p)(void (*mainFunc)(void)) = NULL;
 
     if(p == NULL)
         _dyld_func_lookup("__dyld_register_driverkit_main", (void**)&p);
    p(mainFunc);
 }
+
+// This is populated in the shared cache builder, so that the ranges are protected by __DATA_CONST
+// If we have a root, we can find this range in the shared cache libdyld at runtime
+typedef std::pair<const uint8_t*, const uint8_t*> ObjCConstantRange;
+
+#if TARGET_OS_OSX
+__attribute__((section(("__DATA, __objc_ranges"))))
+#else
+__attribute__((section(("__DATA_CONST, __objc_ranges"))))
+#endif
+__attribute__((used))
+static ObjCConstantRange gSharedCacheObjCConstantRanges[dyld_objc_string_kind + 1];
+
+static std::pair<const void*, uint64_t> getDyldCacheConstantRanges() {
+    const dyld_all_image_infos* allInfo = _dyld_get_all_image_infos();
+    if ( allInfo != nullptr  ) {
+        const DyldSharedCache* cache = (const DyldSharedCache*)(allInfo->sharedCacheBaseAddress);
+        if ( cache != nullptr ) {
+            return cache->getObjCConstantRange();
+        }
+    }
+    return { nullptr, 0 };
+}
+
+bool _dyld_is_objc_constant(DyldObjCConstantKind kind, const void* addr) {
+    assert(kind <= dyld_objc_string_kind);
+    // The common case should be that the value is in range, as this is a security
+    // check, so first test against the values in the struct.  If we have a root then
+    // we'll take the slow path later
+    if ( (addr >= gSharedCacheObjCConstantRanges[kind].first) && (addr < gSharedCacheObjCConstantRanges[kind].second) ) {
+        // Make sure that we are pointing at the start of a constant object, not in to the middle of it
+        uint64_t offset = (uint64_t)addr - (uint64_t)gSharedCacheObjCConstantRanges[kind].first;
+        return (offset % (uint64_t)DyldSharedCache::ConstantClasses::cfStringAtomSize) == 0;
+    }
+
+    // If we are in the shared cache, then the above check was sufficient, so this really isn't a valid constant address
+    extern void* __dso_handle;
+    const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)&__dso_handle;
+    if ( ma->inDyldCache() )
+        return false;
+
+    // We now know we are a root, so use the pointers in the shared cache libdyld version of gSharedCacheObjCConstantRanges
+    static std::pair<const void*, uint64_t> sharedCacheRanges = { nullptr, ~0ULL };
+
+    // FIXME: Should we fold this in as an inititalizer above?
+    // That would mean we need to link against somewhere to get ___cxa_guard_acquire/___cxa_guard_release
+    if ( sharedCacheRanges.second == ~0ULL )
+        sharedCacheRanges = getDyldCacheConstantRanges();
+
+    // We have the range of the section in libdyld in the shared cache, now get an array of ranges from it
+    uint64_t numRanges = sharedCacheRanges.second / sizeof(ObjCConstantRange);
+    if ( kind >= numRanges )
+        return false;
+
+    const ObjCConstantRange* rangeArrayBase = (const ObjCConstantRange*)sharedCacheRanges.first;
+    if ( (addr >= rangeArrayBase[kind].first) && (addr < rangeArrayBase[kind].second) ) {
+        // Make sure that we are pointing at the start of a constant object, not in to the middle of it
+        uint64_t offset = (uint64_t)addr - (uint64_t)rangeArrayBase[kind].first;
+        return (offset % (uint64_t)DyldSharedCache::ConstantClasses::cfStringAtomSize) == 0;
+    }
+    return false;
+}
index 7a3448a8156f0d4d640b0ca7098d81dbd35ee5e0..76d6e6196b5d23aa09bedd98cbe055601a89adb3 100644 (file)
@@ -38,7 +38,7 @@
 // from libc.a
 extern "C" void mach_init();
 extern "C" void __guard_setup(const char* apple[]);
-
+extern "C" void _subsystem_init(const char* apple[]);
 
 // from dyld_debugger.cpp
 extern void syncProcessInfo();
@@ -140,6 +140,8 @@ uintptr_t start(const dyld3::MachOLoaded* appsMachHeader, int argc, const char*
        runDyldInitializers(argc, argv, envp, apple);
 #endif
 
+       _subsystem_init(apple);
+
        // now that we are done bootstrapping dyld, call dyld's main
        uintptr_t appsSlide = appsMachHeader->getSlide();
        return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);
index 66ef080eca5642ff082f6a72485130e1a8e3dde9..9039472edc62fdcd284531bea40502c6fd9a7219 100644 (file)
@@ -60,7 +60,7 @@ struct __DATA__dyld {
 static volatile struct __DATA__dyld  myDyldSection __attribute__ ((section ("__DATA,__dyld"))) 
        = { 0, 0, NULL, &NXArgc, &NXArgv, &environ, &__progname };
 
-#if __arm__ && __MAC_OS_X_VERSION_MIN_REQUIRED
+#if __arm__ && TARGET_OS_OSX
 // <rdar://problem/8755380>
 // For historical reasons, gcc and llvm-gcc added -ldylib1.o to the link line of armv6 
 // dylibs when targeting MacOSX (but not iOS).  clang cleans up that mistake, but doing
index 06cc6be5b06a1017f3ed7516a14fdae59af07274..2bdafba21da777798b99a917f906e6d5e87d7811 100644 (file)
@@ -51,7 +51,10 @@ struct dyld_static_pool {
        dyld_static_pool*       previousPool;
        uint8_t*                        current;
        uint8_t*                        end;
-       uint8_t                         pool[1]; 
+
+    // libunwind, and probably others, need the pool to be 16-byte aligned as malloc guarantees that
+    __attribute__((__aligned__(16)))
+    uint8_t                            pool[1];
 };
 
 // allocate initial pool independently of pool header to take less space on disk
@@ -68,6 +71,9 @@ void* malloc(size_t size)
                return p;
        }
        else {
+        // keep allocations 16-byte aligned
+        size = ((size + 15) & -16);
+
                if ( size > DYLD_POOL_CHUNK_SIZE ) {
                        dyld::log("dyld malloc overflow: size=%lu\n", size);
                        dyld::halt("dyld malloc overflow\n");
index b1083f311c12d892c8ea932d3d7ce1658d1fd3f7..8b9b6ccc75d3027480f4df1560a2dace1066e54b 100644 (file)
@@ -212,7 +212,7 @@ Lapple:     ldr     r4, [r3]
 
 
 
-#if __arm64__
+#if __arm64__ && !TARGET_OS_SIMULATOR
        .text
        .align 2
        .globl __dyld_start
@@ -287,7 +287,7 @@ Lapple:     ldr     w4, [x3]
        br      x16
 #endif
 
-#endif // __arm64__
+#endif // __arm64__ && !TARGET_OS_SIMULATOR
 
 
 // When iOS 10.0 simulator runs on 10.11, abort_with_payload() does not exist,
@@ -297,11 +297,11 @@ Lapple:   ldr     w4, [x3]
        .align 2
        .globl  _dyld_fatal_error
 _dyld_fatal_error:
+#if __arm64__ || __arm64e__
+    brk #3
+#else
     int3
+#endif
     nop
 #endif
 
-
-
-
-
index ad689a2f00d70abcee5c37e52d3bd39180642aef..145ed26cff81e10abdae216b60a5a7f78bd6ea72 100644 (file)
@@ -140,6 +140,9 @@ namespace dyld {
                DYLD_SYSCALL_VTABLE_ENTRY(mach_msg_destroy);
                DYLD_SYSCALL_VTABLE_ENTRY(mach_port_construct);
                DYLD_SYSCALL_VTABLE_ENTRY(mach_port_destruct);
+               // Add in version 13
+               DYLD_SYSCALL_VTABLE_ENTRY(fstat);
+               DYLD_SYSCALL_VTABLE_ENTRY(vm_copy);
        };
        extern const struct SyscallHelpers* gSyscallHelpers;
 
index bb24ecca99813376b8bf5cccc5953662a4ccf2f4..072c4b682ba76215599d20f4e77ba6d01bcf19e0 100644 (file)
 
 extern "C"     void _dyld_debugger_notification(enum dyld_notify_mode mode, unsigned long count, uint64_t machHeaders[]);
 
-#if __IPHONE_OS_VERSION_MIN_REQUIRED
-       #define INITIAL_UUID_IMAGE_COUNT 4
-#else
+#if TARGET_OS_OSX
        #define INITIAL_UUID_IMAGE_COUNT 32
+#else
+       #define INITIAL_UUID_IMAGE_COUNT 4
 #endif
 
 VECTOR_NEVER_DESTRUCTED(dyld_image_info);
@@ -50,6 +50,10 @@ VECTOR_NEVER_DESTRUCTED(dyld_uuid_info);
 static std::vector<dyld_image_info> sImageInfos;
 static std::vector<dyld_uuid_info>  sImageUUIDs;
 
+#if __x86_64__
+static std::vector<dyld_aot_image_info> sAotImageInfos;
+#endif
+
 size_t allImagesCount()
 {
        return sImageInfos.size();
@@ -92,6 +96,26 @@ void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[])
        dyld::gProcessInfo->infoArray = &sImageInfos[0];
 }
 
+#if __x86_64__
+void addAotImagesToAllAotImages(uint32_t aotInfoCount, const dyld_aot_image_info aotInfo[])
+{
+    if (sAotImageInfos.size() == 0) {
+        sAotImageInfos.reserve(INITIAL_IMAGE_COUNT);
+    }
+    // set aotInfoArray to NULL to denote it is in-use
+    dyld::gProcessInfo->aotInfoArray = NULL;
+
+    for (uint32_t i = 0; i < aotInfoCount; ++i) {
+        sAotImageInfos.push_back(aotInfo[i]);
+    }
+    dyld::gProcessInfo->aotInfoCount = (uint32_t)sAotImageInfos.size();
+    dyld::gProcessInfo->aotInfoArrayChangeTimestamp = mach_absolute_time();
+
+    // set aotInfoArray back to base address of vector (other process can now read)
+    dyld::gProcessInfo->aotInfoArray = &sAotImageInfos[0];
+}
+#endif
+
 #if TARGET_OS_SIMULATOR
 // called once in dyld_sim start up to copy image list from host dyld to sImageInfos
 void syncProcessInfo()
@@ -229,10 +253,10 @@ void removeImageFromAllImages(const struct mach_header* loadAddress)
 
        struct dyld_all_image_infos  dyld_all_image_infos __attribute__ ((section ("__DATA,__all_image_info"))) 
                                                                = { 
-                                                                       16, 0, {NULL}, &gdb_image_notifier, false, false, (const mach_header*)&__dso_handle, NULL,
+                                                                       17, 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, {0}, "/usr/lib/dyld", {0}, {0}, 0, 0, NULL, 0
                                                                        };
 
        struct dyld_shared_cache_ranges dyld_shared_cache_ranges;
index 09dec12e82ab22ddc7151dcf16de25fd582d439c..12698b7127015e1ea58c1c0ce53123ba970059aa 100644 (file)
@@ -104,11 +104,11 @@ RemoteBuffer::map(task_t task, mach_vm_address_t remote_address, vm_size_t size,
     if (!shared) {
         void* buffer = malloc(size);
         if (buffer == nullptr) {
-            (void)vm_deallocate(mach_task_self(), localAddress, size);
-            return std::make_pair(MACH_VM_MIN_ADDRESS, kr);
+            (void)vm_deallocate(mach_task_self(), (vm_address_t)localAddress, size);
+            return std::make_pair(MACH_VM_MIN_ADDRESS, KERN_NO_SPACE);
         }
         memcpy(buffer, (void *)localAddress, size);
-        (void)vm_deallocate(mach_task_self(), localAddress, size);
+        (void)vm_deallocate(mach_task_self(), (vm_address_t)localAddress, size);
         return std::make_pair((vm_address_t)buffer, KERN_SUCCESS);
     }
     // A shared buffer was requested, if the permissions are not correct deallocate the region and return failure
@@ -195,13 +195,15 @@ struct __attribute__((visibility("hidden"))) dyld_process_info_base {
     template<typename T>
     static dyld_process_info_ptr makeSuspended(task_t task, const T& allImageInfo, kern_return_t* kr);
 
-    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); }
-    dyld_platform_t             platform() const { return _platform; }
+    std::atomic<uint32_t>&       retainCount() const { return _retainCount; }
+    dyld_process_cache_info*     cacheInfo() const { return (dyld_process_cache_info*)(((char*)this) + _cacheInfoOffset); }
+    dyld_process_aot_cache_info* aotCacheInfo() const { return (dyld_process_aot_cache_info*)(((char*)this) + _aotCacheInfoOffset); }
+    dyld_process_state_info*     stateInfo() const { return (dyld_process_state_info*)(((char*)this) + _stateInfoOffset); }
+    dyld_platform_t              platform() const { return _platform; }
 
-    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                         forEachImage(void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)) const;
+    void                         forEachAotImage(bool (^callback)(uint64_t x86Address, uint64_t aotAddress, uint64_t aotSize, uint8_t* aotImageKey, size_t aotImageKeySize)) const;
+    void                         forEachSegment(uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)) const;
 
     bool reserveSpace(size_t space) {
         if (_freeSpace < space) { return false; }
@@ -238,12 +240,14 @@ private:
         uint64_t                size;
     };
 
-                                dyld_process_info_base(dyld_platform_t platform, unsigned imageCount, size_t totalSize);
+                                dyld_process_info_base(dyld_platform_t platform, unsigned imageCount, unsigned aotImageCount, size_t totalSize);
     void*                       operator new (size_t, void* buf) { return buf; }
 
     static bool                 inCache(uint64_t addr) { return (addr > SHARED_REGION_BASE) && (addr < SHARED_REGION_BASE+SHARED_REGION_SIZE); }
     bool                        addImage(task_t task, bool sameCacheAsThisProcess, uint64_t imageAddress, uint64_t imagePath, const char* imagePathLocal);
 
+    bool                        addAotImage(dyld_aot_image_info_64 aotImageInfo);
+
     kern_return_t               addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath);
 
     bool                        invalid() { return ((char*)_stringRevBumpPtr < (char*)_curSegment); }
@@ -259,13 +263,17 @@ private:
 
     mutable std::atomic<uint32_t>            _retainCount;
     const uint32_t              _cacheInfoOffset;
+    const uint32_t              _aotCacheInfoOffset;
     const uint32_t              _stateInfoOffset;
     const uint32_t              _imageInfosOffset;
+    const uint32_t              _aotImageInfosOffset;
     const uint32_t              _segmentInfosOffset;
     size_t                      _freeSpace;
     dyld_platform_t             _platform;
     ImageInfo* const            _firstImage;
     ImageInfo*                  _curImage;
+    dyld_aot_image_info_64* const            _firstAotImage;
+    dyld_aot_image_info_64*                  _curAotImage;
     SegmentInfo* const          _firstSegment;
     SegmentInfo*                _curSegment;
     uint32_t                    _curSegmentIndex;
@@ -278,14 +286,18 @@ private:
     // char                     stringPool[]
 };
 
-dyld_process_info_base::dyld_process_info_base(dyld_platform_t platform, unsigned imageCount, size_t totalSize)
+dyld_process_info_base::dyld_process_info_base(dyld_platform_t platform, unsigned imageCount, unsigned aotImageCount, size_t totalSize)
  :  _retainCount(1), _cacheInfoOffset(sizeof(dyld_process_info_base)),
-    _stateInfoOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info)),
-    _imageInfosOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + sizeof(dyld_process_state_info)),
-    _segmentInfosOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + sizeof(dyld_process_state_info) + imageCount*sizeof(ImageInfo)),
+    _aotCacheInfoOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info)),
+    _stateInfoOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + sizeof(dyld_process_aot_cache_info)),
+    _imageInfosOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + sizeof(dyld_process_aot_cache_info) + sizeof(dyld_process_state_info)),
+    _aotImageInfosOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + sizeof(dyld_process_aot_cache_info) + sizeof(dyld_process_state_info) + imageCount*sizeof(ImageInfo)),
+    _segmentInfosOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + sizeof(dyld_process_aot_cache_info) + sizeof(dyld_process_state_info) + imageCount*sizeof(ImageInfo) + aotImageCount*sizeof(dyld_aot_image_info_64)),
     _freeSpace(totalSize), _platform(platform),
     _firstImage((ImageInfo*)(((uint8_t*)this) + _imageInfosOffset)),
     _curImage((ImageInfo*)(((uint8_t*)this) + _imageInfosOffset)),
+    _firstAotImage((dyld_aot_image_info_64*)(((uint8_t*)this) + _aotImageInfosOffset)),
+    _curAotImage((dyld_aot_image_info_64*)(((uint8_t*)this) + _aotImageInfosOffset)),
     _firstSegment((SegmentInfo*)(((uint8_t*)this) + _segmentInfosOffset)),
     _curSegment((SegmentInfo*)(((uint8_t*)this) + _segmentInfosOffset)),
     _curSegmentIndex(0),
@@ -377,9 +389,11 @@ dyld_process_info_ptr dyld_process_info_base::make(task_t task, const T1& allIma
             // allocate result object
             size_t allocationSize = sizeof(dyld_process_info_base)
                                         + sizeof(dyld_process_cache_info)
+                                        + sizeof(dyld_process_aot_cache_info)
                                         + sizeof(dyld_process_state_info)
                                         + sizeof(ImageInfo)*(imageCountWithDyld)
-                                        + sizeof(SegmentInfo)*imageCountWithDyld*5
+                                        + sizeof(dyld_aot_image_info_64)*(allImageInfo.aotInfoCount) // add the size necessary for aot info to this buffer
+                                        + sizeof(SegmentInfo)*imageCountWithDyld*10
                                         + countOfPathsNeedingCopying*PATH_MAX;
             void* storage = malloc(allocationSize);
             if (storage == nullptr) {
@@ -387,8 +401,9 @@ dyld_process_info_ptr dyld_process_info_base::make(task_t task, const T1& allIma
                 result = nullptr;
                 return;
             }
-            auto info = dyld_process_info_ptr(new (storage) dyld_process_info_base(allImageInfo.platform, imageCountWithDyld, allocationSize), deleter);
-            (void)info->reserveSpace(sizeof(dyld_process_info_base)+sizeof(dyld_process_cache_info)+sizeof(dyld_process_state_info));
+            auto info = dyld_process_info_ptr(new (storage) dyld_process_info_base(allImageInfo.platform, imageCountWithDyld, allImageInfo.aotInfoCount, allocationSize), deleter);
+            (void)info->reserveSpace(sizeof(dyld_process_info_base)+sizeof(dyld_process_cache_info)+sizeof(dyld_process_state_info)+sizeof(dyld_process_aot_cache_info));
+            (void)info->reserveSpace(sizeof(ImageInfo)*imageCountWithDyld);
 
             // fill in base info
             dyld_process_cache_info* cacheInfo = info->cacheInfo();
@@ -403,6 +418,11 @@ dyld_process_info_ptr dyld_process_info_base::make(task_t task, const T1& allIma
                 }
             }
 
+            // fill in aot shared cache info
+            dyld_process_aot_cache_info* aotCacheInfo = info->aotCacheInfo();
+            memcpy(aotCacheInfo->cacheUUID, &allImageInfo.aotSharedCacheUUID[0], 16);
+            aotCacheInfo->cacheBaseAddress = allImageInfo.aotSharedCacheBaseAddress;
+
             dyld_process_state_info* stateInfo = info->stateInfo();
             stateInfo->timestamp           = currentTimestamp;
             stateInfo->imageCount          = imageCountWithDyld;
@@ -442,6 +462,24 @@ dyld_process_info_ptr dyld_process_info_base::make(task_t task, const T1& allIma
             result = std::move(info);
         });
 
+        mach_vm_address_t aotImageArray = allImageInfo.aotInfoArray;
+        // shortcircuit this code path if aotImageArray == 0 (32 vs 64 bit struct difference)
+        // and if result == nullptr, since we need to append aot image infos to the process info struct
+        if (aotImageArray != 0 && result != nullptr) {
+            uint32_t aotImageCount = allImageInfo.aotInfoCount;
+            size_t aotImageArraySize = aotImageCount * sizeof(dyld_aot_image_info_64);
+
+            withRemoteBuffer(task, aotImageArray, aotImageArraySize, false, false, kr, ^(void *buffer, size_t size) {
+                dyld_aot_image_info_64* imageArray = (dyld_aot_image_info_64*)buffer;
+                for (uint32_t i = 0; i < aotImageCount; i++) {
+                    if (!result->addAotImage(imageArray[i])) {
+                        result = nullptr;
+                        return;
+                    }
+                }
+            });
+        }
+
         if (result) break;
     }
 
@@ -513,20 +551,25 @@ dyld_process_info_ptr dyld_process_info_base::makeSuspended(task_t task, const T
     //fprintf(stderr, "dyld: addr=0x%llX, path=%s\n", dyldAddress, dyldPathBuffer);
     //fprintf(stderr, "app:  addr=0x%llX, path=%s\n", mainExecutableAddress, mainExecutablePathBuffer);
 
+    // explicitly set aot image count to 0 in the suspended case
+    unsigned aotImageCount = 0;
+
     // allocate result object
     size_t allocationSize =   sizeof(dyld_process_info_base)
                             + sizeof(dyld_process_cache_info)
+                            + sizeof(dyld_process_aot_cache_info)
                             + sizeof(dyld_process_state_info)
                             + sizeof(ImageInfo)*(imageCount)
-                            + sizeof(SegmentInfo)*imageCount*5
+                            + sizeof(dyld_aot_image_info_64)*aotImageCount // this should always be 0, but including it here to be explicit
+                            + sizeof(SegmentInfo)*imageCount*10
                             + imageCount*PATH_MAX;
     void* storage = malloc(allocationSize);
     if (storage == nullptr) {
         *kr = KERN_NO_SPACE;
         return  nullptr;
     }
-    auto obj = dyld_process_info_ptr(new (storage) dyld_process_info_base((dyld_platform_t)platformID, imageCount, allocationSize), deleter);
-    (void)obj->reserveSpace(sizeof(dyld_process_info_base)+sizeof(dyld_process_cache_info)+sizeof(dyld_process_state_info));
+    auto obj = dyld_process_info_ptr(new (storage) dyld_process_info_base((dyld_platform_t)platformID, imageCount, aotImageCount, allocationSize), deleter);
+    (void)obj->reserveSpace(sizeof(dyld_process_info_base)+sizeof(dyld_process_cache_info)+sizeof(dyld_process_aot_cache_info)+sizeof(dyld_process_state_info));
     // fill in base info
     dyld_process_cache_info* cacheInfo = obj->cacheInfo();
     bzero(cacheInfo->cacheUUID, 16);
@@ -534,6 +577,11 @@ dyld_process_info_ptr dyld_process_info_base::makeSuspended(task_t task, const T
     cacheInfo->noCache             = true;
     cacheInfo->privateCache        = false;
 
+    // zero out aot cache info
+    dyld_process_aot_cache_info* aotCacheInfo = obj->aotCacheInfo();
+    bzero(aotCacheInfo->cacheUUID, 16);
+    aotCacheInfo->cacheBaseAddress = 0;
+
     dyld_process_state_info* stateInfo = obj->stateInfo();
     stateInfo->timestamp           = 0;
     stateInfo->imageCount          = imageCount;
@@ -594,7 +642,6 @@ const char* dyld_process_info_base::copyPath(task_t task, uint64_t stringAddress
 
 bool dyld_process_info_base::addImage(task_t task, bool sameCacheAsThisProcess, uint64_t imageAddress, uint64_t imagePath, const char* imagePathLocal)
 {
-    if (!reserveSpace(sizeof(ImageInfo))) { return false; }
     _curImage->loadAddress = imageAddress;
     _curImage->segmentStartIndex = _curSegmentIndex;
     if ( imagePathLocal != NULL ) {
@@ -618,6 +665,18 @@ bool dyld_process_info_base::addImage(task_t task, bool sameCacheAsThisProcess,
     return true;
 }
 
+bool dyld_process_info_base::addAotImage(dyld_aot_image_info_64 aotImageInfo) {
+    if (!reserveSpace(sizeof(dyld_aot_image_info_64))) {
+        return false;
+    }
+    _curAotImage->x86LoadAddress = aotImageInfo.x86LoadAddress;
+    _curAotImage->aotLoadAddress = aotImageInfo.aotLoadAddress;
+    _curAotImage->aotImageSize = aotImageInfo.aotImageSize;
+    memcpy(_curAotImage->aotImageKey, aotImageInfo.aotImageKey, sizeof(aotImageInfo.aotImageKey));
+
+    _curAotImage++;
+    return true;
+}
 
 kern_return_t dyld_process_info_base::addInfoFromRemoteLoadCommands(task_t task, uint64_t remoteMH) {
     __block kern_return_t kr = KERN_SUCCESS;
@@ -649,11 +708,6 @@ kern_return_t dyld_process_info_base::addInfoFromRemoteLoadCommands(task_t task,
 
 kern_return_t dyld_process_info_base::addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath)
 {
-    if (!reserveSpace(sizeof(ImageInfo))) {
-        // If we don't have ebnough spacee the data will be truncated, but well formed. Return success so
-        // symbolicators can try and use it
-        return KERN_SUCCESS;
-    }
     __block kern_return_t kr = KERN_SUCCESS;
     _curImage->loadAddress = dyldAddress;
     _curImage->segmentStartIndex = _curSegmentIndex;
@@ -722,7 +776,13 @@ void dyld_process_info_base::addInfoFromLoadCommands(const mach_header* mh, uint
 const char* dyld_process_info_base::copySegmentName(const char* name)
 {
     // don't copy names of standard segments into string pool
-    static const char* stdSegNames[] = {"__TEXT", "__DATA", "__LINKEDIT", "__DATA_DIRTY", "__DATA_CONST", "__OBJC", NULL };
+    static const char* stdSegNames[] = {
+        "__TEXT", "__DATA", "__LINKEDIT",
+        "__DATA_DIRTY", "__DATA_CONST",
+        "__OBJC", "__OBJC_CONST",
+        "__AUTH", "__AUTH_CONST",
+        NULL
+    };
     for (const char** s=stdSegNames; *s != NULL; ++s) {
         if ( strcmp(name, *s) == 0 )
         return *s;
@@ -738,6 +798,18 @@ void dyld_process_info_base::forEachImage(void (^callback)(uint64_t machHeaderAd
     }
 }
 
+
+#if TARGET_OS_OSX
+void dyld_process_info_base::forEachAotImage(bool (^callback)(uint64_t x86Address, uint64_t aotAddress, uint64_t aotSize, uint8_t* aotImageKey, size_t aotImageKeySize)) const
+{
+    for (const dyld_aot_image_info_64* p = _firstAotImage; p < _curAotImage; ++p) {
+        if (!callback(p->x86LoadAddress, p->aotLoadAddress, p->aotImageSize, (uint8_t*)p->aotImageKey, sizeof(p->aotImageKey))) {
+            break;
+        }
+    }
+}
+#endif
+
 void dyld_process_info_base::forEachSegment(uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)) const
 {
     for (const ImageInfo* p = _firstImage; p < _curImage; ++p) {
@@ -809,6 +881,11 @@ void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_inf
     *cacheInfo = *info->cacheInfo();
 }
 
+void _dyld_process_info_get_aot_cache(dyld_process_info info, dyld_process_aot_cache_info* aotCacheInfo)
+{
+    *aotCacheInfo = *info->aotCacheInfo();
+}
+
 void _dyld_process_info_retain(dyld_process_info object)
 {
     const_cast<dyld_process_info_base*>(object)->retain();
@@ -828,6 +905,12 @@ void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(
     info->forEachImage(callback);
 }
 
+#if TARGET_OS_OSX
+void _dyld_process_info_for_each_aot_image(dyld_process_info info, bool (^callback)(uint64_t x86Address, uint64_t aotAddress, uint64_t aotSize, uint8_t* aotImageKey, size_t aotImageKeySize))
+{
+    info->forEachAotImage(callback);
+}
+#endif
 
 void _dyld_process_info_for_each_segment(dyld_process_info info, uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName))
 {
index df3d96310c2bb34b62cd28fc22f0edc97f91a4ee..3e9e81e8bfbb1ae3d6cf42cfa549c1e93501915f 100644 (file)
@@ -67,6 +67,12 @@ struct dyld_all_image_infos_32 {
     uint32_t                        compact_dyld_image_info_addr;
     uint32_t                        compact_dyld_image_info_size;
     uint32_t                        platform;
+    // the aot fields below will not be set in the 32 bit case
+    uint32_t                        aotInfoCount;
+    std::atomic<uint64_t>           aotInfoArray;
+    uint64_t                        aotInfoArrayChangeTimestamp;
+    uint64_t                        aotSharedCacheBaseAddress;
+    std::array<uint8_t, 16>         aotSharedCacheUUID[16];
 };
 
 struct dyld_all_image_infos_64 {
@@ -102,6 +108,11 @@ struct dyld_all_image_infos_64 {
     uint64_t                compact_dyld_image_info_addr;
     uint64_t                compact_dyld_image_info_size;
     uint32_t                platform;
+    uint32_t                aotInfoCount;
+    std::atomic<uint64_t>   aotInfoArray;
+    uint64_t                aotInfoArrayChangeTimestamp;
+    uint64_t                aotSharedCacheBaseAddress;
+    std::array<uint8_t, 16> aotSharedCacheUUID[16];
 };
 
 struct dyld_image_info_32 {
@@ -115,6 +126,14 @@ struct dyld_image_info_64 {
     uint64_t                    imageFileModDate;
 };
 
+#define DYLD_AOT_IMAGE_KEY_SIZE 32
+struct dyld_aot_image_info_64 {
+    uint64_t                    x86LoadAddress;
+    uint64_t                    aotLoadAddress;
+    uint64_t                    aotImageSize;
+    uint8_t                     aotImageKey[DYLD_AOT_IMAGE_KEY_SIZE];
+};
+
 #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
index 2ee93850b199a4ebc891ad7f2ad59fbd36aca747..a2e2a4fcadb870953fd9b0089bf676a206fd08a9 100644 (file)
@@ -73,7 +73,7 @@ exit_usage(void)
     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");
+    fprintf(stderr, "dyld_usage, Terminal, telnetd, sshd, rlogind, tcsh, csh, sh\n");
 
     exit(1);
 }
@@ -346,11 +346,11 @@ public:
         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 << "\"";
+            sstr << ",\"result\":\"0x" << dlopenNode->result << "\"";
         } else if (auto dlopenPreflightNode = dynamic_cast<dlopen_preflight *>(node.get())) {
             sstr << std::hex;
             sstr << "{\"type\":\"dlopen_preflight\",\"path\":\"" << dlopenPreflightNode->path << "\"";
-            sstr << ",\"result\":\"" << dlopenPreflightNode->result << "\"";
+            sstr << ",\"result\":\"0x" << dlopenPreflightNode->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 << "\"";
@@ -729,7 +729,7 @@ main(int argc, char *argv[])
     s = ktrace_session_create();
     assert(s);
 
-    while ((ch = getopt(argc, argv, "jJeR:t:")) != -1) {
+    while ((ch = getopt(argc, argv, "hjJeR:t:")) != -1) {
         switch (ch) {
             case 'j':
                 JSON_flag = true;
@@ -756,6 +756,7 @@ main(int argc, char *argv[])
                     exit(1);
                 }
                 break;
+            case 'h':
             default:
                 exit_usage();
         }
@@ -778,7 +779,7 @@ main(int argc, char *argv[])
 
     if (!RAW_flag) {
         if (geteuid() != 0) {
-            fprintf(stderr, "'dyld_usage' must be run as root...\n");
+            fprintf(stderr, "'dyld_usage' must be run as root\n");
             exit(1);
         }
 
@@ -879,6 +880,7 @@ main(int argc, char *argv[])
         exit(1);
     }
 
+    setvbuf(stdout, (char *)NULL, _IONBF, 0);
     dispatch_main();
 
     return 0;
index 8b02b746e08f1e0b1e7da235db2e26216e0c366e..850b902aff01fda36b7794261d7fef7aee5167ed 100644 (file)
@@ -400,29 +400,6 @@ void* memset(void* b, int c, size_t len)
 }
 
 
-// <rdar://problem/10111032> wrap calls to stat() with check for EAGAIN
-int _ZN4dyld7my_statEPKcP4stat(const char* path, struct stat* buf)
-{
-       int result;
-       do {
-               result = stat(path, buf);
-    } while ((result == -1) && ((errno == EAGAIN) || (errno == EINTR)));
-
-       return result;
-}
-
-// <rdar://problem/13805025> dyld should retry open() if it gets an EGAIN
-int _ZN4dyld7my_openEPKcii(const char* path, int flag, int other)
-{
-       int result;
-       do {
-               result = open(path, flag, other);
-       } while ((result == -1) && ((errno == EAGAIN) || (errno == EINTR)));
-
-       return result;
-}
-
-
 //
 // The dyld in the iOS simulator cannot do syscalls, so it calls back to
 // host dyld.
@@ -430,9 +407,15 @@ int _ZN4dyld7my_openEPKcii(const char* path, int flag, int other)
 
 #if TARGET_OS_SIMULATOR
 
-int myopen(const char* path, int oflag, int extra) __asm("_open");
-int myopen(const char* path, int oflag, int extra) {
-       return gSyscallHelpers->open(path, oflag, extra);
+int open(const char* path, int oflag, ...) {
+       int retval;
+
+       va_list args;
+       va_start(args, oflag);
+       retval = gSyscallHelpers->open(path, oflag, va_arg(args, int));
+       va_end(args);
+
+       return retval;
 }
 
 int close(int fd) {
@@ -463,14 +446,26 @@ int stat(const char* path, struct stat* buf) {
        return gSyscallHelpers->stat(path, buf);
 }
 
-int myfcntl(int fd, int cmd, void* result) __asm("_fcntl");
-int myfcntl(int fd, int cmd, void* result) {
-       return gSyscallHelpers->fcntl(fd, cmd, result);
+int fcntl(int fd, int cmd, ...) {
+        int retval;
+
+        va_list args;
+        va_start(args, cmd);
+        retval = gSyscallHelpers->fcntl(fd, cmd, va_arg(args, void *));
+        va_end(args);
+
+       return retval;
 }
 
-int myioctl(int fd, unsigned long request, void* result) __asm("_ioctl");
-int myioctl(int fd, unsigned long request, void* result) {
-       return gSyscallHelpers->ioctl(fd, request, result);
+int ioctl(int fd, unsigned long request, ...) {
+        int retval;
+
+        va_list args;
+        va_start(args, request);
+        retval = gSyscallHelpers->ioctl(fd, request, va_arg(args, void *));
+        va_end(args);
+
+       return retval;
 }
 
 int issetugid() {
@@ -1163,3 +1158,6 @@ void uuid_unparse_upper(const uuid_t uu, uuid_string_t out)
              uu[8], uu[9],
              uu[10], uu[11], uu[12], uu[13], uu[14], uu[15]);
 }
+
+
+
index 89ee46f96dedfbe7d166e28d112e3ff7fe885d91..351bcc577b0f67c13b0cc932c2c473e1351bcbe1 100755 (executable)
@@ -10,6 +10,16 @@ Example, main.c may contain:
     // RUN:  ./example.exe
     int main() { return 0; }
 
+It is possible to restrict build and run lines to specific platforms using the BUILD(): syntax, for example
+
+    // BUILD(macos):    $CC main.c  -o $BUILD_DIR/example.exe
+    // BUILD(ios):      $CC main.c -DIOS=1 -o $BUILD_DIR/example.exe
+    // RUN(ios,macos):  ./example.exe
+    int main() { return 0; }
+
+will build example.exe with distinct options for macOS and iOS, and will invoke example.exe on both macOS and iOS (but not tvOS,
+watchOS, or bridgeOS). Valid platforms are "macos", ios", tvos", "watchos", "bridgeos".
+
 When build lines are executed, the current directory is set to the test case's .dtest dir.
 Build lines may contain the follow variables:
     $BUILD_DIR    - expands to the directory in $DSTROOT where this test case binaries are installed
@@ -40,11 +50,5 @@ run in. LOG() will capture messages in a per image queue. By default these logs
 ignored if a test succeeds. While debugging tests logs can be emitted even during success by setting the LOG_ON_SUCCESS
 environment variable. This allows us to leave logging statements in production dyld_tests.Fdtra
 
-To support tests that are platform specific, add the BUILD_ONLY: line which specifies the platform.
-Valid platforms are: MacOSX, iOS, watchOS, and tvOS.  When a specific platform is specified, a
-new min OS version can also be specified via the BUILD_MIN_OS option.  For instance:
-    // BUILD_ONLY: MacOSX
-    // BUILD_MIN_OS: 10.5
-
 Note, to run the tests as root, you need to first set "defaults write com.apple.dt.Xcode EnableRootTesting YES",
 and then check the "Debug process as root" box in the Test scheme on the ContainerizedTestRunner scheme.
index ea958c93ea979ac0ac43853a30867aea6d41d628..2eed4aa27b959d51296514f13cb0c7f8e5ae62dd 100755 (executable)
@@ -176,12 +176,13 @@ $TARGETS
                 lineSpaceAvailable = 132-(wrappedLineLeadingSpaceLen+wordLen)
         return result
 
-def processBuildLines(ninja, buildLines, testName, macOSBuild, minOS, testDstDir, testSrcDir):
+def processBuildLines(ninja, buildLines, testName, platform, osFlag, forceArchs, testDstDir, testSrcDir):
     testInstallTarget = ninja.newTarget("phony", "install-{}".format(testName))
     testTarget = ninja.newTarget("phony", testName)
     ninja.findTarget("all").addInput(testTarget)
     ninja.findTarget("install").addInput(testInstallTarget)
     for buildLine in buildLines:
+        minOS = None
         args = buildLine.split()
         if args[0] == "$DTRACE":
             target = None
@@ -195,6 +196,7 @@ def processBuildLines(ninja, buildLines, testName, macOSBuild, minOS, testDstDir
             testTarget.addInput(target)
             installTarget = ninja.newTarget("install", "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/{}".format(args[2][9:]))
             installTarget.addInput(target.output)
+            installTarget.addVariable("mode", "0644")
             testInstallTarget.addInput(installTarget)
         elif args[0] == "$SYMLINK":
             target = ninja.newTarget("symlink", args[2])
@@ -211,11 +213,11 @@ def processBuildLines(ninja, buildLines, testName, macOSBuild, minOS, testDstDir
             ninja.deleteTarget(target)
             testInstallTarget.inputs.remove(target)
         elif args[0] == "$DYLD_ENV_VARS_ENABLE":
-            if not macOSBuild:
+            if platform != "macos":
                 target = ninja.findTarget(args[1])
                 target.addVariable("entitlements", "--entitlements $SRCROOT/testing/get_task_allow_entitlement.plist")
         elif args[0] == "$TASK_FOR_PID_ENABLE":
-            if not macOSBuild:
+            if platform != "macos":
                 target = ninja.findTarget(args[1])
                 target.addVariable("entitlements", "--entitlements $SRCROOT/testing/task_for_pid_entitlement.plist")
         elif args[0] in ["$CC", "$CXX"]:
@@ -226,7 +228,8 @@ def processBuildLines(ninja, buildLines, testName, macOSBuild, minOS, testDstDir
             dependencies = []
             skipCount = 0
             linkTarget = None
-            isMainExecutable = True
+            linkTestSupport = True
+            platformVersion = None
             targetNames = [target.output for target in ninja.targets]
             args = [escapedArg.replace("\"", "\\\"") for escapedArg in args[1:]]
             #First find the target
@@ -251,6 +254,13 @@ def processBuildLines(ninja, buildLines, testName, macOSBuild, minOS, testDstDir
                     ldflags.append(nextArg)
                     cflags.append(arg)
                     cflags.append(nextArg)
+                elif arg in ["-target"]:
+                    skipCount = 1
+                    nextArg = args[idx+1]
+                    ldflags.append(arg)
+                    ldflags.append(nextArg)
+                    cflags.append(arg)
+                    cflags.append(nextArg)
                 elif arg in ["-install_name","-framework", "-rpath","-compatibility_version","-sub_library", "-undefined", "-current_version"]:
                     skipCount = 1
                     nextArg = args[idx+1]
@@ -263,19 +273,32 @@ def processBuildLines(ninja, buildLines, testName, macOSBuild, minOS, testDstDir
                     ldflags.append(args[idx+2])
                     ldflags.append(args[idx+3])
                 elif arg[:2] == "-L": ldflags.append(arg)
-                elif arg in ["-nostdlib", "-flat_namespace"]: ldflags.append(arg)
+                elif arg[:2] == "-F": ldflags.append(arg)
+                elif arg == "-nostdlib":
+                    ldflags.append(arg)
+                    # Kernel tests pass -nostdlib so don't link test support
+                    linkTestSupport = False
+                elif arg == "-flat_namespace":
+                    ldflags.append(arg)
                 elif arg in ["-dynamiclib","-bundle"]:
                     ldflags.append(arg)
-                    isMainExecutable = False
+                    linkTestSupport = False
                 elif arg.endswith((".s", ".c", ".cpp", ".cxx", ".m", ".mm")):
-                    sources.append(testSrcDir + "/" +arg)
+                    if not arg.startswith("$SRCROOT"): sources.append(testSrcDir + "/" + arg)
+                    else: sources.append(arg)
                 elif arg in targetNames:
                     linkTarget.addInput(arg)
+                elif osFlag in arg:
+                    minOS = arg[arg.find('=')+1:]
                 elif arg[:4] == "-Wl,":
                     linkerArgs = arg.split(",")
-                    for linkerArg in linkerArgs:
-                        if linkerArg in targetNames: linkTarget.addDependency(linkerArg)
-                    ldflags.append(arg)
+                    if linkerArgs[1] == "-platform_version":
+                        minOS = linkerArgs[3]
+                        platformVersion = arg
+                    else:
+                        for linkerArg in linkerArgs[1:]:
+                            if linkerArg in targetNames: linkTarget.addDependency(linkerArg)
+                        ldflags.append(arg)
                 elif arg[:2] == "-l":
                     candidate = "{}/lib{}.dylib".format(testDstDir, arg[2:])
                     if candidate in targetNames: linkTarget.addDependency(candidate)
@@ -288,9 +311,12 @@ def processBuildLines(ninja, buildLines, testName, macOSBuild, minOS, testDstDir
                     candidate = "{}/lib{}.dylib".format(testDstDir, arg[9:])
                     if candidate in targetNames: linkTarget.addDependency(candidate)
                     ldflags.append(arg)
+                elif arg[:8] == "-fuse-ld":
+                    # This is not typically used, but if we ever wanted to try a new ld64, it can be useful
+                    ldflags.append(arg)
                 else:
                     cflags.append(arg)
-            if isMainExecutable:
+            if linkTestSupport:
                 ldflags.append("-force_load $BUILT_PRODUCTS_DIR/libtest_support.a")
             for source in sources:
                 objectHash = hashlib.sha1(linkTarget.output+source+tool+"".join(cflags)).hexdigest()
@@ -298,53 +324,137 @@ def processBuildLines(ninja, buildLines, testName, macOSBuild, minOS, testDstDir
                 target.addInput(source)
                 target.dependencies = dependencies
                 if cflags: target.addVariable("cflags", " ".join(cflags))
-                if minOS: target.addVariable("minOS", minOS)
+                if minOS: target.addVariable("minOS", "-" + osFlag + "=" + minOS)
+                if forceArchs: target.addVariable("archs", " ".join(["-arch {}".format(arch) for arch in forceArchs]))
                 linkTarget.addInput(target)
             if ldflags: linkTarget.addVariable("ldflags", " ".join(ldflags))
-            if minOS: linkTarget.addVariable("minOS", minOS)
+            if platformVersion: linkTarget.addVariable("minOS", platformVersion)
+            elif minOS: linkTarget.addVariable("minOS", "-" + osFlag + "=" + minOS)
+            if forceArchs: linkTarget.addVariable("archs", " ".join(["-arch {}".format(arch) for arch in forceArchs]))
+            installTarget = ninja.newTarget("install", "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/{}".format(linkTarget.output[9:]))
+            installTarget.addInput(linkTarget)
+            testTarget.addInput(linkTarget)
+            testInstallTarget.addInput(installTarget)
+        elif args[0] == "$APP_CACHE_UTIL":
+            tool = args[0][1:].lower()
+            sources = []
+            flags = []
+            dependencies = []
+            skipCount = 0
+            linkTarget = None
+            args = [escapedArg.replace("\"", "\\\"") for escapedArg in args[1:]]
+            skipCount = 0
+            for idx, arg in enumerate(args):
+                if skipCount: skipCount -= 1
+                elif arg == "$DEPENDS_ON":
+                    skipCount = 1
+                    dependencies.append(args[idx+1])
+                elif arg == "-create-kernel-collection":
+                    skipCount = 1
+                    linkTarget = ninja.newTarget("app-cache-util", args[idx+1])
+                    linkTarget.addVariable("create_kind", arg)
+                    testTarget.addInput(linkTarget)
+                    dependencies.append("$BUILT_PRODUCTS_DIR/host_tools/dyld_app_cache_util")
+                elif arg == "-kernel":
+                    skipCount = 1
+                    linkTarget.addInput(args[idx+1])
+                    flags.append(arg)
+                    flags.append(args[idx+1])
+                elif arg == "-create-aux-kernel-collection":
+                    skipCount = 1
+                    linkTarget = ninja.newTarget("app-cache-util", args[idx+1])
+                    linkTarget.addVariable("create_kind", arg)
+                    testTarget.addInput(linkTarget)
+                    dependencies.append("$BUILT_PRODUCTS_DIR/host_tools/dyld_app_cache_util")
+                elif arg == "-create-pageable-kernel-collection":
+                    skipCount = 1
+                    linkTarget = ninja.newTarget("app-cache-util", args[idx+1])
+                    linkTarget.addVariable("create_kind", arg)
+                    testTarget.addInput(linkTarget)
+                    dependencies.append("$BUILT_PRODUCTS_DIR/host_tools/dyld_app_cache_util")
+                elif arg == "-kernel-collection":
+                    skipCount = 1
+                    linkTarget.addInput(args[idx+1])
+                    flags.append(arg)
+                    flags.append(args[idx+1])
+                elif arg == "-pageable-collection":
+                    skipCount = 1
+                    linkTarget.addInput(args[idx+1])
+                    flags.append(arg)
+                    flags.append(args[idx+1])
+                else:
+                    flags.append(arg)
+            linkTarget.dependencies = dependencies
+            if flags: linkTarget.addVariable("flags", " ".join(flags))
+            if forceArchs: linkTarget.addVariable("archs", " ".join(["-arch {}".format(arch) for arch in forceArchs]))
             installTarget = ninja.newTarget("install", "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/{}".format(linkTarget.output[9:]))
             installTarget.addInput(linkTarget)
             testTarget.addInput(linkTarget)
             testInstallTarget.addInput(installTarget)
         else: raise ValueError("Unknown Command: {}".format(args[0]))
         
-def processRunLines(ninja, runLines, testName, macOSBuild, symRoot, xcTestInvocations):
+
+def processRunLine(runFile, runLine, environment):
+    if runLine.startswith("sudo "):
+        runFile.write("sudo {} {}\n".format(environment, runLine[5:]))
+    else:
+        runFile.write("{} {}\n".format(environment, runLine))
+
+def processRunLines(ninja, runLines, testName, platform, runStatic, symRoot, xcTestInvocations):
     runFilePath = "{}/{}/run.sh".format(symRoot, testName)
     for runLine in runLines:
         xcTestInvocations.append("{{ \"{}\", \"{}\" }}".format(testName, runLine.replace("\"","\\\"").replace("sudo","")))
     with BufferedFile(runFilePath) as runFile:
         runFile.write("#!/bin/sh\n")
         runFile.write("cd  {}\n".format(testRunDir))
-        
-        runFile.write("echo \"run in dyld2 mode\" \n");
-        for runLine in runLines:
-            runFile.write("TEST_OUTPUT=BATS TEST_DYLD_MODE=2 DYLD_USE_CLOSURES=0 {}\n".format(runLine))
 
-        if macOSBuild:
-            runFile.write("echo \"run in dyld2 mode with no shared cache\" \n");
+        if runStatic:
+            runFile.write("echo \"run static\" \n");
+            for runLine in runLines:
+                processRunLine(runFile, runLine, "TEST_OUTPUT=BATS")
+        else:
+            runFile.write("echo \"run in dyld2 mode\" \n");
             for runLine in runLines:
-                runFile.write("TEST_OUTPUT=BATS TEST_DYLD_MODE=2 DYLD_SHARED_REGION=avoid {}\n".format(runLine))
+                processRunLine(runFile, runLine, "TEST_OUTPUT=BATS DYLD_USE_CLOSURES=0")
 
-        runFile.write("echo \"run in dyld3 mode\" \n");
-        for runLine in runLines:
-            if runLine.startswith("sudo "):
-                runFile.write("sudo TEST_OUTPUT=BATS TEST_DYLD_MODE=3 DYLD_USE_CLOSURES=1 {}\n".format(runline[5:]))
-            else:
-                runFile.write("TEST_OUTPUT=BATS TEST_DYLD_MODE=3 DYLD_USE_CLOSURES=1 {}\n".format(runLine))
+            runFile.write("echo \"run in dyld3 mode\" \n");
+            for runLine in runLines:
+                processRunLine(runFile, runLine, "TEST_OUTPUT=BATS DYLD_USE_CLOSURES=1")
 
-        if macOSBuild:
-            runFile.write("echo \"run in dyld3 mode with no shared cache\" \n");
+            runFile.write("echo \"run in dyld3s mode\" \n");
             for runLine in runLines:
                 if runLine.startswith("sudo "):
-                    runFile.write("sudo TEST_OUTPUT=BATS TEST_DYLD_MODE=3 DYLD_SHARED_REGION=avoid DYLD_USE_CLOSURES=1 {}\n".format(runline[5:]))
+                    runFile.write("sudo TEST_OUTPUT=BATS DYLD_USE_CLOSURES=2 {}\n".format(runLine[5:]))
                 else:
-                    runFile.write("TEST_OUTPUT=BATS TEST_DYLD_MODE=3 DYLD_SHARED_REGION=avoid DYLD_USE_CLOSURES=1 {}\n".format(runLine))
+                    runFile.write("TEST_OUTPUT=BATS DYLD_USE_CLOSURES=2 {}\n".format(runLine))
     os.chmod(runFilePath, 0755)
     installPath = "$INSTALL_DIR/AppleInternal/CoreOS/tests/dyld/{}/run.sh".format(testName)
     target = ninja.newTarget("install", installPath)
     target.addInput(runFilePath)
     ninja.findTarget("install-{}".format(testName)).addInput(installPath)
 
+# returns a tuple of:
+# 1. Idx after end of directive
+# 2. Set of platforms directive is restricted to
+# 3. Bool indicatin if the directive has a platform specifier
+def parseDirective(line, directive, platform, archs):
+    idx = string.find(line, directive)
+    if idx == -1: return -1, archs, False
+    if line[idx + len(directive)] == ':': return idx+len(directive)+1, archs, False
+    match = re.match("\((.*?)\)?(?:\|(.*?))?\:", line[idx + len(directive):]);
+    if match:
+        foundPlatform = False
+        platforms = []
+        restrictedArchs = []
+        if match.group(1):
+            foundPlatform = True
+            platforms = match.group(1).split(",");
+        if match.group(2): restrictedArchs = match.group(2).split(",");
+        if platforms and platform not in platforms: return -1, archs, foundPlatform
+        effectiveArchs = list(set(archs) & set(restrictedArchs))
+        if effectiveArchs: return idx + len(directive) + len(match.group()), effectiveArchs, foundPlatform
+        return len(line), archs, foundPlatform
+    return -1, archs, False
 
 if __name__ == "__main__":
     configPath = sys.argv[1]
@@ -388,13 +498,23 @@ if __name__ == "__main__":
     headerPaths = " -isysroot " + sdkRoot
     for headerPath in userHeaderSearchPaths:  headerPaths += " -I{}".format(headerPath)
     for headerPath in systemHeaderSearchPaths:  headerPaths += " -I{}".format(headerPath)
-    macOSBuild = False
     sudoCmd = ""
+    platform = ""
     if osFlag == "mmacosx-version-min":
-        macOSBuild = True
+        platform = "macos"
         sudoCmd = "sudo"
+    elif osFlag == "miphoneos-version-min": platform = "ios"
+    elif osFlag == "mtvos-version-min": platform = "tvos"
+    elif osFlag == "mwatchos-version-min": platform = "watchos"
+    elif osFlag == "mbridgeos-version-min": platform = "bridgeos"
+    else:
+        sys.stderr.write("Unknown platform\n")
+        sys.exit(-1)
 
     with NinjaFile(derivedFilesDir + "/build.ninja") as ninja:
+        extraCmds = "$extraCmds"
+        if "RC_XBS" in os.environ and os.environ["RC_XBS"] == "YES":
+            extraCmds = "&& dsymutil -o $out.dSYM $out $extraCmds"
         ninja.addInclude("config.ninja")
         ninja.addVariable("minOS", "-" + osFlag + "=" + osVers)
         ninja.addVariable("archs", " ".join(["-arch {}".format(arch) for arch in archs]))
@@ -403,8 +523,9 @@ if __name__ == "__main__":
 
         ninja.addRule("cc", "{} -g -MMD -MF $out.d $archs -o $out -c $in $minOS $headerpaths $cflags".format(ccTool), "$out.d")
         ninja.addRule("cxx", "{} -g -MMD -MF $out.d $archs -o $out -c $in $minOS $headerpaths $cflags".format(cxxTool), "$out.d")
-        ninja.addRule("cc-link", "{}  -g $archs -o $out -ltest_support $in $minOS -isysroot {} {} $ldflags && dsymutil -o $out.dSYM $out $extraCmds && codesign --force --sign - $entitlements $out".format(ccTool, sdkRoot, linkerFlags), False)
-        ninja.addRule("cxx-link", "{}  -g $archs -o $out -ltest_support $in $minOS -isysroot {} {} $ldflags && dsymutil -o $out.dSYM $out $extraCmds && codesign --force --sign - $entitlements $out".format(cxxTool, sdkRoot, linkerFlags), False)
+        ninja.addRule("cc-link", "{}  -g $archs -o $out -ltest_support $in $minOS -isysroot {} {} $ldflags {} && codesign --force --sign - $entitlements $out".format(ccTool, sdkRoot, linkerFlags, extraCmds), False)
+        ninja.addRule("cxx-link", "{}  -g $archs -o $out -ltest_support $in $minOS -isysroot {} {} $ldflags {} && codesign --force --sign - $entitlements $out".format(cxxTool, sdkRoot, linkerFlags, extraCmds), False)
+        ninja.addRule("app-cache-util", "$BUILT_PRODUCTS_DIR/host_tools/dyld_app_cache_util $archs $create_kind $out $flags", False)
         ninja.addRule("dtrace", "/usr/sbin/dtrace -h -s $in -o $out", False)
         ninja.addRule("cp", "/bin/cp -p $in $out", False)
         ninja.addRule("install", "/usr/bin/install -m $mode -o {} -g {} $install_flags $in $out".format(installOwner, installGroup), False)
@@ -420,19 +541,28 @@ if __name__ == "__main__":
         batsSuppressedCrashes = []
         xctestInvocations = []
         with BufferedFile(runAllScriptPath) as runAllScript:
+            missingPlatformDirectives = False
             runAllScript.write("#!/bin/sh\n")
             for entry in os.listdir(testSrcRoot):
                 if entry.endswith((".dtest")):
                     testName = entry[:-6]
                     sys.stdout.write("Processing " + testName + "\n")
                     runLines = []
+                    runStaticLines = []
                     buildLines = []
-                    minOS = None
 
+                    testSrcDir = "$SRCROOT/testing/test-cases/{}.dtest".format(testName)
+                    testDstDir = "$SYMROOT/{}".format(testName)
+                    testRunDir = "/AppleInternal/CoreOS/tests/dyld/{}".format(testName)
+
+                    batsTest = {}
+                    batsTest["TestName"] = testName
+                    batsTest["Arch"] = "platform-native"
+                    batsTest["WorkingDirectory"] = testRunDir
+                    batsTest["ShowSubtestResults"] = True
+                    batsTest["Command"] = []
+                    batsTest["Command"].append("./run.sh")
                     for file in os.listdir(testSrcRoot + "/" + entry):
-                        testSrcDir = "$SRCROOT/testing/test-cases/{}.dtest".format(testName)
-                        testDstDir = "$SYMROOT/{}".format(testName)
-                        testRunDir = "/AppleInternal/CoreOS/tests/dyld/{}".format(testName)
                         buildSubs = {
                             "BUILD_DIR":            testDstDir,
                             "RUN_DIR":              testRunDir,
@@ -441,52 +571,53 @@ if __name__ == "__main__":
                         runSubs = {
                             "RUN_DIR":        testRunDir,
                             "SUDO":           sudoCmd,
+                            "RUN_STATIC":     "/AppleInternal/CoreOS/tests/dyld/run-static",
                         }
-                        batsTest = {}
-                        batsTest["TestName"] = testName
-                        batsTest["Arch"] = "platform-native"
-                        batsTest["WorkingDirectory"] = testRunDir
-                        batsTest["ShowSubtestResults"] = True
-                        batsTest["Command"] = []
-                        batsTest["Command"].append("./run.sh")
                         if file.endswith((".c", ".cpp", ".cxx", ".m", ".mm")):
                             with open(testSrcRoot + "/" + entry + "/" + file) as f:
+                                requiresPlatformDirective = False
+                                foundPlatformDirective = False
                                 for line in f.read().splitlines():
-                                    idx = string.find(line,"BUILD_ONLY:")
-                                    if idx != -1:
-                                        skippedOS = line[idx+11:].lstrip()
-                                        if skippedOS == "MacOSX" and not macOSBuild: break
-                                        else: continue
-                                    idx = string.find(line,"BUILD_MIN_OS:")
-                                    if idx != -1:
-                                        minOS = "-" + osFlag + "=" + line[idx+13:].lstrip()
-                                    idx = string.find(line,"BUILD:")
+                                    idx, forceArchs, foundPlatform = parseDirective(line, "BUILD", platform, archs);
+                                    if foundPlatform: requiresPlatformDirective = True
                                     if idx != -1:
-                                        buildLines.append(string.Template(line[idx+6:]).safe_substitute(buildSubs))
+                                        foundPlatformDirective = True
+                                        if line[idx:]: buildLines.append(string.Template(line[idx:]).safe_substitute(buildSubs))
                                         continue
-                                    idx = string.find(line,"RUN:")
+                                    idx, _, _ = parseDirective(line, "RUN", platform, archs);
                                     if idx != -1:
                                         if "$SUDO" in line: batsTest["AsRoot"] = True
-                                        runLines.append(string.Template(line[idx+4:]).safe_substitute(runSubs))
+                                        runLines.append(string.Template(line[idx:]).safe_substitute(runSubs).lstrip())
+                                        continue
+                                    idx, _, _ = parseDirective(line,"RUN_STATIC", platform, archs)
+                                    if idx != -1:
+                                        runStaticLines.append(string.Template(line[idx:]).safe_substitute(runSubs).lstrip())
                                         continue
-                                    idx = string.find(line,"RUN_TIMEOUT:")
+                                    idx, _, _ = parseDirective(line,"RUN_TIMEOUT", platform, archs)
                                     if idx != -1:
-                                        batsTest["Timeout"] = line[idx+12:].lstrip()
+                                        batsTest["Timeout"] = line[idx:].lstrip()
                                         continue
-                                    idx = string.find(line,"BOOT_ARGS:")
+                                    idx, _, _ = parseDirective(line,"BOOT_ARGS", platform, archs)
                                     if idx != -1:
-                                        batsTest["BootArgsSet"] = ",".join(line[idx+9:].split())
+                                        batsTest["BootArgsSet"] = ",".join(line[idx:].split())
                                         continue
-                                    idx = string.find(line,"NO_CRASH_LOG:")
+                                    idx, _, _ = parseDirective(line,"NO_CRASH_LOG", platform, archs)
                                     if idx != -1:
-                                        batsSuppressedCrashes.append(line[idx+13:].lstrip())
+                                        batsSuppressedCrashes.append(line[idx:].lstrip())
                                         continue
-                    if buildLines and runLines:
-                        processBuildLines(ninja, buildLines, testName, macOSBuild, minOS, testDstDir, testSrcDir)
-                        processRunLines(ninja, runLines, testName, macOSBuild, symRoot, xctestInvocations)
+                                if requiresPlatformDirective and not foundPlatformDirective:
+                                    missingPlatformDirectives = True
+                                    sys.stderr.write("Did not find platform({}) BUILD directive for {}\n".format(platform, testName))
+                    if buildLines and (runLines or runStaticLines):
+                        processBuildLines(ninja, buildLines, testName, platform, osFlag, forceArchs, testDstDir, testSrcDir)
+                        if runLines:
+                            processRunLines(ninja, runLines, testName, platform, False, symRoot, xctestInvocations)
+                        if runStaticLines:
+                            processRunLines(ninja, runStaticLines, testName, platform, True, symRoot, xctestInvocations)
                         runAllScript.write("/AppleInternal/CoreOS/tests/dyld/{}/run.sh\n".format(testName))
                         batsTests.append(batsTest)
-            sys.stderr.write("Wrote test config to: {}".format(xctestPath))
+            if missingPlatformDirectives: sys.exit(-1)
+            sys.stderr.write("Wrote test config to: {}\n".format(xctestPath))
             with BufferedFile(xctestPath) as xcTestFile:
                 xcTestFile.write("static const TestInfo sTests[] = {\n")
                 xcTestFile.write(",\n".join(xctestInvocations))
index d0541540806c71647065bab8aa7eef0b8a4aac30..ae5a45dce5aa37152e4314cd93d247f79478d479 100644 (file)
@@ -48,6 +48,27 @@ private:
     bool suspended;
     bool async;
 };
+
+#define STDERR_WRITER ^(int fd) {           \
+    char buffer[16384];                     \
+    ssize_t size = 0;                       \
+    do {                                    \
+        size = read(fd, &buffer[0], 16384); \
+        buffer[size] = 0;                   \
+        fprintf(stderr, "%s", &buffer[0]);  \
+    } while (size > 0);                     \
+}
+
+#define STDOUT_WRITER ^(int fd) {           \
+    char buffer[16384];                     \
+    ssize_t size = 0;                       \
+    do {                                    \
+        size = read(fd, &buffer[0], 16384); \
+        buffer[size] = 0;                   \
+        fprintf(stdout, "%s", &buffer[0]);  \
+    } while (size > 0);                     \
+}
+
 #endif /* __cplusplus */
 
 #define PASS(...)           _PASS(__FILE__,__LINE__,__VA_ARGS__)
diff --git a/testing/kernel-cache-tests/KernelCollection.py b/testing/kernel-cache-tests/KernelCollection.py
new file mode 100644 (file)
index 0000000..cf7ea08
--- /dev/null
@@ -0,0 +1,206 @@
+#!/usr/bin/python2.7
+
+import string
+import os
+import json
+import sys
+import commands
+import subprocess
+
+
+class KernelCollection:
+
+    def __init__(self):
+        self.print_json=False
+
+    def __init__(self, print_json):
+        self.print_json = print_json
+
+    def buildKernelCollection(self, arch_flag, kernel_cache_path, kernel_path, extensions_dir, bundle_ids=[], options=[]):
+        try:
+            test_root = os.path.dirname(__file__)
+            build_root = os.path.realpath(os.path.dirname(__file__) + "/..")
+            app_cache_util = build_root + "/../build/Release/dyld_app_cache_util"
+            args = [app_cache_util,
+                    "-create-kernel-collection", test_root + kernel_cache_path,
+                    "-kernel", test_root + kernel_path,
+                    "-arch", arch_flag]
+            if extensions_dir is not None:
+                args.append("-extensions")
+                args.append(test_root + extensions_dir)
+            for bundle_id in bundle_ids:
+                args.append("-bundle-id")
+                args.append(bundle_id)
+            file_text = subprocess.check_output(["file", build_root + kernel_cache_path]);
+            for opt in options:
+                args.append(opt.replace("$PWD", test_root))
+
+            runline = ""
+            for arg in args:
+                if not arg:
+                    runline = runline + '"' + arg + '"' + ' '
+                else:
+                    runline = runline + arg + ' '
+
+            self.dict = {}
+            if self.print_json:
+                print "Run with: " + runline
+            process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+            self.json_text, self.error_message = process.communicate()
+            if self.print_json:
+                print self.json_text
+                print self.error_message
+            if process.returncode:
+                if not self.print_json:
+                    print self.error_message
+                print "Non-zero return code"
+                print "Run with: " + runline
+                sys.exit(0)
+            #print self.json_text
+            #print self.error_message
+            if self.json_text:
+                self.dict = json.loads(self.json_text)
+                self.error_message = ""
+        except subprocess.CalledProcessError as e:
+            #print "can't make closure for " + kernel_cache_path
+            self.error_message = e.output
+            self.dict = {}
+        except:
+            assert False
+            self.dict = {}
+
+    def buildPageableKernelCollection(self, arch_flag, aux_kernel_cache_path, kernel_cache_path, extensions_dir, bundle_ids=[], options=[]):
+        try:
+            test_root = os.path.dirname(__file__)
+            build_root = os.path.realpath(os.path.dirname(__file__) + "/..")
+            app_cache_util = build_root + "/../build/Release/dyld_app_cache_util"
+            args = [app_cache_util,
+                    "-create-pageable-kernel-collection", test_root + aux_kernel_cache_path,
+                    "-kernel-collection", test_root + kernel_cache_path,
+                    "-arch", arch_flag]
+            if extensions_dir is not None:
+                args.append("-extensions")
+                args.append(test_root + extensions_dir)
+            for bundle_id in bundle_ids:
+                args.append("-bundle-id")
+                args.append(bundle_id)
+            file_text = subprocess.check_output(["file", build_root + kernel_cache_path]);
+            for opt in options:
+                args.append(opt)
+            self.dict = {}
+            if self.print_json:
+                print "Run with: " + ' '.join(args)
+            process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+            self.json_text, self.error_message = process.communicate()
+            if self.print_json:
+                print self.json_text
+                print self.error_message
+            if process.returncode:
+                if not self.print_json:
+                    print self.error_message
+                print "Non-zero return code"
+                print "Run with: " + ' '.join(args)
+                sys.exit(0)
+            #print self.json_text
+            #print self.error_message
+            if self.json_text:
+                self.dict = json.loads(self.json_text)
+                self.error_message = ""
+        except subprocess.CalledProcessError as e:
+            #print "can't make closure for " + kernel_cache_path
+            self.error_message = e.output
+            self.dict = {}
+        except:
+            assert False
+            self.dict = {}
+
+    def buildAuxKernelCollection(self, arch_flag, aux_kernel_cache_path, kernel_cache_path, pageable_cache_path, extensions_dir, bundle_ids=[], options=[]):
+        try:
+            test_root = os.path.dirname(__file__)
+            build_root = os.path.realpath(os.path.dirname(__file__) + "/..")
+            app_cache_util = build_root + "/../build/Release/dyld_app_cache_util"
+            args = [app_cache_util,
+                    "-create-aux-kernel-collection", test_root + aux_kernel_cache_path,
+                    "-kernel-collection", test_root + kernel_cache_path,
+                    "-arch", arch_flag]
+            if pageable_cache_path:
+                args.append("-pageable-collection")
+                args.append(test_root + pageable_cache_path)
+            if extensions_dir is not None:
+                args.append("-extensions")
+                args.append(test_root + extensions_dir)
+            for bundle_id in bundle_ids:
+                args.append("-bundle-id")
+                args.append(bundle_id)
+            file_text = subprocess.check_output(["file", build_root + kernel_cache_path]);
+            for opt in options:
+                args.append(opt)
+            self.dict = {}
+            if self.print_json:
+                print "Run with: " + ' '.join(args)
+            process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+            self.json_text, self.error_message = process.communicate()
+            if self.print_json:
+                print self.json_text
+                print self.error_message
+            if process.returncode:
+                if not self.print_json:
+                    print self.error_message
+                print "Non-zero return code"
+                print "Run with: " + ' '.join(args)
+                sys.exit(0)
+            #print self.json_text
+            #print self.error_message
+            if self.json_text:
+                self.dict = json.loads(self.json_text)
+                self.error_message = ""
+        except subprocess.CalledProcessError as e:
+            #print "can't make closure for " + kernel_cache_path
+            self.error_message = e.output
+            self.dict = {}
+        except:
+            assert False
+            self.dict = {}
+
+    def analyze(self, app_cache_path, options=[]):
+        try:
+            test_root = os.path.dirname(__file__)
+            build_root = os.path.realpath(os.path.dirname(__file__) + "/..")
+            app_cache_util = build_root + "/../build/Release/dyld_app_cache_util"
+            args = [app_cache_util, "-app-cache", test_root + app_cache_path, "-platform", "kernel"]
+            file_text = subprocess.check_output(["file", build_root + app_cache_path]);
+            for opt in options:
+                args.append(opt)
+            self.dict = {}
+            if self.print_json:
+                print "Run with: " + ' '.join(args)
+            process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+            self.json_text, self.error_message = process.communicate()
+            if self.print_json:
+                print self.json_text
+                print self.error_message
+            if process.returncode:
+                if not self.print_json:
+                    print self.error_message
+                print "Non-zero return code"
+                print "Run with: " + ' '.join(args)
+                sys.exit(0)
+            #print self.json_text
+            #print self.error_message
+            if self.json_text:
+                self.dict = json.loads(self.json_text)
+                self.error_message = ""
+        except subprocess.CalledProcessError as e:
+            #print "can't make closure for " + app_cache_path
+            self.error_message = e.output
+            self.dict = {}
+        except:
+            assert False
+            self.dict = {}
+
+    def dictionary(self):
+        return self.dict
+
+    def error(self):
+        return self.error_message
+
diff --git a/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/bar.c b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/bar.c
new file mode 100644 (file)
index 0000000..2ad0d8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/bar.kext/bar b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..7eafc00
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..cd1fa31
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/foo.c b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/foo.c
new file mode 100644 (file)
index 0000000..5a6eec2
--- /dev/null
@@ -0,0 +1,4 @@
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/main.c b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/main.kernel b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/main.kernel differ
diff --git a/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/test.py b/testing/kernel-cache-tests/auxkc-bind-to-pageablekc/test.py
new file mode 100644 (file)
index 0000000..d5c61e3
--- /dev/null
@@ -0,0 +1,35 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that a kext can bind to another kext
+# foo.kext exports foo and bar.kext uses it
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/auxkc-bind-to-pageablekc/main.kc", "/auxkc-bind-to-pageablekc/main.kernel", "", [], [])
+    kernel_cache.analyze("/auxkc-bind-to-pageablekc/main.kc", ["-layout", "-arch", "arm64"])
+
+    # Now build an pageable cache using the baseline kernel collection
+    kernel_cache.buildPageableKernelCollection("arm64", "/auxkc-bind-to-pageablekc/pageable.kc", "/auxkc-bind-to-pageablekc/main.kc", "/auxkc-bind-to-pageablekc/extensions", ["com.apple.foo"], [])
+    kernel_cache.analyze("/auxkc-bind-to-pageablekc/pageable.kc", ["-symbols", "-arch", "arm64"])
+
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][0]["name"] == "_foo"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][0]["vmAddr"] == "0x8000"
+
+    # Now build an aux cache using the baseline and pageable kernel collections
+    kernel_cache.buildAuxKernelCollection("arm64", "/auxkc-bind-to-pageablekc/aux.kc", "/auxkc-bind-to-pageablekc/main.kc", "/auxkc-bind-to-pageablekc/pageable.kc", "/auxkc-bind-to-pageablekc/extensions", ["com.apple.bar"], [])
+    kernel_cache.analyze("/auxkc-bind-to-pageablekc/aux.kc", ["-fixups", "-arch", "arm64"])
+
+    # bar.kext
+    assert len(kernel_cache.dictionary()["fixups"]) == 1
+    # extern int foo()
+    assert kernel_cache.dictionary()["fixups"]["0x4000"] == "kc(1) + 0x8000"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/auxkc-branch-fixups/bar.c b/testing/kernel-cache-tests/auxkc-branch-fixups/bar.c
new file mode 100644 (file)
index 0000000..2ad0d8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/bar.kext/bar b/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..9aca68e
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..fd1bc1a
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-branch-fixups/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/auxkc-branch-fixups/foo.c b/testing/kernel-cache-tests/auxkc-branch-fixups/foo.c
new file mode 100644 (file)
index 0000000..5a6eec2
--- /dev/null
@@ -0,0 +1,4 @@
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/auxkc-branch-fixups/main.c b/testing/kernel-cache-tests/auxkc-branch-fixups/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-branch-fixups/main.kernel b/testing/kernel-cache-tests/auxkc-branch-fixups/main.kernel
new file mode 100755 (executable)
index 0000000..cdc3eba
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-branch-fixups/main.kernel differ
diff --git a/testing/kernel-cache-tests/auxkc-branch-fixups/test.py b/testing/kernel-cache-tests/auxkc-branch-fixups/test.py
new file mode 100644 (file)
index 0000000..f5d864f
--- /dev/null
@@ -0,0 +1,21 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Check that we can link x86_64 kext's with branch relocations
+
+def check(kernel_cache):
+    # First build a kernel collection
+    kernel_cache.buildKernelCollection("x86_64", "/auxkc-branch-fixups/main.kc", "/auxkc-branch-fixups/main.kernel", "/auxkc-branch-fixups/extensions", [], [])
+    kernel_cache.analyze("/auxkc-branch-fixups/main.kc", ["-layout", "-arch", "x86_64"])
+
+    # Now build an aux cache using the baseline kernel collection
+    kernel_cache.buildAuxKernelCollection("x86_64", "/auxkc-branch-fixups/aux.kc", "/auxkc-branch-fixups/main.kc", "", "/auxkc-branch-fixups/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/auxkc-branch-fixups/aux.kc", ["-layout", "-arch", "x86_64"])
+
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__HIB,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -Wl,-segprot,__HIB,r-x,r-x -Wl,-image_base,0x8000 -Wl,-segaddr,__HIB,0x4000
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib bar.c -o extensions/bar.kext/bar
+
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/SymbolSets.plist b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/SymbolSets.plist
new file mode 100644 (file)
index 0000000..91041a2
--- /dev/null
@@ -0,0 +1,26 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.bar</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolName</key>
+                                       <string>_symbol_from_bar</string>
+                                       <key>AliasTarget</key>
+                                       <string>_symbol_from_xnu</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/bar.exports b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/bar.exports
new file mode 100644 (file)
index 0000000..32a6100
--- /dev/null
@@ -0,0 +1 @@
+_symbol_from_bar:_symbol_from_xnu
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/extensions-auxkc/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/extensions-auxkc/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..1ec2b4b
--- /dev/null
@@ -0,0 +1,21 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.codeless</key>
+               <string>1.0</string>
+               <key>com.apple.bar</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/extensions-auxkc/foo.kext/foo b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/extensions-auxkc/foo.kext/foo
new file mode 100755 (executable)
index 0000000..1e68cc8
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/extensions-auxkc/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/extensions-basekc/codeless.kext/Info.plist b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/extensions-basekc/codeless.kext/Info.plist
new file mode 100644 (file)
index 0000000..baa1369
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>codeless</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.codeless</string>
+       <key>CFBundleName</key>
+       <string>codeless</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/foo.c b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/foo.c
new file mode 100644 (file)
index 0000000..f71c06d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int symbol_from_bar();
+
+int foo() {
+       return symbol_from_bar();
+}
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/main.c b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/main.c
new file mode 100644 (file)
index 0000000..263aaad
--- /dev/null
@@ -0,0 +1,9 @@
+
+// This will be re-exported from a symbol set in bar
+int symbol_from_xnu() {
+       return 0;
+}
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/main.kernel b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/main.kernel
new file mode 100755 (executable)
index 0000000..bc817c4
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/main.kernel differ
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/test.py b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel-codeless-kext/test.py
new file mode 100644 (file)
index 0000000..818103a
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# codeless kext's are in the PRELINK_INFO in the baseKC, but can be a dependency for auxKC kexts
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/auxkc-kext-bind-to-kernel-codeless-kext/main.kc", "/auxkc-kext-bind-to-kernel-codeless-kext/main.kernel", "/auxkc-kext-bind-to-kernel-codeless-kext/extensions-basekc", ["com.apple.bar", "com.apple.codeless"], [])
+    kernel_cache.analyze("/auxkc-kext-bind-to-kernel-codeless-kext/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+
+    # Now build an aux cache using the baseline kernel collection
+    kernel_cache.buildAuxKernelCollection("arm64", "/auxkc-kext-bind-to-kernel-codeless-kext/aux.kc", "/auxkc-kext-bind-to-kernel-codeless-kext/main.kc", "", "/auxkc-kext-bind-to-kernel-codeless-kext/extensions-auxkc", ["com.apple.foo"], [])
+    kernel_cache.analyze("/auxkc-kext-bind-to-kernel-codeless-kext/aux.kc", ["-layout", "-arch", "arm64"])
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-kext-bind-to-kernel-codeless-kext/aux.kc", ["-fixups", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 1
+    assert kernel_cache.dictionary()["fixups"]["0x4000"] == "kc(0) + 0x8000"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r--
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo
+# [~]> rm -r extensions/foo.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/SymbolSets.plist b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/SymbolSets.plist
new file mode 100644 (file)
index 0000000..91041a2
--- /dev/null
@@ -0,0 +1,26 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.bar</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolName</key>
+                                       <string>_symbol_from_bar</string>
+                                       <key>AliasTarget</key>
+                                       <string>_symbol_from_xnu</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/bar.exports b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/bar.exports
new file mode 100644 (file)
index 0000000..32a6100
--- /dev/null
@@ -0,0 +1 @@
+_symbol_from_bar:_symbol_from_xnu
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..1111344
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.bar</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..1e68cc8
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/foo.c b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/foo.c
new file mode 100644 (file)
index 0000000..f71c06d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int symbol_from_bar();
+
+int foo() {
+       return symbol_from_bar();
+}
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/main.c b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/main.c
new file mode 100644 (file)
index 0000000..263aaad
--- /dev/null
@@ -0,0 +1,9 @@
+
+// This will be re-exported from a symbol set in bar
+int symbol_from_xnu() {
+       return 0;
+}
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/main.kernel b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/main.kernel
new file mode 100755 (executable)
index 0000000..bc817c4
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/main.kernel differ
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/test.py b/testing/kernel-cache-tests/auxkc-kext-bind-to-kernel/test.py
new file mode 100644 (file)
index 0000000..85219ad
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that we use the symbol set when resolving symbols from aux KC to the kernel
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/auxkc-kext-bind-to-kernel/main.kc", "/auxkc-kext-bind-to-kernel/main.kernel", "/auxkc-kext-bind-to-kernel/extensions", ["com.apple.bar"], [])
+    kernel_cache.analyze("/auxkc-kext-bind-to-kernel/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+
+    # Now build an aux cache using the baseline kernel collection
+    kernel_cache.buildAuxKernelCollection("arm64", "/auxkc-kext-bind-to-kernel/aux.kc", "/auxkc-kext-bind-to-kernel/main.kc", "", "/auxkc-kext-bind-to-kernel/extensions", ["com.apple.foo"], [])
+    kernel_cache.analyze("/auxkc-kext-bind-to-kernel/aux.kc", ["-layout", "-arch", "arm64"])
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-kext-bind-to-kernel/aux.kc", ["-fixups", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 1
+    assert kernel_cache.dictionary()["fixups"]["0x4000"] == "kc(0) + 0x8000"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r--
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo
+# [~]> rm -r extensions/foo.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/bar.c b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/bar.c
new file mode 100644 (file)
index 0000000..8d0c3b4
--- /dev/null
@@ -0,0 +1,4 @@
+
+int symbol_from_bar() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-auxkc/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-auxkc/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..1ec2b4b
--- /dev/null
@@ -0,0 +1,21 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.codeless</key>
+               <string>1.0</string>
+               <key>com.apple.bar</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-auxkc/foo.kext/foo b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-auxkc/foo.kext/foo
new file mode 100755 (executable)
index 0000000..1e68cc8
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-auxkc/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc/bar.kext/Info.plist b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..77b3de8
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc/bar.kext/bar b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc/bar.kext/bar
new file mode 100755 (executable)
index 0000000..0f2e34a
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc/codeless.kext/Info.plist b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc/codeless.kext/Info.plist
new file mode 100644 (file)
index 0000000..baa1369
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>codeless</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.codeless</string>
+       <key>CFBundleName</key>
+       <string>codeless</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/foo.c b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/foo.c
new file mode 100644 (file)
index 0000000..f71c06d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int symbol_from_bar();
+
+int foo() {
+       return symbol_from_bar();
+}
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/main.c b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/main.c
new file mode 100644 (file)
index 0000000..263aaad
--- /dev/null
@@ -0,0 +1,9 @@
+
+// This will be re-exported from a symbol set in bar
+int symbol_from_xnu() {
+       return 0;
+}
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/main.kernel b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/main.kernel
new file mode 100755 (executable)
index 0000000..1c0fb7f
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/main.kernel differ
diff --git a/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/test.py b/testing/kernel-cache-tests/auxkc-kext-bind-to-pageablekc-codeless-kext/test.py
new file mode 100644 (file)
index 0000000..1e0eaa1
--- /dev/null
@@ -0,0 +1,37 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# codeless kext's are in the PRELINK_INFO in the pageableKC, but can be a dependency for auxKC kexts
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/auxkc-kext-bind-to-pageablekc-codeless-kext/main.kc", "/auxkc-kext-bind-to-pageablekc-codeless-kext/main.kernel", None, [], [])
+    kernel_cache.analyze("/auxkc-kext-bind-to-pageablekc-codeless-kext/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+
+    # Now build an pageable cache using the baseline kernel collection
+    kernel_cache.buildPageableKernelCollection("arm64", "/auxkc-kext-bind-to-pageablekc-codeless-kext/pageable.kc", "/auxkc-kext-bind-to-pageablekc-codeless-kext/main.kc", "/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-pageablekc", ["com.apple.bar", "com.apple.codeless"], [])
+    kernel_cache.analyze("/auxkc-kext-bind-to-pageablekc-codeless-kext/pageable.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar"
+
+    # Now build an aux cache using the baseline kernel collection
+    kernel_cache.buildAuxKernelCollection("arm64", "/auxkc-kext-bind-to-pageablekc-codeless-kext/aux.kc", "/auxkc-kext-bind-to-pageablekc-codeless-kext/main.kc", "/auxkc-kext-bind-to-pageablekc-codeless-kext/pageable.kc", "/auxkc-kext-bind-to-pageablekc-codeless-kext/extensions-auxkc", ["com.apple.foo"], [])
+    kernel_cache.analyze("/auxkc-kext-bind-to-pageablekc-codeless-kext/aux.kc", ["-layout", "-arch", "arm64"])
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-kext-bind-to-pageablekc-codeless-kext/aux.kc", ["-fixups", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 1
+    assert kernel_cache.dictionary()["fixups"]["0x4000"] == "kc(1) + 0x8000"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+
+# [~]> xcrun -sdk iphoneos cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions-auxkc/foo.kext/foo
+# [~]> xcrun -sdk iphoneos cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions-pageablekc/bar.kext/bar
+# [~]> rm -r extensions-*/*.kext/*.ld
diff --git a/testing/kernel-cache-tests/auxkc-missing-weak-bind/SymbolSets.plist b/testing/kernel-cache-tests/auxkc-missing-weak-bind/SymbolSets.plist
new file mode 100644 (file)
index 0000000..df941be
--- /dev/null
@@ -0,0 +1,24 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.libkern</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolName</key>
+                                       <string>_gOSKextUnresolved</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-missing-weak-bind/bar.c b/testing/kernel-cache-tests/auxkc-missing-weak-bind/bar.c
new file mode 100644 (file)
index 0000000..9e23395
--- /dev/null
@@ -0,0 +1,12 @@
+
+__attribute__((weak))
+extern int weakValue;
+
+extern int gOSKextUnresolved;
+
+int bar() {
+       // Missing weak import test
+       if ( &weakValue != &gOSKextUnresolved )
+               return 0;
+       return weakValue;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..7e9987d
--- /dev/null
@@ -0,0 +1,21 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+               <key>com.apple.libkern</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/bar.kext/bar b/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..920f5e6
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..7a43b38
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.libkern</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..f29c662
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-missing-weak-bind/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/auxkc-missing-weak-bind/foo.c b/testing/kernel-cache-tests/auxkc-missing-weak-bind/foo.c
new file mode 100644 (file)
index 0000000..9e23395
--- /dev/null
@@ -0,0 +1,12 @@
+
+__attribute__((weak))
+extern int weakValue;
+
+extern int gOSKextUnresolved;
+
+int bar() {
+       // Missing weak import test
+       if ( &weakValue != &gOSKextUnresolved )
+               return 0;
+       return weakValue;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-missing-weak-bind/main.c b/testing/kernel-cache-tests/auxkc-missing-weak-bind/main.c
new file mode 100644 (file)
index 0000000..31fd572
--- /dev/null
@@ -0,0 +1,7 @@
+
+// We need this symbol to bind missing weak imports to
+int gOSKextUnresolved = 0;
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-missing-weak-bind/main.kernel b/testing/kernel-cache-tests/auxkc-missing-weak-bind/main.kernel
new file mode 100755 (executable)
index 0000000..476d3aa
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-missing-weak-bind/main.kernel differ
diff --git a/testing/kernel-cache-tests/auxkc-missing-weak-bind/test.py b/testing/kernel-cache-tests/auxkc-missing-weak-bind/test.py
new file mode 100644 (file)
index 0000000..4b7f49c
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Check that weak binds can be missing, so long as we check for the magic symbol
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/auxkc-missing-weak-bind/main.kc", "/auxkc-missing-weak-bind/main.kernel", "/auxkc-missing-weak-bind/extensions", [], [])
+    kernel_cache.analyze("/auxkc-missing-weak-bind/main.kc", ["-symbols", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["name"] == "_gOSKextUnresolved"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["vmAddr"] == "0x10000"
+
+    # Now build an aux cache using the baseline kernel collection
+    kernel_cache.buildAuxKernelCollection("arm64", "/auxkc-missing-weak-bind/aux.kc", "/auxkc-missing-weak-bind/main.kc", "", "/auxkc-missing-weak-bind/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/auxkc-missing-weak-bind/aux.kc", ["-fixups", "-arch", "arm64"])
+
+    # Check the fixups
+    assert len(kernel_cache.dictionary()["fixups"]) == 4
+    assert kernel_cache.dictionary()["fixups"]["0x14000"] == "kc(0) + 0x10000"
+    assert kernel_cache.dictionary()["fixups"]["0x14008"] == "kc(0) + 0x10000"
+    assert kernel_cache.dictionary()["fixups"]["0x14010"] == "kc(0) + 0x10000"
+    assert kernel_cache.dictionary()["fixups"]["0x14018"] == "kc(0) + 0x10000"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar -Wl,-fixup_chains
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/auxkc-no-split-seg/bar.c b/testing/kernel-cache-tests/auxkc-no-split-seg/bar.c
new file mode 100644 (file)
index 0000000..3bb1d0b
--- /dev/null
@@ -0,0 +1,7 @@
+
+int i = 2;
+int*p = &i;
+
+int bar() {
+       return i;
+}
diff --git a/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/bar.kext/bar b/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..c53aafd
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..07ee33d
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-no-split-seg/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/auxkc-no-split-seg/foo.c b/testing/kernel-cache-tests/auxkc-no-split-seg/foo.c
new file mode 100644 (file)
index 0000000..3a9c313
--- /dev/null
@@ -0,0 +1,7 @@
+
+int i = 2;
+int*p = &i;
+
+int foo() {
+       return i;
+}
diff --git a/testing/kernel-cache-tests/auxkc-no-split-seg/main.c b/testing/kernel-cache-tests/auxkc-no-split-seg/main.c
new file mode 100644 (file)
index 0000000..2aa96d7
--- /dev/null
@@ -0,0 +1,5 @@
+
+__attribute__((section(("__HIB,__text"))))
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-no-split-seg/main.kernel b/testing/kernel-cache-tests/auxkc-no-split-seg/main.kernel
new file mode 100755 (executable)
index 0000000..1bcd894
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-no-split-seg/main.kernel differ
diff --git a/testing/kernel-cache-tests/auxkc-no-split-seg/test.py b/testing/kernel-cache-tests/auxkc-no-split-seg/test.py
new file mode 100644 (file)
index 0000000..430e9ba
--- /dev/null
@@ -0,0 +1,97 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Verify that we can build an auxKC from third party kext's without split seg
+
+def check(kernel_cache):
+    # First build a kernel collection
+    kernel_cache.buildKernelCollection("x86_64", "/auxkc-no-split-seg/main.kc", "/auxkc-no-split-seg/main.kernel", "/auxkc-no-split-seg/extensions", [], [])
+    kernel_cache.analyze("/auxkc-no-split-seg/main.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["cache-segments"]) == 6
+    assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x8000"
+    assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0xC000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xC000"
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0x10000"
+    assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__HIB"
+    assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0x14000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xC000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__HIB"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0x14000"
+
+    # Now build an aux cache using the baseline kernel collection
+    kernel_cache.buildAuxKernelCollection("x86_64", "/auxkc-no-split-seg/aux.kc", "/auxkc-no-split-seg/main.kc", "", "/auxkc-no-split-seg/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/auxkc-no-split-seg/aux.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["cache-segments"]) == 8
+    assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0x8000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__PRELINK_INFO"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0x0"
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__REGION0"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0x8000"
+    assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__REGION1"
+    assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x9000"
+    assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__REGION2"
+    assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0xA000"
+    assert kernel_cache.dictionary()["cache-segments"][6]["name"] == "__REGION3"
+    assert kernel_cache.dictionary()["cache-segments"][6]["vmAddr"] == "0xB000"
+    assert kernel_cache.dictionary()["cache-segments"][7]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["cache-segments"][7]["vmAddr"] == "0xC000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0x8000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x9000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0xC000"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0xA000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0xB000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0xC000"
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-no-split-seg/aux.kc", ["-fixups", "-arch", "x86_64"])
+    assert kernel_cache.dictionary()["fixups"] == ""
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["fixups"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x5008"] == "kc(3) + 0x9000"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["fixups"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"]["0x5008"] == "kc(3) + 0xB000"
+
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -Wl,-segprot,__HIB,r-x,r-x -Wl,-image_base,0x8000 -Wl,-segaddr,__HIB,0x4000
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib bar.c -o extensions/bar.kext/bar
+
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-uuid/bar.c b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/bar.c
new file mode 100644 (file)
index 0000000..2ad0d8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/bar.kext/bar b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..6288d91
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..cd1fa31
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-uuid/foo.c b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/foo.c
new file mode 100644 (file)
index 0000000..5a6eec2
--- /dev/null
@@ -0,0 +1,4 @@
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-uuid/main.c b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-uuid/main.kernel b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/main.kernel differ
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-uuid/test.py b/testing/kernel-cache-tests/auxkc-pageablekc-uuid/test.py
new file mode 100644 (file)
index 0000000..2ee1618
--- /dev/null
@@ -0,0 +1,43 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Verify that an auxKC references the UUID of the base KC
+# And also the UUID of the pageable KC
+
+def check(kernel_cache):
+    # First build a kernel collection
+    kernel_cache.buildKernelCollection("arm64", "/auxkc-pageablekc-uuid/main.kc", "/auxkc-pageablekc-uuid/main.kernel", "/auxkc-pageablekc-uuid/extensions", [], [])
+    kernel_cache.analyze("/auxkc-pageablekc-uuid/main.kc", ["-layout", "-arch", "arm64"])
+
+    # Check the kernel UUID
+    kernel_cache.analyze("/auxkc-pageablekc-uuid/main.kc", ["-uuid", "-arch", "arm64"])
+    kernelUUID = kernel_cache.dictionary()["uuid"]
+    assert kernelUUID != "00000000-0000-0000-0000-000000000000"
+    assert kernelUUID == kernel_cache.dictionary()["prelink-info-uuid"]
+
+    # Now build a pageable cache using the baseline kernel collection
+    kernel_cache.buildPageableKernelCollection("arm64", "/auxkc-pageablekc-uuid/pageable.kc", "/auxkc-pageablekc-uuid/main.kc", "/auxkc-pageablekc-uuid/extensions", ["com.apple.foo", "com.apple.bar"], [])
+
+    # Check the pageable UUID
+    kernel_cache.analyze("/auxkc-pageablekc-uuid/pageable.kc", ["-uuid", "-arch", "arm64"])
+    pageableUUID = kernel_cache.dictionary()["uuid"]
+    assert pageableUUID != "00000000-0000-0000-0000-000000000000"
+    assert kernel_cache.dictionary()["prelink-info-base-uuid"] == kernelUUID
+
+    # Now build an aux cache using the baseline and pageable kernel collections
+    kernel_cache.buildAuxKernelCollection("arm64", "/auxkc-pageablekc-uuid/aux.kc", "/auxkc-pageablekc-uuid/main.kc", "/auxkc-pageablekc-uuid/pageable.kc", "/auxkc-pageablekc-uuid/extensions", ["com.apple.foo", "com.apple.bar"], [])
+
+    # Check the aux UUID
+    kernel_cache.analyze("/auxkc-pageablekc-uuid/aux.kc", ["-uuid", "-arch", "arm64"])
+    assert kernel_cache.dictionary()["uuid"] != "00000000-0000-0000-0000-000000000000"
+    assert kernel_cache.dictionary()["prelink-info-base-uuid"] == kernelUUID
+    assert kernel_cache.dictionary()["prelink-info-pageable-uuid"] == pageableUUID
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/SymbolSets.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/SymbolSets.plist
new file mode 100644 (file)
index 0000000..c910db1
--- /dev/null
@@ -0,0 +1,56 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.symbols</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTV8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN1X11KernelClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTVN1X11KernelClass</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/bar1.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/bar1.cpp
new file mode 100644 (file)
index 0000000..ce2c54f
--- /dev/null
@@ -0,0 +1,10 @@
+
+#include "bar1.h"
+
+using namespace X;
+
+OSDefineMetaClassAndStructors( Bar1, Foo2 )
+
+int Bar1::foo() {
+       return 1;
+}
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/bar1.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/bar1.h
new file mode 100644 (file)
index 0000000..f17541f
--- /dev/null
@@ -0,0 +1,14 @@
+
+#include "foo2.h"
+
+namespace X {
+
+class Bar1 : public X::Foo2
+{
+    OSDeclareDefaultStructors( Bar1 )
+    
+public:
+    virtual int foo() override;
+};
+
+} // namespace X
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/bar2.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/bar2.cpp
new file mode 100644 (file)
index 0000000..4aec80a
--- /dev/null
@@ -0,0 +1,18 @@
+
+#include "bar1.h"
+
+using namespace X;
+
+class Bar2 : public Bar1
+{
+    OSDeclareDefaultStructors( Bar2 )
+    
+public:
+    virtual int foo() override;
+};
+
+OSDefineMetaClassAndStructors( Bar2, Bar1 )
+
+int Bar2::foo() {
+       return 1;
+}
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar1.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar1.kext/Info.plist
new file mode 100644 (file)
index 0000000..473c118
--- /dev/null
@@ -0,0 +1,23 @@
+<?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>CFBundleExecutable</key>
+       <string>bar1</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar1</string>
+       <key>CFBundleName</key>
+       <string>bar1</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo1</key>
+               <string>1.0</string>
+               <key>com.apple.foo2</key>
+               <string>1.0</string>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar1.kext/bar1 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar1.kext/bar1
new file mode 100755 (executable)
index 0000000..5d41141
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar1.kext/bar1 differ
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar2.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar2.kext/Info.plist
new file mode 100644 (file)
index 0000000..d766b97
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleExecutable</key>
+       <string>bar2</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar2</string>
+       <key>CFBundleName</key>
+       <string>bar2</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo1</key>
+               <string>1.0</string>
+               <key>com.apple.foo2</key>
+               <string>1.0</string>
+               <key>com.apple.bar1</key>
+               <string>1.0</string>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar2.kext/bar2 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar2.kext/bar2
new file mode 100755 (executable)
index 0000000..2efd434
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/bar2.kext/bar2 differ
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo1.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo1.kext/Info.plist
new file mode 100644 (file)
index 0000000..80ea13d
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>foo1</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo1</string>
+       <key>CFBundleName</key>
+       <string>foo1</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo1.kext/foo1 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo1.kext/foo1
new file mode 100755 (executable)
index 0000000..053b894
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo1.kext/foo1 differ
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo2.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo2.kext/Info.plist
new file mode 100644 (file)
index 0000000..276b51c
--- /dev/null
@@ -0,0 +1,21 @@
+<?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>CFBundleExecutable</key>
+       <string>foo2</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo2</string>
+       <key>CFBundleName</key>
+       <string>foo2</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+               <key>com.apple.foo1</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo2.kext/foo2 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo2.kext/foo2
new file mode 100755 (executable)
index 0000000..f1acca4
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions/foo2.kext/foo2 differ
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo.h
new file mode 100644 (file)
index 0000000..3ee59b5
--- /dev/null
@@ -0,0 +1,24 @@
+
+#include "kernel.h"
+
+namespace X {
+
+class Foo1 : public KernelClass
+{
+    OSDeclareDefaultStructors( Foo1 )
+    
+public:
+    virtual int foo();
+    
+#ifdef FOO1_USED
+    OSMetaClassDeclareReservedUsed(Foo1, 0);
+    virtual int foo1Used0();
+#else
+    OSMetaClassDeclareReservedUnused(Foo1, 0);
+#endif
+    OSMetaClassDeclareReservedUnused(Foo1, 1);
+    OSMetaClassDeclareReservedUnused(Foo1, 2);
+    OSMetaClassDeclareReservedUnused(Foo1, 3);
+};
+
+} // namespace X
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo1.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo1.cpp
new file mode 100644 (file)
index 0000000..e32f75a
--- /dev/null
@@ -0,0 +1,26 @@
+
+#include "foo1.h"
+
+using namespace X;
+
+OSDefineMetaClassAndStructors( Foo1, KernelClass )
+
+// Index 0 has been replaced with a method
+OSMetaClassDefineReservedUsed(Foo1, 0)
+// Index 1 has been replaced with a method
+OSMetaClassDefineReservedUsed( Foo1, 1 )
+
+OSMetaClassDefineReservedUnused( Foo1, 2 )
+OSMetaClassDefineReservedUnused( Foo1, 3 )
+
+int Foo1::foo() {
+       return 0;
+}
+
+int Foo1::foo1Used0() {
+       return 0;
+}
+
+int Foo1::foo1Used1() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo1.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo1.h
new file mode 100644 (file)
index 0000000..906c4b0
--- /dev/null
@@ -0,0 +1,31 @@
+
+#include "kernel.h"
+
+namespace X {
+
+class Foo1 : public KernelClass
+{
+    OSDeclareDefaultStructors( Foo1 )
+    
+public:
+    virtual int foo() override;
+    
+#ifdef FOO1_USED0
+    OSMetaClassDeclareReservedUsed(Foo1, 0);
+    virtual int foo1Used0();
+#else
+    OSMetaClassDeclareReservedUnused(Foo1, 0);
+#endif
+
+#ifdef FOO1_USED1
+    OSMetaClassDeclareReservedUsed(Foo1, 1);
+    virtual int foo1Used1();
+#else
+    OSMetaClassDeclareReservedUnused(Foo1, 1);
+#endif
+
+    OSMetaClassDeclareReservedUnused(Foo1, 2);
+    OSMetaClassDeclareReservedUnused(Foo1, 3);
+};
+
+} // namespace X
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo2.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo2.cpp
new file mode 100644 (file)
index 0000000..e41096f
--- /dev/null
@@ -0,0 +1,14 @@
+
+#include "foo2.h"
+
+using namespace X;
+
+OSDefineMetaClassAndStructors( Foo2, Foo1 )
+
+int Foo2::foo() {
+       return 0;
+}
+
+int Foo2::foo1Used0() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo2.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/foo2.h
new file mode 100644 (file)
index 0000000..d6d98a2
--- /dev/null
@@ -0,0 +1,16 @@
+
+#include "foo1.h"
+
+namespace X {
+
+class Foo2 : public Foo1
+{
+    OSDeclareDefaultStructors( Foo2 )
+    
+public:
+    virtual int foo() override;
+
+    virtual int foo1Used0() override;
+};
+
+} // namespace X
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/kernel.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/kernel.cpp
new file mode 100644 (file)
index 0000000..ee0c8c2
--- /dev/null
@@ -0,0 +1,17 @@
+
+#include "kernel.h"
+
+using namespace X;
+
+OSDefineMetaClassAndStructors( KernelClass, OSObject )
+
+// Index 0 has been replaced with a method
+OSMetaClassDefineReservedUsed(KernelClass, 0)
+
+int KernelClass::foo() {
+       return 0;
+}
+
+int KernelClass::kernelClassUsed0() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/kernel.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/kernel.h
new file mode 100644 (file)
index 0000000..33e1a43
--- /dev/null
@@ -0,0 +1,22 @@
+
+#include <Kernel/libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+namespace X {
+
+class KernelClass : public OSObject
+{
+    OSDeclareDefaultStructors( KernelClass )
+    
+public:
+    virtual int foo();
+    
+#ifdef KERNEL_USED
+    OSMetaClassDeclareReservedUsed(KernelClass, 0);
+    virtual int kernelClassUsed0();
+#else
+    OSMetaClassDeclareReservedUnused(KernelClass, 0);
+#endif
+};
+
+} // namespace X
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/main.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/main.cpp
new file mode 100644 (file)
index 0000000..b689f8a
--- /dev/null
@@ -0,0 +1,119 @@
+
+#include <libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+int __cxa_pure_virtual = 0;
+
+#if 0
+class OSObject : public OSMetaClassBase
+{
+    OSDeclareAbstractStructors(OSObject);
+
+public:
+       OSMetaClassDeclareReservedUnused(OSObject, 0);
+    OSMetaClassDeclareReservedUnused(OSObject, 1);
+    OSMetaClassDeclareReservedUnused(OSObject, 2);
+    OSMetaClassDeclareReservedUnused(OSObject, 3);
+    OSMetaClassDeclareReservedUnused(OSObject, 4);
+    OSMetaClassDeclareReservedUnused(OSObject, 5);
+    OSMetaClassDeclareReservedUnused(OSObject, 6);
+    OSMetaClassDeclareReservedUnused(OSObject, 7);
+    OSMetaClassDeclareReservedUnused(OSObject, 8);
+    OSMetaClassDeclareReservedUnused(OSObject, 9);
+    OSMetaClassDeclareReservedUnused(OSObject, 10);
+    OSMetaClassDeclareReservedUnused(OSObject, 11);
+    OSMetaClassDeclareReservedUnused(OSObject, 12);
+    OSMetaClassDeclareReservedUnused(OSObject, 13);
+    OSMetaClassDeclareReservedUnused(OSObject, 14);
+    OSMetaClassDeclareReservedUnused(OSObject, 15);
+};
+#endif
+
+// OSDefineMetaClassAndAbstractStructors(OSObject, 0);
+/* Class global data */
+OSObject::MetaClass OSObject::gMetaClass;
+const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass;
+const OSMetaClass * const OSObject::superClass = NULL;
+
+// Virtual Padding
+OSMetaClassDefineReservedUnused(OSObject, 0);
+OSMetaClassDefineReservedUnused(OSObject, 1);
+OSMetaClassDefineReservedUnused(OSObject, 2);
+OSMetaClassDefineReservedUnused(OSObject, 3);
+OSMetaClassDefineReservedUnused(OSObject, 4);
+OSMetaClassDefineReservedUnused(OSObject, 5);
+OSMetaClassDefineReservedUnused(OSObject, 6);
+OSMetaClassDefineReservedUnused(OSObject, 7);
+OSMetaClassDefineReservedUnused(OSObject, 8);
+OSMetaClassDefineReservedUnused(OSObject, 9);
+OSMetaClassDefineReservedUnused(OSObject, 10);
+OSMetaClassDefineReservedUnused(OSObject, 11);
+OSMetaClassDefineReservedUnused(OSObject, 12);
+OSMetaClassDefineReservedUnused(OSObject, 13);
+OSMetaClassDefineReservedUnused(OSObject, 14);
+OSMetaClassDefineReservedUnused(OSObject, 15);
+
+// struct IORPC { };
+
+// OSMetaClassBase
+OSMetaClassBase::OSMetaClassBase() { }
+OSMetaClassBase::~OSMetaClassBase() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { }
+kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; }
+bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; }
+
+// OSMetaClass
+OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { }
+OSMetaClass::~OSMetaClass() { }
+void OSMetaClass::reservedCalled(int ind) const { }
+void OSMetaClass::retain() const { }
+void OSMetaClass::release() const { }
+void OSMetaClass::release(int freeWhen) const { }
+int OSMetaClass::getRetainCount() const { return 0; }
+const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; }
+void OSMetaClass::taggedRetain(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; }
+void OSMetaClass::instanceConstructed() const { }
+
+// OSMetaClass reserved methods
+void OSMetaClass::_RESERVEDOSMetaClass0() { }
+void OSMetaClass::_RESERVEDOSMetaClass1() { }
+void OSMetaClass::_RESERVEDOSMetaClass2() { }
+void OSMetaClass::_RESERVEDOSMetaClass3() { }
+void OSMetaClass::_RESERVEDOSMetaClass4() { }
+void OSMetaClass::_RESERVEDOSMetaClass5() { }
+void OSMetaClass::_RESERVEDOSMetaClass6() { }
+void OSMetaClass::_RESERVEDOSMetaClass7() { }
+
+// OSObject::MetaClass
+OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { }
+OSObject *OSObject::MetaClass::alloc() const { return NULL; }
+kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; }
+
+// OSObject
+OSObject::OSObject(OSMetaClass const*) { }
+OSObject::~OSObject() { }
+const OSMetaClass* OSObject::getMetaClass() const { return NULL; }
+void OSObject::free() { }
+bool OSObject::init() { return false; }
+void OSObject::retain() const { }
+void OSObject::release() const { }
+void OSObject::release(int freeWhen) const { }
+int OSObject::getRetainCount() const { return 0; }
+void OSObject::taggedRetain(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSObject::serialize(OSSerialize * serializer) const { return false; }
+kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; }
+void* OSObject::operator new(unsigned long) { return (void*)1; }
+void OSObject::operator delete(void*, unsigned long) { return; }
+
+__attribute__((section(("__HIB, __text"))))
+extern "C" int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kernel b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kernel
new file mode 100755 (executable)
index 0000000..dc41fde
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kernel differ
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/test.py b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces-locals/test.py
new file mode 100644 (file)
index 0000000..6896775
--- /dev/null
@@ -0,0 +1,294 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This is the same as auxkc-pageablekc-vtable-patching-namespaces
+# but this test has all local symbols instead of global symbols
+
+
+# The kernel has class OSObject and subclass KernelClass
+# foo.kext sublclasses KernelClass to get Foo1, and subclasses that to get Foo2
+# bar.kext sublclasses Foo1 to get Bar1, and subclasses that to get Bar2
+
+# In KernelClass the vtable layout is:
+# [ ..., foo() kernelClassUsed0() ]
+
+# In Foo1, the layout is:
+# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1Used1() ]
+
+# In Foo2, the layout is:
+# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1(), foo1_RESERVED2(), foo1_RESERVED3() ]
+
+# In Bar1, the layout is:
+# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1(), foo1_RESERVED2(), foo1_RESERVED3() ]
+
+# In Bar2, the layout is:
+# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1(), foo1_RESERVED2(), foo1_RESERVED3() ]
+
+# All kext's will end up getting the vtable entry after foo() patched to kernelClassUsed0()
+# Foo2, Bar1, Bar2, will also get the vtable entry after foo1Used0() patched to foo1Used1()
+
+def findGlobalSymbolVMAddr(kernel_cache, dylib_index, symbol_name):
+    for symbol_and_addr in kernel_cache.dictionary()["dylibs"][dylib_index]["global-symbols"]:
+        if symbol_and_addr["name"] == symbol_name:
+            return symbol_and_addr["vmAddr"]
+    return None
+
+def findLocalSymbolVMAddr(kernel_cache, dylib_index, symbol_name):
+    for symbol_and_addr in kernel_cache.dictionary()["dylibs"][dylib_index]["local-symbols"]:
+        if symbol_and_addr["name"] == symbol_name:
+            return symbol_and_addr["vmAddr"]
+    return None
+
+def findFixupVMAddr(kernel_cache, fixup_name):
+    for fixup_vmaddr, fixup_target in kernel_cache.dictionary()["fixups"].iteritems():
+        if fixup_target == fixup_name:
+            return fixup_vmaddr
+    return None
+
+def findPagableFixupVMAddr(kernel_cache, dylib_index, fixup_name):
+    for fixup_vmaddr, fixup_target in kernel_cache.dictionary()["dylibs"][dylib_index]["fixups"].iteritems():
+        if fixup_target == fixup_name:
+            return fixup_vmaddr
+    return None
+
+def findAuxFixupVMAddr(kernel_cache, dylib_index, fixup_name):
+    for fixup_vmaddr, fixup_target in kernel_cache.dictionary()["dylibs"][dylib_index]["fixups"].iteritems():
+        if fixup_target == fixup_name:
+            return fixup_vmaddr
+    return None
+
+def offsetVMAddr(vmAddr, offset):
+    het_int = int(vmAddr, 16)
+    het_int = het_int + offset
+    return ''.join([ '0x', hex(het_int).upper()[2:] ])
+
+def check(kernel_cache):
+    enableLogging = False
+    kernel_cache.buildKernelCollection("x86_64", "/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kc", "/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kernel", "", [], [])
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kc", ["-symbols", "-arch", "x86_64"])
+    
+    # From kernel, we want to know where the vtable is, and the foo() and kernelClassUsed0() slots in that vtable
+    # KernelClass::foo()
+    kernelClassFooVMAddr = findGlobalSymbolVMAddr(kernel_cache, 0, "__ZN1X11KernelClass3fooEv")
+    if enableLogging:
+        print "kernelClassFooVMAddr: " + kernelClassFooVMAddr
+
+    # KernelClass::kernelClassUsed0()
+    kernelClassUsed0VMAddr = findGlobalSymbolVMAddr(kernel_cache, 0, "__ZN1X11KernelClass16kernelClassUsed0Ev")
+    if enableLogging:
+        print "kernelClassUsed0VMAddr: " + kernelClassUsed0VMAddr
+
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kc", ["-fixups", "-arch", "x86_64"])
+    
+    # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup
+    kernelFooFixupAddr = findFixupVMAddr(kernel_cache, "kc(0) + " + kernelClassFooVMAddr + " : pointer64")
+    if enableLogging:
+        print "kernelFooFixupAddr: " + kernelFooFixupAddr
+    # Then the following fixup should be to KernelClass::kernelClassUsed0()
+    kernelFooNextFixupAddr = offsetVMAddr(kernelFooFixupAddr, 8)
+    if enableLogging:
+        print "kernelFooNextFixupAddr: " + kernelFooNextFixupAddr
+    assert kernel_cache.dictionary()["fixups"][kernelFooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMAddr + " : pointer64"
+
+    # From this point on, the vmAddr for __ZN1X11KernelClass16kernelClassUsed0Ev is an offset in to kc(0)
+    # so we want to turn it from a vmAddr to vmOffset by subtracting the base address of 0x4000 which is on __HIB
+    kernelClassUsed0VMOffset = offsetVMAddr(kernelClassUsed0VMAddr, -0x4000)
+    if enableLogging:
+        print "kernelClassUsed0VMOffset: " + kernelClassUsed0VMOffset
+
+    # -----------------------------------------------------------
+    # Now build an pageable cache using the baseline kernel collection
+    kernel_cache.buildPageableKernelCollection("x86_64", "/auxkc-pageablekc-vtable-patching-namespaces-locals/pageable.kc", "/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kc", "/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions", ["com.apple.foo1", "com.apple.foo2"], [])
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces-locals/pageable.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.foo1"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo2"
+
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces-locals/pageable.kc", ["-symbols", "-arch", "x86_64"])
+    
+    # From foo1, find the vtable and its override of foo()
+    # Foo1::foo()
+    pageableFoo1FooVMAddr = findLocalSymbolVMAddr(kernel_cache, 0, "__ZN1X4Foo13fooEv")
+    if enableLogging:
+        print "pageableFoo1FooVMAddr: " + pageableFoo1FooVMAddr
+
+    pageableFoo1FooUsed0VMAddr = findLocalSymbolVMAddr(kernel_cache, 0, "__ZN1X4Foo19foo1Used0Ev")
+    if enableLogging:
+        print "pageableFoo1FooUsed0VMAddr: " + pageableFoo1FooUsed0VMAddr
+
+    pageableFoo1FooUsed1VMAddr = findLocalSymbolVMAddr(kernel_cache, 0, "__ZN1X4Foo19foo1Used1Ev")
+    if enableLogging:
+        print "pageableFoo1FooUsed1VMAddr: " + pageableFoo1FooUsed1VMAddr
+
+    # From foo2, find the vtable and its override of foo()
+    # Foo2::foo()
+    pageableFoo2FooVMAddr = findLocalSymbolVMAddr(kernel_cache, 1, "__ZN1X4Foo23fooEv")
+    if enableLogging:
+        print "pageableFoo2FooVMAddr: " + pageableFoo2FooVMAddr
+    # Also find Foo2::foo1Used0() as it overrides foo1Used0 from the superclass
+    pageableFoo2FooUsed0VMAddr = findLocalSymbolVMAddr(kernel_cache, 1, "__ZN1X4Foo29foo1Used0Ev")
+    if enableLogging:
+        print "pageableFoo2FooUsed0VMAddr: " + pageableFoo2FooUsed0VMAddr
+
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces-locals/pageable.kc", ["-fixups", "-arch", "x86_64"])
+    kernel_cache.dictionary()["fixups"] == "none"
+
+    # --- foo1.kext ---
+    # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1Used1() ]
+    # and we want           [ ..., foo(), kernelClassUsed0(),      foo1Used0(), foo1Used1() ]
+
+    # In vtable for Foo1, we match the entry for Foo1::foo() by looking for its value on the RHS of the fixup
+    pageableFoo1FooFixupAddr = findPagableFixupVMAddr(kernel_cache, 0, "kc(1) + " + pageableFoo1FooVMAddr)
+    if enableLogging:
+        print "pageableFoo1FooFixupAddr: " + pageableFoo1FooFixupAddr
+
+    # Then the following fixup should be to KernelClass::kernelClassUsed0()
+    pageableFoo1FooNextFixupAddr = offsetVMAddr(pageableFoo1FooFixupAddr, 8)
+    if enableLogging:
+        print "pageableFoo1FooNextFixupAddr: " + pageableFoo1FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"][pageableFoo1FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset
+
+    # Then we should have foo1Used0()
+    pageableFoo1FooNextFixupAddr = offsetVMAddr(pageableFoo1FooFixupAddr, 16)
+    if enableLogging:
+        print "pageableFoo1FooNextFixupAddr: " + pageableFoo1FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"][pageableFoo1FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed0VMAddr
+
+    # And then foo1Used1()
+    pageableFoo1FooNextFixupAddr = offsetVMAddr(pageableFoo1FooFixupAddr, 24)
+    if enableLogging:
+        print "pageableFoo1FooNextFixupAddr: " + pageableFoo1FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"][pageableFoo1FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr
+
+    # --- foo2.kext ---
+    # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1() ]
+    # and we want           [ ..., foo(), kernelClassUsed0(),      foo1Used0(), foo1Used1() ]
+
+    # In vtable for Foo2, we match the entry for Foo2::foo() by looking for its value on the RHS of the fixup
+    pageableFoo2FooFixupAddr = findPagableFixupVMAddr(kernel_cache, 1, "kc(1) + " + pageableFoo2FooVMAddr)
+    if enableLogging:
+        print "pageableFoo2FooFixupAddr: " + pageableFoo2FooFixupAddr
+
+    # Then the following fixup should be to KernelClass::kernelClassUsed0()
+    pageableFoo2FooNextFixupAddr = offsetVMAddr(pageableFoo2FooFixupAddr, 8)
+    if enableLogging:
+        print "pageableFoo2FooNextFixupAddr: " + pageableFoo2FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"][pageableFoo2FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset
+
+    # Then we should have foo1Used0(), but Foo2 overrides that, so it should be the Foo2 implementation, not the Foo1 implementation
+    pageableFoo2FooNextFixupAddr = offsetVMAddr(pageableFoo2FooFixupAddr, 16)
+    if enableLogging:
+        print "pageableFoo2FooNextFixupAddr: " + pageableFoo2FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"][pageableFoo2FooNextFixupAddr] == "kc(1) + " + pageableFoo2FooUsed0VMAddr
+
+    # And then foo1Used1()
+    pageableFoo2FooNextFixupAddr = offsetVMAddr(pageableFoo2FooFixupAddr, 24)
+    if enableLogging:
+        print "pageableFoo2FooNextFixupAddr: " + pageableFoo2FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"][pageableFoo2FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr
+
+
+    # -----------------------------------------------------------
+    # Now build an aux cache using the baseline kernel collection
+    kernel_cache.buildAuxKernelCollection("x86_64", "/auxkc-pageablekc-vtable-patching-namespaces-locals/aux.kc", "/auxkc-pageablekc-vtable-patching-namespaces-locals/main.kc", "/auxkc-pageablekc-vtable-patching-namespaces-locals/pageable.kc", "/auxkc-pageablekc-vtable-patching-namespaces-locals/extensions", ["com.apple.bar1", "com.apple.bar2"], [])
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces-locals/aux.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar1"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar2"
+
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces-locals/aux.kc", ["-symbols", "-arch", "x86_64"])
+
+    # From bar1, find the vtable and its override of foo()
+    # Bar1::foo()
+    auxBar1FooVMAddr = findLocalSymbolVMAddr(kernel_cache, 0, "__ZN1X4Bar13fooEv")
+    if enableLogging:
+        print "auxBar1FooVMAddr: " + auxBar1FooVMAddr
+
+    # From bar2, find the vtable and its override of foo()
+    # Bar1::foo()
+    auxBar2FooVMAddr = findLocalSymbolVMAddr(kernel_cache, 1, "__ZN4Bar23fooEv")
+    if enableLogging:
+        print "auxBar2FooVMAddr: " + auxBar2FooVMAddr
+
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces-locals/aux.kc", ["-fixups", "-arch", "x86_64"])
+
+    # --- foo1.kext ---
+    # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1Used1() ]
+    # and we want           [ ..., foo(), kernelClassUsed0(),      foo1Used0(), foo1Used1() ]
+
+    # In vtable for Bar1, we match the entry for Bar1::foo() by looking for its value on the RHS of the fixup
+    auxBar1FooFixupAddr = findAuxFixupVMAddr(kernel_cache, 0, "kc(3) + " + auxBar1FooVMAddr)
+    if enableLogging:
+        print "auxBar1FooFixupAddr: " + auxBar1FooFixupAddr
+
+    # Then the following fixup should be to KernelClass::kernelClassUsed0()
+    auxBar1FooNextFixupAddr = offsetVMAddr(auxBar1FooFixupAddr, 8)
+    if enableLogging:
+        print "auxBar1FooNextFixupAddr: " + auxBar1FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"][auxBar1FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset
+
+    # Then we should have foo1Used0() from Foo2 as it overrides it from Foo1
+    auxBar1FooNextFixupAddr = offsetVMAddr(auxBar1FooFixupAddr, 16)
+    if enableLogging:
+        print "auxBar1FooNextFixupAddr: " + auxBar1FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"][auxBar1FooNextFixupAddr] == "kc(1) + " + pageableFoo2FooUsed0VMAddr
+
+    # And then foo1Used1()
+    auxBar1FooNextFixupAddr = offsetVMAddr(auxBar1FooFixupAddr, 24)
+    if enableLogging:
+        print "auxBar1FooNextFixupAddr: " + auxBar1FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"][auxBar1FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr
+
+    # --- bar2.kext ---
+    # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1() ]
+    # and we want           [ ..., foo(), kernelClassUsed0(),      foo1Used0(), foo1Used1() ]
+
+    # In vtable for Foo2, we match the entry for Foo2::foo() by looking for its value on the RHS of the fixup
+    auxBar2FooFixupAddr = findAuxFixupVMAddr(kernel_cache, 1, "kc(3) + " + auxBar2FooVMAddr)
+    if enableLogging:
+        print "auxBar2FooFixupAddr: " + auxBar2FooFixupAddr
+
+    # Then the following fixup should be to KernelClass::kernelClassUsed0()
+    auxBar2FooNextFixupAddr = offsetVMAddr(auxBar2FooFixupAddr, 8)
+    if enableLogging:
+        print "auxBar2FooNextFixupAddr: " + auxBar2FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"][auxBar2FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset
+
+    # Then we should have foo1Used0() from Foo2 as it overrides it from Foo1
+    auxBar2FooNextFixupAddr = offsetVMAddr(auxBar2FooFixupAddr, 16)
+    if enableLogging:
+        print "auxBar2FooNextFixupAddr: " + auxBar2FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"][auxBar2FooNextFixupAddr] == "kc(1) + " + pageableFoo2FooUsed0VMAddr
+
+    # And then foo1Used1()
+    auxBar2FooNextFixupAddr = offsetVMAddr(auxBar2FooFixupAddr, 24)
+    if enableLogging:
+        print "auxBar2FooNextFixupAddr: " + auxBar2FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"][auxBar2FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp kernel.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- -std=c++11 -DKERNEL_USED=1 -Wl,-exported_symbol,__ZN1X11KernelClass10gMetaClassE -Wl,-exported_symbol,__ZN8OSObject10gMetaClassE -Wl,-exported_symbol,__ZNK11OSMetaClass19instanceConstructedEv
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo1.cpp -o extensions/foo1.kext/foo1 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1  -DFOO1_USED1=1 -Wl,-exported_symbol,__ZN1X4Foo110gMetaClassE -Wl,-exported_symbol,__ZN1X4Foo1C2EPK11OSMetaClass -Wl,-exported_symbol,__ZTVN1X4Foo1E -Wl,-exported_symbol,__ZN1X4Foo1D2Ev
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo2.cpp -o extensions/foo2.kext/foo2 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1 -Wl,-exported_symbol,__ZN1X4Foo210gMetaClassE -Wl,-exported_symbol,__ZN1X4Foo2C2EPK11OSMetaClass -Wl,-exported_symbol,__ZTVN1X4Foo2E -Wl,-exported_symbol,__ZN1X4Foo2D2Ev
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-data_const bar1.cpp -o extensions/bar1.kext/bar1 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1 -Wl,-exported_symbol,__ZN1X4Bar110gMetaClassE -Wl,-exported_symbol,__ZN1X4Bar1C2EPK11OSMetaClass -Wl,-exported_symbol,__ZTVN1X4Bar1E -Wl,-exported_symbol,__ZN1X4Bar1D2Ev
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-data_const bar2.cpp -o extensions/bar2.kext/bar2 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1
+
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/SymbolSets.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/SymbolSets.plist
new file mode 100644 (file)
index 0000000..c910db1
--- /dev/null
@@ -0,0 +1,56 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.symbols</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTV8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN1X11KernelClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTVN1X11KernelClass</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/bar1.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/bar1.cpp
new file mode 100644 (file)
index 0000000..ce2c54f
--- /dev/null
@@ -0,0 +1,10 @@
+
+#include "bar1.h"
+
+using namespace X;
+
+OSDefineMetaClassAndStructors( Bar1, Foo2 )
+
+int Bar1::foo() {
+       return 1;
+}
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/bar1.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/bar1.h
new file mode 100644 (file)
index 0000000..f17541f
--- /dev/null
@@ -0,0 +1,14 @@
+
+#include "foo2.h"
+
+namespace X {
+
+class Bar1 : public X::Foo2
+{
+    OSDeclareDefaultStructors( Bar1 )
+    
+public:
+    virtual int foo() override;
+};
+
+} // namespace X
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/bar2.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/bar2.cpp
new file mode 100644 (file)
index 0000000..4aec80a
--- /dev/null
@@ -0,0 +1,18 @@
+
+#include "bar1.h"
+
+using namespace X;
+
+class Bar2 : public Bar1
+{
+    OSDeclareDefaultStructors( Bar2 )
+    
+public:
+    virtual int foo() override;
+};
+
+OSDefineMetaClassAndStructors( Bar2, Bar1 )
+
+int Bar2::foo() {
+       return 1;
+}
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar1.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar1.kext/Info.plist
new file mode 100644 (file)
index 0000000..473c118
--- /dev/null
@@ -0,0 +1,23 @@
+<?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>CFBundleExecutable</key>
+       <string>bar1</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar1</string>
+       <key>CFBundleName</key>
+       <string>bar1</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo1</key>
+               <string>1.0</string>
+               <key>com.apple.foo2</key>
+               <string>1.0</string>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar1.kext/bar1 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar1.kext/bar1
new file mode 100755 (executable)
index 0000000..e6b8a8b
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar1.kext/bar1 differ
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar2.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar2.kext/Info.plist
new file mode 100644 (file)
index 0000000..d766b97
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleExecutable</key>
+       <string>bar2</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar2</string>
+       <key>CFBundleName</key>
+       <string>bar2</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo1</key>
+               <string>1.0</string>
+               <key>com.apple.foo2</key>
+               <string>1.0</string>
+               <key>com.apple.bar1</key>
+               <string>1.0</string>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar2.kext/bar2 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar2.kext/bar2
new file mode 100755 (executable)
index 0000000..0341c74
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/bar2.kext/bar2 differ
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo1.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo1.kext/Info.plist
new file mode 100644 (file)
index 0000000..80ea13d
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>foo1</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo1</string>
+       <key>CFBundleName</key>
+       <string>foo1</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo1.kext/foo1 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo1.kext/foo1
new file mode 100755 (executable)
index 0000000..7caf637
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo1.kext/foo1 differ
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo2.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo2.kext/Info.plist
new file mode 100644 (file)
index 0000000..276b51c
--- /dev/null
@@ -0,0 +1,21 @@
+<?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>CFBundleExecutable</key>
+       <string>foo2</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo2</string>
+       <key>CFBundleName</key>
+       <string>foo2</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+               <key>com.apple.foo1</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo2.kext/foo2 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo2.kext/foo2
new file mode 100755 (executable)
index 0000000..0b0aecd
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/extensions/foo2.kext/foo2 differ
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo.h
new file mode 100644 (file)
index 0000000..3ee59b5
--- /dev/null
@@ -0,0 +1,24 @@
+
+#include "kernel.h"
+
+namespace X {
+
+class Foo1 : public KernelClass
+{
+    OSDeclareDefaultStructors( Foo1 )
+    
+public:
+    virtual int foo();
+    
+#ifdef FOO1_USED
+    OSMetaClassDeclareReservedUsed(Foo1, 0);
+    virtual int foo1Used0();
+#else
+    OSMetaClassDeclareReservedUnused(Foo1, 0);
+#endif
+    OSMetaClassDeclareReservedUnused(Foo1, 1);
+    OSMetaClassDeclareReservedUnused(Foo1, 2);
+    OSMetaClassDeclareReservedUnused(Foo1, 3);
+};
+
+} // namespace X
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo1.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo1.cpp
new file mode 100644 (file)
index 0000000..e32f75a
--- /dev/null
@@ -0,0 +1,26 @@
+
+#include "foo1.h"
+
+using namespace X;
+
+OSDefineMetaClassAndStructors( Foo1, KernelClass )
+
+// Index 0 has been replaced with a method
+OSMetaClassDefineReservedUsed(Foo1, 0)
+// Index 1 has been replaced with a method
+OSMetaClassDefineReservedUsed( Foo1, 1 )
+
+OSMetaClassDefineReservedUnused( Foo1, 2 )
+OSMetaClassDefineReservedUnused( Foo1, 3 )
+
+int Foo1::foo() {
+       return 0;
+}
+
+int Foo1::foo1Used0() {
+       return 0;
+}
+
+int Foo1::foo1Used1() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo1.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo1.h
new file mode 100644 (file)
index 0000000..906c4b0
--- /dev/null
@@ -0,0 +1,31 @@
+
+#include "kernel.h"
+
+namespace X {
+
+class Foo1 : public KernelClass
+{
+    OSDeclareDefaultStructors( Foo1 )
+    
+public:
+    virtual int foo() override;
+    
+#ifdef FOO1_USED0
+    OSMetaClassDeclareReservedUsed(Foo1, 0);
+    virtual int foo1Used0();
+#else
+    OSMetaClassDeclareReservedUnused(Foo1, 0);
+#endif
+
+#ifdef FOO1_USED1
+    OSMetaClassDeclareReservedUsed(Foo1, 1);
+    virtual int foo1Used1();
+#else
+    OSMetaClassDeclareReservedUnused(Foo1, 1);
+#endif
+
+    OSMetaClassDeclareReservedUnused(Foo1, 2);
+    OSMetaClassDeclareReservedUnused(Foo1, 3);
+};
+
+} // namespace X
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo2.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo2.cpp
new file mode 100644 (file)
index 0000000..e41096f
--- /dev/null
@@ -0,0 +1,14 @@
+
+#include "foo2.h"
+
+using namespace X;
+
+OSDefineMetaClassAndStructors( Foo2, Foo1 )
+
+int Foo2::foo() {
+       return 0;
+}
+
+int Foo2::foo1Used0() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo2.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/foo2.h
new file mode 100644 (file)
index 0000000..d6d98a2
--- /dev/null
@@ -0,0 +1,16 @@
+
+#include "foo1.h"
+
+namespace X {
+
+class Foo2 : public Foo1
+{
+    OSDeclareDefaultStructors( Foo2 )
+    
+public:
+    virtual int foo() override;
+
+    virtual int foo1Used0() override;
+};
+
+} // namespace X
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/kernel.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/kernel.cpp
new file mode 100644 (file)
index 0000000..ee0c8c2
--- /dev/null
@@ -0,0 +1,17 @@
+
+#include "kernel.h"
+
+using namespace X;
+
+OSDefineMetaClassAndStructors( KernelClass, OSObject )
+
+// Index 0 has been replaced with a method
+OSMetaClassDefineReservedUsed(KernelClass, 0)
+
+int KernelClass::foo() {
+       return 0;
+}
+
+int KernelClass::kernelClassUsed0() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/kernel.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/kernel.h
new file mode 100644 (file)
index 0000000..33e1a43
--- /dev/null
@@ -0,0 +1,22 @@
+
+#include <Kernel/libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+namespace X {
+
+class KernelClass : public OSObject
+{
+    OSDeclareDefaultStructors( KernelClass )
+    
+public:
+    virtual int foo();
+    
+#ifdef KERNEL_USED
+    OSMetaClassDeclareReservedUsed(KernelClass, 0);
+    virtual int kernelClassUsed0();
+#else
+    OSMetaClassDeclareReservedUnused(KernelClass, 0);
+#endif
+};
+
+} // namespace X
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/main.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/main.cpp
new file mode 100644 (file)
index 0000000..b689f8a
--- /dev/null
@@ -0,0 +1,119 @@
+
+#include <libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+int __cxa_pure_virtual = 0;
+
+#if 0
+class OSObject : public OSMetaClassBase
+{
+    OSDeclareAbstractStructors(OSObject);
+
+public:
+       OSMetaClassDeclareReservedUnused(OSObject, 0);
+    OSMetaClassDeclareReservedUnused(OSObject, 1);
+    OSMetaClassDeclareReservedUnused(OSObject, 2);
+    OSMetaClassDeclareReservedUnused(OSObject, 3);
+    OSMetaClassDeclareReservedUnused(OSObject, 4);
+    OSMetaClassDeclareReservedUnused(OSObject, 5);
+    OSMetaClassDeclareReservedUnused(OSObject, 6);
+    OSMetaClassDeclareReservedUnused(OSObject, 7);
+    OSMetaClassDeclareReservedUnused(OSObject, 8);
+    OSMetaClassDeclareReservedUnused(OSObject, 9);
+    OSMetaClassDeclareReservedUnused(OSObject, 10);
+    OSMetaClassDeclareReservedUnused(OSObject, 11);
+    OSMetaClassDeclareReservedUnused(OSObject, 12);
+    OSMetaClassDeclareReservedUnused(OSObject, 13);
+    OSMetaClassDeclareReservedUnused(OSObject, 14);
+    OSMetaClassDeclareReservedUnused(OSObject, 15);
+};
+#endif
+
+// OSDefineMetaClassAndAbstractStructors(OSObject, 0);
+/* Class global data */
+OSObject::MetaClass OSObject::gMetaClass;
+const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass;
+const OSMetaClass * const OSObject::superClass = NULL;
+
+// Virtual Padding
+OSMetaClassDefineReservedUnused(OSObject, 0);
+OSMetaClassDefineReservedUnused(OSObject, 1);
+OSMetaClassDefineReservedUnused(OSObject, 2);
+OSMetaClassDefineReservedUnused(OSObject, 3);
+OSMetaClassDefineReservedUnused(OSObject, 4);
+OSMetaClassDefineReservedUnused(OSObject, 5);
+OSMetaClassDefineReservedUnused(OSObject, 6);
+OSMetaClassDefineReservedUnused(OSObject, 7);
+OSMetaClassDefineReservedUnused(OSObject, 8);
+OSMetaClassDefineReservedUnused(OSObject, 9);
+OSMetaClassDefineReservedUnused(OSObject, 10);
+OSMetaClassDefineReservedUnused(OSObject, 11);
+OSMetaClassDefineReservedUnused(OSObject, 12);
+OSMetaClassDefineReservedUnused(OSObject, 13);
+OSMetaClassDefineReservedUnused(OSObject, 14);
+OSMetaClassDefineReservedUnused(OSObject, 15);
+
+// struct IORPC { };
+
+// OSMetaClassBase
+OSMetaClassBase::OSMetaClassBase() { }
+OSMetaClassBase::~OSMetaClassBase() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { }
+kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; }
+bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; }
+
+// OSMetaClass
+OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { }
+OSMetaClass::~OSMetaClass() { }
+void OSMetaClass::reservedCalled(int ind) const { }
+void OSMetaClass::retain() const { }
+void OSMetaClass::release() const { }
+void OSMetaClass::release(int freeWhen) const { }
+int OSMetaClass::getRetainCount() const { return 0; }
+const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; }
+void OSMetaClass::taggedRetain(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; }
+void OSMetaClass::instanceConstructed() const { }
+
+// OSMetaClass reserved methods
+void OSMetaClass::_RESERVEDOSMetaClass0() { }
+void OSMetaClass::_RESERVEDOSMetaClass1() { }
+void OSMetaClass::_RESERVEDOSMetaClass2() { }
+void OSMetaClass::_RESERVEDOSMetaClass3() { }
+void OSMetaClass::_RESERVEDOSMetaClass4() { }
+void OSMetaClass::_RESERVEDOSMetaClass5() { }
+void OSMetaClass::_RESERVEDOSMetaClass6() { }
+void OSMetaClass::_RESERVEDOSMetaClass7() { }
+
+// OSObject::MetaClass
+OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { }
+OSObject *OSObject::MetaClass::alloc() const { return NULL; }
+kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; }
+
+// OSObject
+OSObject::OSObject(OSMetaClass const*) { }
+OSObject::~OSObject() { }
+const OSMetaClass* OSObject::getMetaClass() const { return NULL; }
+void OSObject::free() { }
+bool OSObject::init() { return false; }
+void OSObject::retain() const { }
+void OSObject::release() const { }
+void OSObject::release(int freeWhen) const { }
+int OSObject::getRetainCount() const { return 0; }
+void OSObject::taggedRetain(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSObject::serialize(OSSerialize * serializer) const { return false; }
+kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; }
+void* OSObject::operator new(unsigned long) { return (void*)1; }
+void OSObject::operator delete(void*, unsigned long) { return; }
+
+__attribute__((section(("__HIB, __text"))))
+extern "C" int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/main.kernel b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/main.kernel
new file mode 100755 (executable)
index 0000000..b48d3f3
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/main.kernel differ
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/test.py b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching-namespaces/test.py
new file mode 100644 (file)
index 0000000..7b6ec1d
--- /dev/null
@@ -0,0 +1,285 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+
+# The kernel has class OSObject and subclass KernelClass
+# foo.kext sublclasses KernelClass to get Foo1, and subclasses that to get Foo2
+# bar.kext sublclasses Foo1 to get Bar1, and subclasses that to get Bar2
+
+# In KernelClass the vtable layout is:
+# [ ..., foo() kernelClassUsed0() ]
+
+# In Foo1, the layout is:
+# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1Used1() ]
+
+# In Foo2, the layout is:
+# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1(), foo1_RESERVED2(), foo1_RESERVED3() ]
+
+# In Bar1, the layout is:
+# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1(), foo1_RESERVED2(), foo1_RESERVED3() ]
+
+# In Bar2, the layout is:
+# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1(), foo1_RESERVED2(), foo1_RESERVED3() ]
+
+# All kext's will end up getting the vtable entry after foo() patched to kernelClassUsed0()
+# Foo2, Bar1, Bar2, will also get the vtable entry after foo1Used0() patched to foo1Used1()
+
+def findSymbolVMAddr(kernel_cache, dylib_index, symbol_name):
+    for symbol_and_addr in kernel_cache.dictionary()["dylibs"][dylib_index]["global-symbols"]:
+        if symbol_and_addr["name"] == symbol_name:
+            return symbol_and_addr["vmAddr"]
+    return None
+
+def findFixupVMAddr(kernel_cache, fixup_name):
+    for fixup_vmaddr, fixup_target in kernel_cache.dictionary()["fixups"].iteritems():
+        if fixup_target == fixup_name:
+            return fixup_vmaddr
+    return None
+
+def findPagableFixupVMAddr(kernel_cache, dylib_index, fixup_name):
+    for fixup_vmaddr, fixup_target in kernel_cache.dictionary()["dylibs"][dylib_index]["fixups"].iteritems():
+        if fixup_target == fixup_name:
+            return fixup_vmaddr
+    return None
+
+def findAuxFixupVMAddr(kernel_cache, dylib_index, fixup_name):
+    for fixup_vmaddr, fixup_target in kernel_cache.dictionary()["dylibs"][dylib_index]["fixups"].iteritems():
+        if fixup_target == fixup_name:
+            return fixup_vmaddr
+    return None
+
+def offsetVMAddr(vmAddr, offset):
+    het_int = int(vmAddr, 16)
+    het_int = het_int + offset
+    return ''.join([ '0x', hex(het_int).upper()[2:] ])
+
+def check(kernel_cache):
+    enableLogging = False
+    kernel_cache.buildKernelCollection("x86_64", "/auxkc-pageablekc-vtable-patching-namespaces/main.kc", "/auxkc-pageablekc-vtable-patching-namespaces/main.kernel", "", [], [])
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces/main.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces/main.kc", ["-symbols", "-arch", "x86_64"])
+    
+    # From kernel, we want to know where the vtable is, and the foo() and kernelClassUsed0() slots in that vtable
+    # KernelClass::foo()
+    kernelClassFooVMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN1X11KernelClass3fooEv")
+    if enableLogging:
+        print "kernelClassFooVMAddr: " + kernelClassFooVMAddr
+
+    # KernelClass::kernelClassUsed0()
+    kernelClassUsed0VMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN1X11KernelClass16kernelClassUsed0Ev")
+    if enableLogging:
+        print "kernelClassUsed0VMAddr: " + kernelClassUsed0VMAddr
+
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces/main.kc", ["-fixups", "-arch", "x86_64"])
+    
+    # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup
+    kernelFooFixupAddr = findFixupVMAddr(kernel_cache, "kc(0) + " + kernelClassFooVMAddr + " : pointer64")
+    if enableLogging:
+        print "kernelFooFixupAddr: " + kernelFooFixupAddr
+    # Then the following fixup should be to KernelClass::kernelClassUsed0()
+    kernelFooNextFixupAddr = offsetVMAddr(kernelFooFixupAddr, 8)
+    if enableLogging:
+        print "kernelFooNextFixupAddr: " + kernelFooNextFixupAddr
+    assert kernel_cache.dictionary()["fixups"][kernelFooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMAddr + " : pointer64"
+
+    # From this point on, the vmAddr for __ZN1X11KernelClass16kernelClassUsed0Ev is an offset in to kc(0)
+    # so we want to turn it from a vmAddr to vmOffset by subtracting the base address of 0x4000 which is on __HIB
+    kernelClassUsed0VMOffset = offsetVMAddr(kernelClassUsed0VMAddr, -0x4000)
+    if enableLogging:
+        print "kernelClassUsed0VMOffset: " + kernelClassUsed0VMOffset
+
+    # -----------------------------------------------------------
+    # Now build an pageable cache using the baseline kernel collection
+    kernel_cache.buildPageableKernelCollection("x86_64", "/auxkc-pageablekc-vtable-patching-namespaces/pageable.kc", "/auxkc-pageablekc-vtable-patching-namespaces/main.kc", "/auxkc-pageablekc-vtable-patching-namespaces/extensions", ["com.apple.foo1", "com.apple.foo2"], [])
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces/pageable.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.foo1"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo2"
+
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces/pageable.kc", ["-symbols", "-arch", "x86_64"])
+    
+    # From foo1, find the vtable and its override of foo()
+    # Foo1::foo()
+    pageableFoo1FooVMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN1X4Foo13fooEv")
+    if enableLogging:
+        print "pageableFoo1FooVMAddr: " + pageableFoo1FooVMAddr
+
+    pageableFoo1FooUsed0VMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN1X4Foo19foo1Used0Ev")
+    if enableLogging:
+        print "pageableFoo1FooUsed0VMAddr: " + pageableFoo1FooUsed0VMAddr
+
+    pageableFoo1FooUsed1VMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN1X4Foo19foo1Used1Ev")
+    if enableLogging:
+        print "pageableFoo1FooUsed1VMAddr: " + pageableFoo1FooUsed1VMAddr
+
+    # From foo2, find the vtable and its override of foo()
+    # Foo2::foo()
+    pageableFoo2FooVMAddr = findSymbolVMAddr(kernel_cache, 1, "__ZN1X4Foo23fooEv")
+    if enableLogging:
+        print "pageableFoo2FooVMAddr: " + pageableFoo2FooVMAddr
+    # Also find Foo2::foo1Used0() as it overrides foo1Used0 from the superclass
+    pageableFoo2FooUsed0VMAddr = findSymbolVMAddr(kernel_cache, 1, "__ZN1X4Foo29foo1Used0Ev")
+    if enableLogging:
+        print "pageableFoo2FooUsed0VMAddr: " + pageableFoo2FooUsed0VMAddr
+
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces/pageable.kc", ["-fixups", "-arch", "x86_64"])
+    kernel_cache.dictionary()["fixups"] == "none"
+
+    # --- foo1.kext ---
+    # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1Used1() ]
+    # and we want           [ ..., foo(), kernelClassUsed0(),      foo1Used0(), foo1Used1() ]
+
+    # In vtable for Foo1, we match the entry for Foo1::foo() by looking for its value on the RHS of the fixup
+    pageableFoo1FooFixupAddr = findPagableFixupVMAddr(kernel_cache, 0, "kc(1) + " + pageableFoo1FooVMAddr)
+    if enableLogging:
+        print "pageableFoo1FooFixupAddr: " + pageableFoo1FooFixupAddr
+
+    # Then the following fixup should be to KernelClass::kernelClassUsed0()
+    pageableFoo1FooNextFixupAddr = offsetVMAddr(pageableFoo1FooFixupAddr, 8)
+    if enableLogging:
+        print "pageableFoo1FooNextFixupAddr: " + pageableFoo1FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"][pageableFoo1FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset
+
+    # Then we should have foo1Used0()
+    pageableFoo1FooNextFixupAddr = offsetVMAddr(pageableFoo1FooFixupAddr, 16)
+    if enableLogging:
+        print "pageableFoo1FooNextFixupAddr: " + pageableFoo1FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"][pageableFoo1FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed0VMAddr
+
+    # And then foo1Used1()
+    pageableFoo1FooNextFixupAddr = offsetVMAddr(pageableFoo1FooFixupAddr, 24)
+    if enableLogging:
+        print "pageableFoo1FooNextFixupAddr: " + pageableFoo1FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"][pageableFoo1FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr
+
+    # --- foo2.kext ---
+    # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1() ]
+    # and we want           [ ..., foo(), kernelClassUsed0(),      foo1Used0(), foo1Used1() ]
+
+    # In vtable for Foo2, we match the entry for Foo2::foo() by looking for its value on the RHS of the fixup
+    pageableFoo2FooFixupAddr = findPagableFixupVMAddr(kernel_cache, 1, "kc(1) + " + pageableFoo2FooVMAddr)
+    if enableLogging:
+        print "pageableFoo2FooFixupAddr: " + pageableFoo2FooFixupAddr
+
+    # Then the following fixup should be to KernelClass::kernelClassUsed0()
+    pageableFoo2FooNextFixupAddr = offsetVMAddr(pageableFoo2FooFixupAddr, 8)
+    if enableLogging:
+        print "pageableFoo2FooNextFixupAddr: " + pageableFoo2FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"][pageableFoo2FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset
+
+    # Then we should have foo1Used0(), but Foo2 overrides that, so it should be the Foo2 implementation, not the Foo1 implementation
+    pageableFoo2FooNextFixupAddr = offsetVMAddr(pageableFoo2FooFixupAddr, 16)
+    if enableLogging:
+        print "pageableFoo2FooNextFixupAddr: " + pageableFoo2FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"][pageableFoo2FooNextFixupAddr] == "kc(1) + " + pageableFoo2FooUsed0VMAddr
+
+    # And then foo1Used1()
+    pageableFoo2FooNextFixupAddr = offsetVMAddr(pageableFoo2FooFixupAddr, 24)
+    if enableLogging:
+        print "pageableFoo2FooNextFixupAddr: " + pageableFoo2FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"][pageableFoo2FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr
+
+
+    # -----------------------------------------------------------
+    # Now build an aux cache using the baseline kernel collection
+    kernel_cache.buildAuxKernelCollection("x86_64", "/auxkc-pageablekc-vtable-patching-namespaces/aux.kc", "/auxkc-pageablekc-vtable-patching-namespaces/main.kc", "/auxkc-pageablekc-vtable-patching-namespaces/pageable.kc", "/auxkc-pageablekc-vtable-patching-namespaces/extensions", ["com.apple.bar1", "com.apple.bar2"], [])
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces/aux.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar1"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar2"
+
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces/aux.kc", ["-symbols", "-arch", "x86_64"])
+
+    # From bar1, find the vtable and its override of foo()
+    # Bar1::foo()
+    auxBar1FooVMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN1X4Bar13fooEv")
+    if enableLogging:
+        print "auxBar1FooVMAddr: " + auxBar1FooVMAddr
+
+    # From bar2, find the vtable and its override of foo()
+    # Bar1::foo()
+    auxBar2FooVMAddr = findSymbolVMAddr(kernel_cache, 1, "__ZN4Bar23fooEv")
+    if enableLogging:
+        print "auxBar2FooVMAddr: " + auxBar2FooVMAddr
+
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching-namespaces/aux.kc", ["-fixups", "-arch", "x86_64"])
+
+    # --- foo1.kext ---
+    # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1Used1() ]
+    # and we want           [ ..., foo(), kernelClassUsed0(),      foo1Used0(), foo1Used1() ]
+
+    # In vtable for Bar1, we match the entry for Bar1::foo() by looking for its value on the RHS of the fixup
+    auxBar1FooFixupAddr = findAuxFixupVMAddr(kernel_cache, 0, "kc(3) + " + auxBar1FooVMAddr)
+    if enableLogging:
+        print "auxBar1FooFixupAddr: " + auxBar1FooFixupAddr
+
+    # Then the following fixup should be to KernelClass::kernelClassUsed0()
+    auxBar1FooNextFixupAddr = offsetVMAddr(auxBar1FooFixupAddr, 8)
+    if enableLogging:
+        print "auxBar1FooNextFixupAddr: " + auxBar1FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"][auxBar1FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset
+
+    # Then we should have foo1Used0() from Foo2 as it overrides it from Foo1
+    auxBar1FooNextFixupAddr = offsetVMAddr(auxBar1FooFixupAddr, 16)
+    if enableLogging:
+        print "auxBar1FooNextFixupAddr: " + auxBar1FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"][auxBar1FooNextFixupAddr] == "kc(1) + " + pageableFoo2FooUsed0VMAddr
+
+    # And then foo1Used1()
+    auxBar1FooNextFixupAddr = offsetVMAddr(auxBar1FooFixupAddr, 24)
+    if enableLogging:
+        print "auxBar1FooNextFixupAddr: " + auxBar1FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"][auxBar1FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr
+
+    # --- bar2.kext ---
+    # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1() ]
+    # and we want           [ ..., foo(), kernelClassUsed0(),      foo1Used0(), foo1Used1() ]
+
+    # In vtable for Foo2, we match the entry for Foo2::foo() by looking for its value on the RHS of the fixup
+    auxBar2FooFixupAddr = findAuxFixupVMAddr(kernel_cache, 1, "kc(3) + " + auxBar2FooVMAddr)
+    if enableLogging:
+        print "auxBar2FooFixupAddr: " + auxBar2FooFixupAddr
+
+    # Then the following fixup should be to KernelClass::kernelClassUsed0()
+    auxBar2FooNextFixupAddr = offsetVMAddr(auxBar2FooFixupAddr, 8)
+    if enableLogging:
+        print "auxBar2FooNextFixupAddr: " + auxBar2FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"][auxBar2FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset
+
+    # Then we should have foo1Used0() from Foo2 as it overrides it from Foo1
+    auxBar2FooNextFixupAddr = offsetVMAddr(auxBar2FooFixupAddr, 16)
+    if enableLogging:
+        print "auxBar2FooNextFixupAddr: " + auxBar2FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"][auxBar2FooNextFixupAddr] == "kc(1) + " + pageableFoo2FooUsed0VMAddr
+
+    # And then foo1Used1()
+    auxBar2FooNextFixupAddr = offsetVMAddr(auxBar2FooFixupAddr, 24)
+    if enableLogging:
+        print "auxBar2FooNextFixupAddr: " + auxBar2FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"][auxBar2FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp kernel.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- -std=c++11 -DKERNEL_USED=1
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo1.cpp -o extensions/foo1.kext/foo1 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1  -DFOO1_USED1=1
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo2.cpp -o extensions/foo2.kext/foo2 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-data_const bar1.cpp -o extensions/bar1.kext/bar1 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-data_const bar2.cpp -o extensions/bar2.kext/bar2 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1
+
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/SymbolSets.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/SymbolSets.plist
new file mode 100644 (file)
index 0000000..ff59c2e
--- /dev/null
@@ -0,0 +1,56 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.symbols</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTV8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN11KernelClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTV11KernelClass</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/bar1.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/bar1.cpp
new file mode 100644 (file)
index 0000000..3db027a
--- /dev/null
@@ -0,0 +1,8 @@
+
+#include "bar1.h"
+
+OSDefineMetaClassAndStructors( Bar1, Foo2 )
+
+int Bar1::foo() {
+       return 1;
+}
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/bar1.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/bar1.h
new file mode 100644 (file)
index 0000000..41dfaf0
--- /dev/null
@@ -0,0 +1,10 @@
+
+#include "foo2.h"
+
+class Bar1 : public Foo2
+{
+    OSDeclareDefaultStructors( Bar1 )
+    
+public:
+    virtual int foo() override;
+};
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/bar2.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/bar2.cpp
new file mode 100644 (file)
index 0000000..1fc3b48
--- /dev/null
@@ -0,0 +1,16 @@
+
+#include "bar1.h"
+
+class Bar2 : public Bar1
+{
+    OSDeclareDefaultStructors( Bar2 )
+    
+public:
+    virtual int foo() override;
+};
+
+OSDefineMetaClassAndStructors( Bar2, Bar1 )
+
+int Bar2::foo() {
+       return 1;
+}
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar1.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar1.kext/Info.plist
new file mode 100644 (file)
index 0000000..473c118
--- /dev/null
@@ -0,0 +1,23 @@
+<?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>CFBundleExecutable</key>
+       <string>bar1</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar1</string>
+       <key>CFBundleName</key>
+       <string>bar1</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo1</key>
+               <string>1.0</string>
+               <key>com.apple.foo2</key>
+               <string>1.0</string>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar1.kext/bar1 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar1.kext/bar1
new file mode 100755 (executable)
index 0000000..780b793
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar1.kext/bar1 differ
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar2.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar2.kext/Info.plist
new file mode 100644 (file)
index 0000000..d766b97
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleExecutable</key>
+       <string>bar2</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar2</string>
+       <key>CFBundleName</key>
+       <string>bar2</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo1</key>
+               <string>1.0</string>
+               <key>com.apple.foo2</key>
+               <string>1.0</string>
+               <key>com.apple.bar1</key>
+               <string>1.0</string>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar2.kext/bar2 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar2.kext/bar2
new file mode 100755 (executable)
index 0000000..bb78e82
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/bar2.kext/bar2 differ
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo1.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo1.kext/Info.plist
new file mode 100644 (file)
index 0000000..80ea13d
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>foo1</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo1</string>
+       <key>CFBundleName</key>
+       <string>foo1</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo1.kext/foo1 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo1.kext/foo1
new file mode 100755 (executable)
index 0000000..bf4819f
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo1.kext/foo1 differ
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo2.kext/Info.plist b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo2.kext/Info.plist
new file mode 100644 (file)
index 0000000..276b51c
--- /dev/null
@@ -0,0 +1,21 @@
+<?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>CFBundleExecutable</key>
+       <string>foo2</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo2</string>
+       <key>CFBundleName</key>
+       <string>foo2</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+               <key>com.apple.foo1</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo2.kext/foo2 b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo2.kext/foo2
new file mode 100755 (executable)
index 0000000..92926ea
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/extensions/foo2.kext/foo2 differ
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo.h
new file mode 100644 (file)
index 0000000..606653f
--- /dev/null
@@ -0,0 +1,20 @@
+
+#include "kernel.h"
+
+class Foo1 : public KernelClass
+{
+    OSDeclareDefaultStructors( Foo1 )
+    
+public:
+    virtual int foo();
+    
+#ifdef FOO1_USED
+    OSMetaClassDeclareReservedUsed(Foo1, 0);
+    virtual int foo1Used0();
+#else
+    OSMetaClassDeclareReservedUnused(Foo1, 0);
+#endif
+    OSMetaClassDeclareReservedUnused(Foo1, 1);
+    OSMetaClassDeclareReservedUnused(Foo1, 2);
+    OSMetaClassDeclareReservedUnused(Foo1, 3);
+};
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo1.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo1.cpp
new file mode 100644 (file)
index 0000000..a1de799
--- /dev/null
@@ -0,0 +1,24 @@
+
+#include "foo1.h"
+
+OSDefineMetaClassAndStructors( Foo1, KernelClass )
+
+// Index 0 has been replaced with a method
+OSMetaClassDefineReservedUsed(Foo1, 0)
+// Index 1 has been replaced with a method
+OSMetaClassDefineReservedUsed( Foo1, 1 )
+
+OSMetaClassDefineReservedUnused( Foo1, 2 )
+OSMetaClassDefineReservedUnused( Foo1, 3 )
+
+int Foo1::foo() {
+       return 0;
+}
+
+int Foo1::foo1Used0() {
+       return 0;
+}
+
+int Foo1::foo1Used1() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo1.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo1.h
new file mode 100644 (file)
index 0000000..76b479c
--- /dev/null
@@ -0,0 +1,27 @@
+
+#include "kernel.h"
+
+class Foo1 : public KernelClass
+{
+    OSDeclareDefaultStructors( Foo1 )
+    
+public:
+    virtual int foo() override;
+    
+#ifdef FOO1_USED0
+    OSMetaClassDeclareReservedUsed(Foo1, 0);
+    virtual int foo1Used0();
+#else
+    OSMetaClassDeclareReservedUnused(Foo1, 0);
+#endif
+
+#ifdef FOO1_USED1
+    OSMetaClassDeclareReservedUsed(Foo1, 1);
+    virtual int foo1Used1();
+#else
+    OSMetaClassDeclareReservedUnused(Foo1, 1);
+#endif
+
+    OSMetaClassDeclareReservedUnused(Foo1, 2);
+    OSMetaClassDeclareReservedUnused(Foo1, 3);
+};
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo2.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo2.cpp
new file mode 100644 (file)
index 0000000..e763b7e
--- /dev/null
@@ -0,0 +1,12 @@
+
+#include "foo2.h"
+
+OSDefineMetaClassAndStructors( Foo2, Foo1 )
+
+int Foo2::foo() {
+       return 0;
+}
+
+int Foo2::foo1Used0() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo2.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/foo2.h
new file mode 100644 (file)
index 0000000..410a21c
--- /dev/null
@@ -0,0 +1,12 @@
+
+#include "foo1.h"
+
+class Foo2 : public Foo1
+{
+    OSDeclareDefaultStructors( Foo2 )
+    
+public:
+    virtual int foo() override;
+
+    virtual int foo1Used0() override;
+};
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/kernel.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/kernel.cpp
new file mode 100644 (file)
index 0000000..a577f37
--- /dev/null
@@ -0,0 +1,15 @@
+
+#include "kernel.h"
+
+OSDefineMetaClassAndStructors( KernelClass, OSObject )
+
+// Index 0 has been replaced with a method
+OSMetaClassDefineReservedUsed(KernelClass, 0)
+
+int KernelClass::foo() {
+       return 0;
+}
+
+int KernelClass::kernelClassUsed0() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/kernel.h b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/kernel.h
new file mode 100644 (file)
index 0000000..9edf15c
--- /dev/null
@@ -0,0 +1,18 @@
+
+#include <Kernel/libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+class KernelClass : public OSObject
+{
+    OSDeclareDefaultStructors( KernelClass )
+    
+public:
+    virtual int foo();
+    
+#ifdef KERNEL_USED
+    OSMetaClassDeclareReservedUsed(KernelClass, 0);
+    virtual int kernelClassUsed0();
+#else
+    OSMetaClassDeclareReservedUnused(KernelClass, 0);
+#endif
+};
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/main.cpp b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/main.cpp
new file mode 100644 (file)
index 0000000..b689f8a
--- /dev/null
@@ -0,0 +1,119 @@
+
+#include <libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+int __cxa_pure_virtual = 0;
+
+#if 0
+class OSObject : public OSMetaClassBase
+{
+    OSDeclareAbstractStructors(OSObject);
+
+public:
+       OSMetaClassDeclareReservedUnused(OSObject, 0);
+    OSMetaClassDeclareReservedUnused(OSObject, 1);
+    OSMetaClassDeclareReservedUnused(OSObject, 2);
+    OSMetaClassDeclareReservedUnused(OSObject, 3);
+    OSMetaClassDeclareReservedUnused(OSObject, 4);
+    OSMetaClassDeclareReservedUnused(OSObject, 5);
+    OSMetaClassDeclareReservedUnused(OSObject, 6);
+    OSMetaClassDeclareReservedUnused(OSObject, 7);
+    OSMetaClassDeclareReservedUnused(OSObject, 8);
+    OSMetaClassDeclareReservedUnused(OSObject, 9);
+    OSMetaClassDeclareReservedUnused(OSObject, 10);
+    OSMetaClassDeclareReservedUnused(OSObject, 11);
+    OSMetaClassDeclareReservedUnused(OSObject, 12);
+    OSMetaClassDeclareReservedUnused(OSObject, 13);
+    OSMetaClassDeclareReservedUnused(OSObject, 14);
+    OSMetaClassDeclareReservedUnused(OSObject, 15);
+};
+#endif
+
+// OSDefineMetaClassAndAbstractStructors(OSObject, 0);
+/* Class global data */
+OSObject::MetaClass OSObject::gMetaClass;
+const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass;
+const OSMetaClass * const OSObject::superClass = NULL;
+
+// Virtual Padding
+OSMetaClassDefineReservedUnused(OSObject, 0);
+OSMetaClassDefineReservedUnused(OSObject, 1);
+OSMetaClassDefineReservedUnused(OSObject, 2);
+OSMetaClassDefineReservedUnused(OSObject, 3);
+OSMetaClassDefineReservedUnused(OSObject, 4);
+OSMetaClassDefineReservedUnused(OSObject, 5);
+OSMetaClassDefineReservedUnused(OSObject, 6);
+OSMetaClassDefineReservedUnused(OSObject, 7);
+OSMetaClassDefineReservedUnused(OSObject, 8);
+OSMetaClassDefineReservedUnused(OSObject, 9);
+OSMetaClassDefineReservedUnused(OSObject, 10);
+OSMetaClassDefineReservedUnused(OSObject, 11);
+OSMetaClassDefineReservedUnused(OSObject, 12);
+OSMetaClassDefineReservedUnused(OSObject, 13);
+OSMetaClassDefineReservedUnused(OSObject, 14);
+OSMetaClassDefineReservedUnused(OSObject, 15);
+
+// struct IORPC { };
+
+// OSMetaClassBase
+OSMetaClassBase::OSMetaClassBase() { }
+OSMetaClassBase::~OSMetaClassBase() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { }
+kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; }
+bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; }
+
+// OSMetaClass
+OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { }
+OSMetaClass::~OSMetaClass() { }
+void OSMetaClass::reservedCalled(int ind) const { }
+void OSMetaClass::retain() const { }
+void OSMetaClass::release() const { }
+void OSMetaClass::release(int freeWhen) const { }
+int OSMetaClass::getRetainCount() const { return 0; }
+const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; }
+void OSMetaClass::taggedRetain(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; }
+void OSMetaClass::instanceConstructed() const { }
+
+// OSMetaClass reserved methods
+void OSMetaClass::_RESERVEDOSMetaClass0() { }
+void OSMetaClass::_RESERVEDOSMetaClass1() { }
+void OSMetaClass::_RESERVEDOSMetaClass2() { }
+void OSMetaClass::_RESERVEDOSMetaClass3() { }
+void OSMetaClass::_RESERVEDOSMetaClass4() { }
+void OSMetaClass::_RESERVEDOSMetaClass5() { }
+void OSMetaClass::_RESERVEDOSMetaClass6() { }
+void OSMetaClass::_RESERVEDOSMetaClass7() { }
+
+// OSObject::MetaClass
+OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { }
+OSObject *OSObject::MetaClass::alloc() const { return NULL; }
+kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; }
+
+// OSObject
+OSObject::OSObject(OSMetaClass const*) { }
+OSObject::~OSObject() { }
+const OSMetaClass* OSObject::getMetaClass() const { return NULL; }
+void OSObject::free() { }
+bool OSObject::init() { return false; }
+void OSObject::retain() const { }
+void OSObject::release() const { }
+void OSObject::release(int freeWhen) const { }
+int OSObject::getRetainCount() const { return 0; }
+void OSObject::taggedRetain(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSObject::serialize(OSSerialize * serializer) const { return false; }
+kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; }
+void* OSObject::operator new(unsigned long) { return (void*)1; }
+void OSObject::operator delete(void*, unsigned long) { return; }
+
+__attribute__((section(("__HIB, __text"))))
+extern "C" int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/main.kernel b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/main.kernel
new file mode 100755 (executable)
index 0000000..a937770
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/main.kernel differ
diff --git a/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/test.py b/testing/kernel-cache-tests/auxkc-pageablekc-vtable-patching/test.py
new file mode 100644 (file)
index 0000000..afd49fe
--- /dev/null
@@ -0,0 +1,286 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+
+# The kernel has class OSObject and subclass KernelClass
+# foo.kext sublclasses KernelClass to get Foo1, and subclasses that to get Foo2
+# bar.kext sublclasses Foo1 to get Bar1, and subclasses that to get Bar2
+
+# In KernelClass the vtable layout is:
+# [ ..., foo() kernelClassUsed0() ]
+
+# In Foo1, the layout is:
+# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1Used1() ]
+
+# In Foo2, the layout is:
+# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1(), foo1_RESERVED2(), foo1_RESERVED3() ]
+
+# In Bar1, the layout is:
+# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1(), foo1_RESERVED2(), foo1_RESERVED3() ]
+
+# In Bar2, the layout is:
+# [ ..., foo() kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1(), foo1_RESERVED2(), foo1_RESERVED3() ]
+
+# All kext's will end up getting the vtable entry after foo() patched to kernelClassUsed0()
+# Foo2, Bar1, Bar2, will also get the vtable entry after foo1Used0() patched to foo1Used1()
+
+def findSymbolVMAddr(kernel_cache, dylib_index, symbol_name):
+    for symbol_and_addr in kernel_cache.dictionary()["dylibs"][dylib_index]["global-symbols"]:
+        if symbol_and_addr["name"] == symbol_name:
+            return symbol_and_addr["vmAddr"]
+    return None
+
+def findFixupVMAddr(kernel_cache, fixup_name):
+    for fixup_vmaddr, fixup_target in kernel_cache.dictionary()["fixups"].iteritems():
+        if fixup_target == fixup_name:
+            return fixup_vmaddr
+    return None
+
+def findPagableFixupVMAddr(kernel_cache, dylib_index, fixup_name):
+    for fixup_vmaddr, fixup_target in kernel_cache.dictionary()["dylibs"][dylib_index]["fixups"].iteritems():
+        if fixup_target == fixup_name:
+            return fixup_vmaddr
+    return None
+
+def findAuxFixupVMAddr(kernel_cache, dylib_index, fixup_name):
+    for fixup_vmaddr, fixup_target in kernel_cache.dictionary()["dylibs"][dylib_index]["fixups"].iteritems():
+        if fixup_target == fixup_name:
+            return fixup_vmaddr
+    return None
+
+def offsetVMAddr(vmAddr, offset):
+    het_int = int(vmAddr, 16)
+    het_int = het_int + offset
+    return ''.join([ '0x', hex(het_int).upper()[2:] ])
+
+def check(kernel_cache):
+    enableLogging = False
+    kernel_cache.buildKernelCollection("x86_64", "/auxkc-pageablekc-vtable-patching/main.kc", "/auxkc-pageablekc-vtable-patching/main.kernel", "", [], [])
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching/main.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching/main.kc", ["-symbols", "-arch", "x86_64"])
+    
+    # From kernel, we want to know where the vtable is, and the foo() and kernelClassUsed0() slots in that vtable
+    # KernelClass::foo()
+    kernelClassFooVMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN11KernelClass3fooEv")
+    if enableLogging:
+        print "kernelClassFooVMAddr: " + kernelClassFooVMAddr
+
+    # KernelClass::kernelClassUsed0()
+    kernelClassUsed0VMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN11KernelClass16kernelClassUsed0Ev")
+    if enableLogging:
+        print "kernelClassUsed0VMAddr: " + kernelClassUsed0VMAddr
+
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching/main.kc", ["-fixups", "-arch", "x86_64"])
+    
+    # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup
+    kernelFooFixupAddr = findFixupVMAddr(kernel_cache, "kc(0) + " + kernelClassFooVMAddr + " : pointer64")
+    if enableLogging:
+        print "kernelFooFixupAddr: " + kernelFooFixupAddr
+    # Then the following fixup should be to KernelClass::kernelClassUsed0()
+    kernelFooNextFixupAddr = offsetVMAddr(kernelFooFixupAddr, 8)
+    if enableLogging:
+        print "kernelFooNextFixupAddr: " + kernelFooNextFixupAddr
+    assert kernel_cache.dictionary()["fixups"][kernelFooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMAddr + " : pointer64"
+
+    # From this point on, the vmAddr for __ZN11KernelClass16kernelClassUsed0Ev is an offset in to kc(0)
+    # so we want to turn it from a vmAddr to vmOffset by subtracting the base address of 0x4000 which is on __HIB
+    kernelClassUsed0VMOffset = offsetVMAddr(kernelClassUsed0VMAddr, -0x4000)
+    if enableLogging:
+        print "kernelClassUsed0VMOffset: " + kernelClassUsed0VMOffset
+
+    # -----------------------------------------------------------
+    # Now build an pageable cache using the baseline kernel collection
+    kernel_cache.buildPageableKernelCollection("x86_64", "/auxkc-pageablekc-vtable-patching/pageable.kc", "/auxkc-pageablekc-vtable-patching/main.kc", "/auxkc-pageablekc-vtable-patching/extensions", ["com.apple.foo1", "com.apple.foo2"], [])
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching/pageable.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.foo1"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo2"
+
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching/pageable.kc", ["-symbols", "-arch", "x86_64"])
+    
+    # From foo1, find the vtable and its override of foo()
+    # Foo1::foo()
+    pageableFoo1FooVMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN4Foo13fooEv")
+    if enableLogging:
+        print "pageableFoo1FooVMAddr: " + pageableFoo1FooVMAddr
+
+    pageableFoo1FooUsed0VMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN4Foo19foo1Used0Ev")
+    if enableLogging:
+        print "pageableFoo1FooUsed0VMAddr: " + pageableFoo1FooUsed0VMAddr
+
+    pageableFoo1FooUsed1VMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN4Foo19foo1Used1Ev")
+    if enableLogging:
+        print "pageableFoo1FooUsed1VMAddr: " + pageableFoo1FooUsed1VMAddr
+
+    # From foo2, find the vtable and its override of foo()
+    # Foo2::foo()
+    pageableFoo2FooVMAddr = findSymbolVMAddr(kernel_cache, 1, "__ZN4Foo23fooEv")
+    if enableLogging:
+        print "pageableFoo2FooVMAddr: " + pageableFoo2FooVMAddr
+    # Also find Foo2::foo1Used0() as it overrides foo1Used0 from the superclass
+    pageableFoo2FooUsed0VMAddr = findSymbolVMAddr(kernel_cache, 1, "__ZN4Foo29foo1Used0Ev")
+    if enableLogging:
+        print "pageableFoo2FooUsed0VMAddr: " + pageableFoo2FooUsed0VMAddr
+
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching/pageable.kc", ["-fixups", "-arch", "x86_64"])
+    kernel_cache.dictionary()["fixups"] == "none"
+
+    # --- foo1.kext ---
+    # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1Used1() ]
+    # and we want           [ ..., foo(), kernelClassUsed0(),      foo1Used0(), foo1Used1() ]
+
+    # In vtable for Foo1, we match the entry for Foo1::foo() by looking for its value on the RHS of the fixup
+    pageableFoo1FooFixupAddr = findPagableFixupVMAddr(kernel_cache, 0, "kc(1) + " + pageableFoo1FooVMAddr)
+    if enableLogging:
+        print "pageableFoo1FooFixupAddr: " + pageableFoo1FooFixupAddr
+
+    # Then the following fixup should be to KernelClass::kernelClassUsed0()
+    pageableFoo1FooNextFixupAddr = offsetVMAddr(pageableFoo1FooFixupAddr, 8)
+    if enableLogging:
+        print "pageableFoo1FooNextFixupAddr: " + pageableFoo1FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"][pageableFoo1FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset
+
+    # Then we should have foo1Used0()
+    pageableFoo1FooNextFixupAddr = offsetVMAddr(pageableFoo1FooFixupAddr, 16)
+    if enableLogging:
+        print "pageableFoo1FooNextFixupAddr: " + pageableFoo1FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"][pageableFoo1FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed0VMAddr
+
+    # And then foo1Used1()
+    pageableFoo1FooNextFixupAddr = offsetVMAddr(pageableFoo1FooFixupAddr, 24)
+    if enableLogging:
+        print "pageableFoo1FooNextFixupAddr: " + pageableFoo1FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"][pageableFoo1FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr
+
+    # --- foo2.kext ---
+    # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1() ]
+    # and we want           [ ..., foo(), kernelClassUsed0(),      foo1Used0(), foo1Used1() ]
+
+    # In vtable for Foo2, we match the entry for Foo2::foo() by looking for its value on the RHS of the fixup
+    pageableFoo2FooFixupAddr = findPagableFixupVMAddr(kernel_cache, 1, "kc(1) + " + pageableFoo2FooVMAddr)
+    if enableLogging:
+        print "pageableFoo2FooFixupAddr: " + pageableFoo2FooFixupAddr
+
+    # Then the following fixup should be to KernelClass::kernelClassUsed0()
+    pageableFoo2FooNextFixupAddr = offsetVMAddr(pageableFoo2FooFixupAddr, 8)
+    if enableLogging:
+        print "pageableFoo2FooNextFixupAddr: " + pageableFoo2FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"][pageableFoo2FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset
+
+    # Then we should have foo1Used0(), but Foo2 overrides that, so it should be the Foo2 implementation, not the Foo1 implementation
+    pageableFoo2FooNextFixupAddr = offsetVMAddr(pageableFoo2FooFixupAddr, 16)
+    if enableLogging:
+        print "pageableFoo2FooNextFixupAddr: " + pageableFoo2FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"][pageableFoo2FooNextFixupAddr] == "kc(1) + " + pageableFoo2FooUsed0VMAddr
+
+    # And then foo1Used1()
+    pageableFoo2FooNextFixupAddr = offsetVMAddr(pageableFoo2FooFixupAddr, 24)
+    if enableLogging:
+        print "pageableFoo2FooNextFixupAddr: " + pageableFoo2FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"][pageableFoo2FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr
+
+
+    # -----------------------------------------------------------
+    # Now build an aux cache using the baseline kernel collection
+    kernel_cache.buildAuxKernelCollection("x86_64", "/auxkc-pageablekc-vtable-patching/aux.kc", "/auxkc-pageablekc-vtable-patching/main.kc", "/auxkc-pageablekc-vtable-patching/pageable.kc", "/auxkc-pageablekc-vtable-patching/extensions", ["com.apple.bar1", "com.apple.bar2"], [])
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching/aux.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar1"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar2"
+
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching/aux.kc", ["-symbols", "-arch", "x86_64"])
+
+    # From bar1, find the vtable and its override of foo()
+    # Bar1::foo()
+    auxBar1FooVMAddr = findSymbolVMAddr(kernel_cache, 0, "__ZN4Bar13fooEv")
+    if enableLogging:
+        print "auxBar1FooVMAddr: " + auxBar1FooVMAddr
+
+    # From bar2, find the vtable and its override of foo()
+    # Bar1::foo()
+    auxBar2FooVMAddr = findSymbolVMAddr(kernel_cache, 1, "__ZN4Bar23fooEv")
+    if enableLogging:
+        print "auxBar2FooVMAddr: " + auxBar2FooVMAddr
+
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-pageablekc-vtable-patching/aux.kc", ["-fixups", "-arch", "x86_64"])
+
+    # --- foo1.kext ---
+    # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1Used1() ]
+    # and we want           [ ..., foo(), kernelClassUsed0(),      foo1Used0(), foo1Used1() ]
+
+    # In vtable for Bar1, we match the entry for Bar1::foo() by looking for its value on the RHS of the fixup
+    auxBar1FooFixupAddr = findAuxFixupVMAddr(kernel_cache, 0, "kc(3) + " + auxBar1FooVMAddr)
+    if enableLogging:
+        print "auxBar1FooFixupAddr: " + auxBar1FooFixupAddr
+
+    # Then the following fixup should be to KernelClass::kernelClassUsed0()
+    auxBar1FooNextFixupAddr = offsetVMAddr(auxBar1FooFixupAddr, 8)
+    if enableLogging:
+        print "auxBar1FooNextFixupAddr: " + auxBar1FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"][auxBar1FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset
+
+    # Then we should have foo1Used0() from Foo2 as it overrides it from Foo1
+    auxBar1FooNextFixupAddr = offsetVMAddr(auxBar1FooFixupAddr, 16)
+    if enableLogging:
+        print "auxBar1FooNextFixupAddr: " + auxBar1FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"][auxBar1FooNextFixupAddr] == "kc(1) + " + pageableFoo2FooUsed0VMAddr
+
+    # And then foo1Used1()
+    auxBar1FooNextFixupAddr = offsetVMAddr(auxBar1FooFixupAddr, 24)
+    if enableLogging:
+        print "auxBar1FooNextFixupAddr: " + auxBar1FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"][auxBar1FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr
+
+    # --- bar2.kext ---
+    # The vtable we have is [ ..., foo(), kernelClass_RESERVED0(), foo1Used0(), foo1_RESERVED1() ]
+    # and we want           [ ..., foo(), kernelClassUsed0(),      foo1Used0(), foo1Used1() ]
+
+    # In vtable for Foo2, we match the entry for Foo2::foo() by looking for its value on the RHS of the fixup
+    auxBar2FooFixupAddr = findAuxFixupVMAddr(kernel_cache, 1, "kc(3) + " + auxBar2FooVMAddr)
+    if enableLogging:
+        print "auxBar2FooFixupAddr: " + auxBar2FooFixupAddr
+
+    # Then the following fixup should be to KernelClass::kernelClassUsed0()
+    auxBar2FooNextFixupAddr = offsetVMAddr(auxBar2FooFixupAddr, 8)
+    if enableLogging:
+        print "auxBar2FooNextFixupAddr: " + auxBar2FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"][auxBar2FooNextFixupAddr] == "kc(0) + " + kernelClassUsed0VMOffset
+
+    # Then we should have foo1Used0() from Foo2 as it overrides it from Foo1
+    auxBar2FooNextFixupAddr = offsetVMAddr(auxBar2FooFixupAddr, 16)
+    if enableLogging:
+        print "auxBar2FooNextFixupAddr: " + auxBar2FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"][auxBar2FooNextFixupAddr] == "kc(1) + " + pageableFoo2FooUsed0VMAddr
+
+    # And then foo1Used1()
+    auxBar2FooNextFixupAddr = offsetVMAddr(auxBar2FooFixupAddr, 24)
+    if enableLogging:
+        print "auxBar2FooNextFixupAddr: " + auxBar2FooNextFixupAddr
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"][auxBar2FooNextFixupAddr] == "kc(1) + " + pageableFoo1FooUsed1VMAddr
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp kernel.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- -std=c++11 -DKERNEL_USED=1
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo1.cpp -o extensions/foo1.kext/foo1 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1  -DFOO1_USED1=1
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo2.cpp -o extensions/foo2.kext/foo2 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-data_const bar1.cpp -o extensions/bar1.kext/bar1 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-data_const bar2.cpp -o extensions/bar2.kext/bar2 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -std=c++11 -DFOO1_USED0=1
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/auxkc-uuid/bar.c b/testing/kernel-cache-tests/auxkc-uuid/bar.c
new file mode 100644 (file)
index 0000000..2ad0d8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-uuid/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/auxkc-uuid/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-uuid/extensions/bar.kext/bar b/testing/kernel-cache-tests/auxkc-uuid/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..6288d91
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-uuid/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/auxkc-uuid/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-uuid/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-uuid/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-uuid/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..cd1fa31
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-uuid/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/auxkc-uuid/foo.c b/testing/kernel-cache-tests/auxkc-uuid/foo.c
new file mode 100644 (file)
index 0000000..5a6eec2
--- /dev/null
@@ -0,0 +1,4 @@
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/auxkc-uuid/main.c b/testing/kernel-cache-tests/auxkc-uuid/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-uuid/main.kernel b/testing/kernel-cache-tests/auxkc-uuid/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-uuid/main.kernel differ
diff --git a/testing/kernel-cache-tests/auxkc-uuid/test.py b/testing/kernel-cache-tests/auxkc-uuid/test.py
new file mode 100644 (file)
index 0000000..7aad4e7
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Verify that an auxKC references the UUID of the base KC
+
+def check(kernel_cache):
+    # First build a kernel collection
+    kernel_cache.buildKernelCollection("arm64", "/auxkc-uuid/main.kc", "/auxkc-uuid/main.kernel", "/auxkc-uuid/extensions", [], [])
+    kernel_cache.analyze("/auxkc-uuid/main.kc", ["-layout", "-arch", "arm64"])
+
+    # Check the kernel UUID
+    kernel_cache.analyze("/auxkc-uuid/main.kc", ["-uuid", "-arch", "arm64"])
+    kernelUUID = kernel_cache.dictionary()["uuid"]
+    assert kernelUUID != "00000000-0000-0000-0000-000000000000"
+    assert kernelUUID == kernel_cache.dictionary()["prelink-info-uuid"]
+
+    # Now build an aux cache using the baseline kernel collection
+    kernel_cache.buildAuxKernelCollection("arm64", "/auxkc-uuid/aux.kc", "/auxkc-uuid/main.kc", "", "/auxkc-uuid/extensions", ["com.apple.foo", "com.apple.bar"], [])
+
+    # Check the aux UUID
+    kernel_cache.analyze("/auxkc-uuid/aux.kc", ["-uuid", "-arch", "arm64"])
+    assert kernel_cache.dictionary()["uuid"] != "00000000-0000-0000-0000-000000000000"
+    assert kernel_cache.dictionary()["prelink-info-base-uuid"] == kernelUUID
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/SymbolSets.plist b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/SymbolSets.plist
new file mode 100644 (file)
index 0000000..73a1da6
--- /dev/null
@@ -0,0 +1,56 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.symbols</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTV8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTV11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTV15OSMetaClassBase</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..237c06f
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..f0d6164
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/foo.cpp b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/foo.cpp
new file mode 100644 (file)
index 0000000..a292275
--- /dev/null
@@ -0,0 +1,13 @@
+
+#include "foo.h"
+#include <memory.h>
+
+void* operator new(size_t size) { return (void*)1; }
+void operator delete(void*) { }
+
+OSDefineMetaClassAndStructors( Foo, OSObject )
+
+OSMetaClassDefineReservedUnused( Foo, 0 )
+OSMetaClassDefineReservedUnused( Foo, 1 )
+OSMetaClassDefineReservedUnused( Foo, 2 )
+OSMetaClassDefineReservedUnused( Foo, 3 )
diff --git a/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/foo.h b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/foo.h
new file mode 100644 (file)
index 0000000..662ad0e
--- /dev/null
@@ -0,0 +1,16 @@
+
+#include "osobject.h"
+
+class Foo : public OSObject
+{
+    OSDeclareDefaultStructors( Foo )
+    
+public:
+       // Override from OSMetaClassBase to let us find this slot in the vtable
+       virtual void placeholder() { }
+
+    OSMetaClassDeclareReservedUnused(Foo, 0);
+    OSMetaClassDeclareReservedUnused(Foo, 1);
+    OSMetaClassDeclareReservedUnused(Foo, 2);
+    OSMetaClassDeclareReservedUnused(Foo, 3);
+};
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/main.cpp b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/main.cpp
new file mode 100644 (file)
index 0000000..168e130
--- /dev/null
@@ -0,0 +1,44 @@
+
+#include "metaclass.h"
+#include "osobject.h"
+
+int __cxa_pure_virtual = 0;
+void operator delete(void*) { }
+
+OSMetaClassBase::~OSMetaClassBase() { }
+void OSMetaClassBase::placeholder() { }
+//void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { }
+//void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { }
+//void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { }
+//void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { }
+int OSMetaClassBase::metaclassBaseUsed4() { return 0; }
+int OSMetaClassBase::metaclassBaseUsed5() { return 0; }
+int OSMetaClassBase::metaclassBaseUsed6() { return 0; }
+int OSMetaClassBase::metaclassBaseUsed7() { return 0; }
+
+OSMetaClass::~OSMetaClass() { }
+void OSMetaClass::_RESERVEDOSMetaClass0() { }
+void OSMetaClass::_RESERVEDOSMetaClass1() { }
+void OSMetaClass::_RESERVEDOSMetaClass2() { }
+void OSMetaClass::_RESERVEDOSMetaClass3() { }
+void OSMetaClass::_RESERVEDOSMetaClass4() { }
+void OSMetaClass::_RESERVEDOSMetaClass5() { }
+void OSMetaClass::_RESERVEDOSMetaClass6() { }
+void OSMetaClass::_RESERVEDOSMetaClass7() { }
+
+OSObject::OSObject(const OSMetaClass *) { }
+OSObject::~OSObject() { }
+
+// OSDefineMetaClassAndAbstractStructors(OSObject, 0);
+/* Class global data */
+OSObject::MetaClass OSObject::gMetaClass;
+const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass;
+const OSMetaClass * const OSObject::superClass = NULL;
+
+OSObject::MetaClass::MetaClass() { }
+OSObject* OSObject::MetaClass::alloc() const { return NULL; }
+
+__attribute__((section(("__HIB, __text"))))
+extern "C" int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/main.kernel b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/main.kernel
new file mode 100755 (executable)
index 0000000..6653523
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/main.kernel differ
diff --git a/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/metaclass.h b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/metaclass.h
new file mode 100644 (file)
index 0000000..fb140f8
--- /dev/null
@@ -0,0 +1,116 @@
+
+#ifndef METACLASS_H
+#define METACLASS_H
+
+#define NULL 0
+#define APPLE_KEXT_OVERRIDE
+
+#define OSDeclareCommonStructors(className, dispatch)           \
+    private:                                                    \
+    static const OSMetaClass * const superClass;                \
+    public:                                                     \
+    static const OSMetaClass * const metaClass;                 \
+       static class MetaClass : public OSMetaClass {           \
+       public:                                                 \
+           MetaClass();                                        \
+           virtual OSObject *alloc() const APPLE_KEXT_OVERRIDE;\
+       } gMetaClass;                                           \
+       friend class className ::MetaClass;                     \
+    protected:                                                  \
+    className (const OSMetaClass *);                            \
+    virtual ~ className () APPLE_KEXT_OVERRIDE;
+
+#define _OSDeclareAbstractStructors(className, dispatch)                        \
+    OSDeclareCommonStructors(className, dispatch);                              \
+    private:                                                                    \
+    className (void); /* Make primary constructor private in abstract */            \
+    protected:
+
+#define OSDeclareAbstractStructorsWithDispatch(className)                       \
+_OSDeclareAbstractStructors(className, dispatch)
+
+#define OSMetaClassDefineReservedUsed(className, index)
+#define OSMetaClassDefineReservedUnused(className, index)       \
+void className ::_RESERVED ## className ## index ()  { }
+
+#define OSMetaClassDeclareReservedUsed(className, index)
+#define OSMetaClassDeclareReservedUnused(className, index)        \
+    private:                                                      \
+    virtual void _RESERVED ## className ## index ()
+
+#define _OSDeclareDefaultStructors(className, dispatch)    \
+    OSDeclareCommonStructors(className, dispatch);        \
+    public:                                     \
+    className (void);                           \
+    protected:
+
+#define OSDeclareDefaultStructors(className)   \
+_OSDeclareDefaultStructors(className, )
+
+#define OSDefineMetaClassWithInit(className, superclassName, init)            \
+       /* Class global data */                                                   \
+    className ::MetaClass className ::gMetaClass;                             \
+    const OSMetaClass * const className ::metaClass =                         \
+       & className ::gMetaClass;                                             \
+    const OSMetaClass * const className ::superClass =                        \
+       & superclassName ::gMetaClass;                                        \
+       /* Class member functions */                                              \
+    className :: className(const OSMetaClass *meta)                           \
+       : superclassName (meta) { }                                           \
+    className ::~ className() { }                                             \
+       /* The ::MetaClass constructor */                                         \
+    className ::MetaClass::MetaClass() { init; }
+
+#define OSDefineDefaultStructors(className, superclassName)     \
+    OSObject * className ::MetaClass::alloc() const { return new className; } \
+    className :: className () : superclassName (&gMetaClass) { }
+
+#define OSDefineMetaClassAndStructorsWithInit(className, superclassName, init) \
+    OSDefineMetaClassWithInit(className, superclassName, init)        \
+    OSDefineDefaultStructors(className, superclassName)
+
+#define OSDefineMetaClassAndStructors(className, superclassName)    \
+    OSDefineMetaClassAndStructorsWithInit(className, superclassName, )
+
+
+class OSMetaClassBase
+{
+public:
+       virtual ~OSMetaClassBase();
+
+       // Add a placeholder we can find later
+       virtual void placeholder();
+
+       // Virtual Padding
+#ifdef METACLASS_BASE_USED
+       OSMetaClassDeclareReservedUsed(OSMetaClassBase, 4);
+    virtual int metaclassBaseUsed4();
+       OSMetaClassDeclareReservedUsed(OSMetaClassBase, 5);
+    virtual int metaclassBaseUsed5();
+       OSMetaClassDeclareReservedUsed(OSMetaClassBase, 6);
+    virtual int metaclassBaseUsed6();
+       OSMetaClassDeclareReservedUsed(OSMetaClassBase, 7);
+    virtual int metaclassBaseUsed7();
+#else
+       virtual void _RESERVEDOSMetaClassBase4();
+       virtual void _RESERVEDOSMetaClassBase5();
+       virtual void _RESERVEDOSMetaClassBase6();
+       virtual void _RESERVEDOSMetaClassBase7();
+#endif
+};
+
+class OSMetaClass : public OSMetaClassBase {
+public:
+       virtual ~OSMetaClass();
+       // Virtual Padding functions for MetaClass's
+       OSMetaClassDeclareReservedUnused(OSMetaClass, 0);
+       OSMetaClassDeclareReservedUnused(OSMetaClass, 1);
+       OSMetaClassDeclareReservedUnused(OSMetaClass, 2);
+       OSMetaClassDeclareReservedUnused(OSMetaClass, 3);
+       OSMetaClassDeclareReservedUnused(OSMetaClass, 4);
+       OSMetaClassDeclareReservedUnused(OSMetaClass, 5);
+       OSMetaClassDeclareReservedUnused(OSMetaClass, 6);
+       OSMetaClassDeclareReservedUnused(OSMetaClass, 7);
+};
+
+#endif // METACLASS_H
diff --git a/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/osobject.h b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/osobject.h
new file mode 100644 (file)
index 0000000..187ca21
--- /dev/null
@@ -0,0 +1,8 @@
+
+
+#include "metaclass.h"
+
+class OSObject : public OSMetaClassBase
+{
+       OSDeclareAbstractStructorsWithDispatch(OSObject);
+};
diff --git a/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/test.py b/testing/kernel-cache-tests/auxkc-vtable-metaclass-patching/test.py
new file mode 100644 (file)
index 0000000..a628667
--- /dev/null
@@ -0,0 +1,81 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# OSMetaClass in the kernel has a vtable which has to be patched in to Foo::MetaClass
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/auxkc-vtable-metaclass-patching/main.kc", "/auxkc-vtable-metaclass-patching/main.kernel", None, [], [])
+    kernel_cache.analyze("/auxkc-vtable-metaclass-patching/main.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/auxkc-vtable-metaclass-patching/main.kc", ["-symbols", "-arch", "x86_64"])
+    
+    # From OSMetaClass, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][11]["name"] == "__ZN15OSMetaClassBase11placeholderEv"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][11]["vmAddr"] == "0x14BE0"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][12]["name"] == "__ZN15OSMetaClassBase18metaclassBaseUsed4Ev"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][12]["vmAddr"] == "0x14BF0"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][13]["name"] == "__ZN15OSMetaClassBase18metaclassBaseUsed5Ev"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][13]["vmAddr"] == "0x14C10"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][14]["name"] == "__ZN15OSMetaClassBase18metaclassBaseUsed6Ev"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][14]["vmAddr"] == "0x14C30"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][15]["name"] == "__ZN15OSMetaClassBase18metaclassBaseUsed7Ev"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][15]["vmAddr"] == "0x14C50"
+
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-vtable-metaclass-patching/main.kc", ["-fixups", "-arch", "x86_64"])
+    
+    # In vtable for OSMetaClass, we match the entry for OSMetaClass::placeholder() by looking for its value on the RHS of the fixup
+    assert kernel_cache.dictionary()["fixups"]["0x14168"] == "kc(0) + 0x14BE0 : pointer64"
+    # Then the following fixup should be to OSMetaClass::metaclassBaseUsed4()
+    assert kernel_cache.dictionary()["fixups"]["0x14170"] == "kc(0) + 0x14BF0 : pointer64"
+    # Then OSMetaClass::metaclassBaseUsed5()
+    assert kernel_cache.dictionary()["fixups"]["0x14178"] == "kc(0) + 0x14C10 : pointer64"
+    # Then OSMetaClass::metaclassBaseUsed6()
+    assert kernel_cache.dictionary()["fixups"]["0x14180"] == "kc(0) + 0x14C30 : pointer64"
+    # Then OSMetaClass::metaclassBaseUsed7()
+    assert kernel_cache.dictionary()["fixups"]["0x14188"] == "kc(0) + 0x14C50 : pointer64"
+
+
+    # -----------------------------------------------------------
+    # Now build an aux cache using the baseline kernel collection
+    kernel_cache.buildAuxKernelCollection("x86_64", "/auxkc-vtable-metaclass-patching/aux.kc", "/auxkc-vtable-metaclass-patching/main.kc", "", "/auxkc-vtable-metaclass-patching/extensions", ["com.apple.foo"], [])
+    kernel_cache.analyze("/auxkc-vtable-metaclass-patching/aux.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.foo"
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/auxkc-vtable-metaclass-patching/aux.kc", ["-symbols", "-arch", "x86_64"])
+    
+    # From foo.kext, find the vtable and its override of placeholder()
+    # Foo::placeholder()
+    assert kernel_cache.dictionary()["dylibs"][0]["local-symbols"][4]["name"] == "__ZN3Foo11placeholderEv"
+    assert kernel_cache.dictionary()["dylibs"][0]["local-symbols"][4]["vmAddr"] == "0xCF90"
+
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-vtable-metaclass-patching/aux.kc", ["-fixups", "-arch", "x86_64"])
+
+    # Now in foo.kext, match the entry for its Foo::placeholder() symbol
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x50"] == "kc(3) + 0xCF90"
+    # And if the patching was correct, then following entry should be to OSMetaClass::metaclassBaseUsed4()
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x58"] == "kc(0) + 0x10BF0"
+    # Then OSMetaClass::metaclassBaseUsed5()
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x60"] == "kc(0) + 0x10C10"
+    # Then OSMetaClass::metaclassBaseUsed6()
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x68"] == "kc(0) + 0x10C30"
+    # Then OSMetaClass::metaclassBaseUsed7()
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x70"] == "kc(0) + 0x10C50"
+
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- -DMETACLASS_BASE_USED=1
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.cpp -o extensions/foo.kext/foo -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/SymbolSets.plist b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/SymbolSets.plist
new file mode 100644 (file)
index 0000000..57b9a95
--- /dev/null
@@ -0,0 +1,48 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.symbols</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTV8OSObject</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/bar.cpp b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/bar.cpp
new file mode 100644 (file)
index 0000000..a87b825
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include "foo.h"
+
+class Bar : public Foo
+{
+    OSDeclareDefaultStructors( Bar )
+    
+public:
+    virtual int foo();
+};
+
+OSDefineMetaClassAndStructors( Bar, Foo )
+
+int Bar::foo() {
+       return 1;
+}
+
+int bar() {
+       Bar* bar = new Bar();
+       return bar->foo();
+}
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..911622b
--- /dev/null
@@ -0,0 +1,21 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/bar.kext/bar b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..ec61097
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..237c06f
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..30c7f3d
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/foo.cpp b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/foo.cpp
new file mode 100644 (file)
index 0000000..13f66ef
--- /dev/null
@@ -0,0 +1,29 @@
+
+#include "foo.h"
+
+OSDefineMetaClassAndStructors( Foo, OSObject )
+
+// Redefine this just so that we can write tests
+#undef OSMetaClassDefineReservedUnused
+#define OSMetaClassDefineReservedUnused(className, index)       \
+void className ::_RESERVED ## className ## index ()             \
+       { gMetaClass.reservedCalled(index); }
+
+// Index 0 has been replaced with a method
+OSMetaClassDefineReservedUsed(Foo, 0)
+OSMetaClassDefineReservedUnused( Foo, 1 )
+OSMetaClassDefineReservedUnused( Foo, 2 )
+OSMetaClassDefineReservedUnused( Foo, 3 )
+
+int Foo::foo() {
+       return 0;
+}
+
+int Foo::fooUsed0() {
+       return 0;
+}
+
+int foo() {
+       Foo* foo = new Foo();
+       return foo->foo() + foo->fooUsed0();
+}
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/foo.h b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/foo.h
new file mode 100644 (file)
index 0000000..401eeb4
--- /dev/null
@@ -0,0 +1,27 @@
+
+#include <Kernel/libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+// Redefine this just so that we can write tests
+#undef OSMetaClassDeclareReservedUnused
+#define OSMetaClassDeclareReservedUnused(className, index)        \
+    private:                                                      \
+    virtual void _RESERVED ## className ## index ()
+
+class Foo : public OSObject
+{
+    OSDeclareDefaultStructors( Foo )
+    
+public:
+    virtual int foo();
+    
+#ifdef FOO_USED
+    OSMetaClassDeclareReservedUsed(Foo, 0);
+    virtual int fooUsed0();
+#else
+    OSMetaClassDeclareReservedUnused(Foo, 0);
+#endif
+    OSMetaClassDeclareReservedUnused(Foo, 1);
+    OSMetaClassDeclareReservedUnused(Foo, 2);
+    OSMetaClassDeclareReservedUnused(Foo, 3);
+};
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/main.cpp b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/main.cpp
new file mode 100644 (file)
index 0000000..5dd800d
--- /dev/null
@@ -0,0 +1,108 @@
+
+#include <libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+int __cxa_pure_virtual = 0;
+
+#if 0
+class OSObject : public OSMetaClassBase
+{
+    OSDeclareAbstractStructors(OSObject);
+
+public:
+    OSMetaClassDeclareReservedUnused(OSObject, 0);
+    OSMetaClassDeclareReservedUnused(OSObject, 1);
+    OSMetaClassDeclareReservedUnused(OSObject, 2);
+    OSMetaClassDeclareReservedUnused(OSObject, 3);
+    OSMetaClassDeclareReservedUnused(OSObject, 4);
+    OSMetaClassDeclareReservedUnused(OSObject, 5);
+    OSMetaClassDeclareReservedUnused(OSObject, 6);
+    OSMetaClassDeclareReservedUnused(OSObject, 7);
+    OSMetaClassDeclareReservedUnused(OSObject, 8);
+    OSMetaClassDeclareReservedUnused(OSObject, 9);
+    OSMetaClassDeclareReservedUnused(OSObject, 10);
+    OSMetaClassDeclareReservedUnused(OSObject, 11);
+    OSMetaClassDeclareReservedUnused(OSObject, 12);
+    OSMetaClassDeclareReservedUnused(OSObject, 13);
+    OSMetaClassDeclareReservedUnused(OSObject, 14);
+    OSMetaClassDeclareReservedUnused(OSObject, 15);
+};
+#endif
+
+// OSDefineMetaClassAndAbstractStructors(OSObject, 0);
+/* Class global data */
+OSObject::MetaClass OSObject::gMetaClass;
+const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass;
+const OSMetaClass * const OSObject::superClass = NULL;
+
+// Virtual Padding
+OSMetaClassDefineReservedUnused(OSObject, 0);
+OSMetaClassDefineReservedUnused(OSObject, 1);
+OSMetaClassDefineReservedUnused(OSObject, 2);
+OSMetaClassDefineReservedUnused(OSObject, 3);
+OSMetaClassDefineReservedUnused(OSObject, 4);
+OSMetaClassDefineReservedUnused(OSObject, 5);
+OSMetaClassDefineReservedUnused(OSObject, 6);
+OSMetaClassDefineReservedUnused(OSObject, 7);
+OSMetaClassDefineReservedUnused(OSObject, 8);
+OSMetaClassDefineReservedUnused(OSObject, 9);
+OSMetaClassDefineReservedUnused(OSObject, 10);
+OSMetaClassDefineReservedUnused(OSObject, 11);
+OSMetaClassDefineReservedUnused(OSObject, 12);
+OSMetaClassDefineReservedUnused(OSObject, 13);
+OSMetaClassDefineReservedUnused(OSObject, 14);
+OSMetaClassDefineReservedUnused(OSObject, 15);
+
+// struct IORPC { };
+
+// OSMetaClassBase
+OSMetaClassBase::OSMetaClassBase() { }
+OSMetaClassBase::~OSMetaClassBase() { }
+kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; }
+bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; }
+
+// OSMetaClass
+OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { }
+OSMetaClass::~OSMetaClass() { }
+void OSMetaClass::reservedCalled(int ind) const { }
+void OSMetaClass::retain() const { }
+void OSMetaClass::release() const { }
+void OSMetaClass::release(int freeWhen) const { }
+int OSMetaClass::getRetainCount() const { return 0; }
+const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; }
+void OSMetaClass::taggedRetain(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; }
+void OSMetaClass::instanceConstructed() const { }
+
+// OSMetaClass reserved methods
+// Thee are none of these for arm64e
+
+// OSObject::MetaClass
+OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { }
+OSObject *OSObject::MetaClass::alloc() const { return NULL; }
+kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; }
+
+// OSObject
+OSObject::OSObject(OSMetaClass const*) { }
+OSObject::~OSObject() { }
+const OSMetaClass* OSObject::getMetaClass() const { return NULL; }
+void OSObject::free() { }
+bool OSObject::init() { return false; }
+void OSObject::retain() const { }
+void OSObject::release() const { }
+void OSObject::release(int freeWhen) const { }
+int OSObject::getRetainCount() const { return 0; }
+void OSObject::taggedRetain(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSObject::serialize(OSSerialize * serializer) const { return false; }
+kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; }
+void* OSObject::operator new(unsigned long) { return (void*)1; }
+void OSObject::operator delete(void*, unsigned long) { return; }
+
+__attribute__((section(("__TEXT_EXEC, __text"))))
+extern "C" int _start() {
+    return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/main.kernel b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/main.kernel
new file mode 100755 (executable)
index 0000000..0fb7e40
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/main.kernel differ
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/test.py b/testing/kernel-cache-tests/auxkc-vtable-patching-arm64e/test.py
new file mode 100644 (file)
index 0000000..cf3c913
--- /dev/null
@@ -0,0 +1,70 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that a kext can bind to another kext
+# foo.kext exports foo and bar.kext uses it
+# We put foo.kext in the base KC so that the patch slot in bar.kext has to know to use the correct fixup level in the fixup chain
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64e", "/auxkc-vtable-patching-arm64e/main.kc", "/auxkc-vtable-patching-arm64e/main.kernel", "/auxkc-vtable-patching-arm64e/extensions", ["com.apple.foo"], [])
+    kernel_cache.analyze("/auxkc-vtable-patching-arm64e/main.kc", ["-layout", "-arch", "arm64e"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/auxkc-vtable-patching-arm64e/main.kc", ["-symbols", "-arch", "arm64e"])
+    
+    # From foo, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable
+    # Foo::foo()
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][6]["name"] == "__ZN3Foo3fooEv"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][6]["vmAddr"] == "0x20408"
+    # Foo::fooUsed0()
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][7]["name"] == "__ZN3Foo8fooUsed0Ev"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][7]["vmAddr"] == "0x20420"
+
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-vtable-patching-arm64e/main.kc", ["-fixups", "-arch", "arm64e"])
+    
+    # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup
+    assert kernel_cache.dictionary()["fixups"]["0x200F0"] == "kc(0) + 0x20408 auth(IA addr 49764)"
+    # Then the following fixup should be to Foo::fooUsed0()
+    assert kernel_cache.dictionary()["fixups"]["0x200F8"] == "kc(0) + 0x20420 auth(IA addr 61962)"
+
+
+    # -----------------------------------------------------------
+    # Now build an aux cache using the baseline kernel collection
+    kernel_cache.buildAuxKernelCollection("arm64e", "/auxkc-vtable-patching-arm64e/aux.kc", "/auxkc-vtable-patching-arm64e/main.kc", "", "/auxkc-vtable-patching-arm64e/extensions", ["com.apple.bar"], [])
+    kernel_cache.analyze("/auxkc-vtable-patching-arm64e/aux.kc", ["-layout", "-arch", "arm64e"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar"
+
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/auxkc-vtable-patching-arm64e/aux.kc", ["-symbols", "-arch", "arm64e"])
+    
+    # From bar, find the vtable and its override of foo()
+    # Bar::foo()
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["name"] == "__ZN3Bar3fooEv"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["vmAddr"] == "0x1036C"
+
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-vtable-patching-arm64e/aux.kc", ["-fixups", "-arch", "arm64e"])
+
+    # Now in bar, again match the entry for its Bar::foo() symbol
+    assert kernel_cache.dictionary()["fixups"]["0x140E8"] == "kc(3) + 0x1036C auth(IA addr 49764)"
+    # And if the patching was correct, then following entry should be to Foo::fooUsed0()
+    assert kernel_cache.dictionary()["fixups"]["0x140F0"] == "kc(0) + 0x10420 auth(IA addr 61962)"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- -Wl,-fixup_chains -Wl,-kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.cpp -o extensions/foo.kext/foo -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -DFOO_USED=1
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/SymbolSets.plist b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/SymbolSets.plist
new file mode 100644 (file)
index 0000000..57b9a95
--- /dev/null
@@ -0,0 +1,48 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.symbols</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTV8OSObject</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/bar.cpp b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/bar.cpp
new file mode 100644 (file)
index 0000000..a87b825
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include "foo.h"
+
+class Bar : public Foo
+{
+    OSDeclareDefaultStructors( Bar )
+    
+public:
+    virtual int foo();
+};
+
+OSDefineMetaClassAndStructors( Bar, Foo )
+
+int Bar::foo() {
+       return 1;
+}
+
+int bar() {
+       Bar* bar = new Bar();
+       return bar->foo();
+}
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..911622b
--- /dev/null
@@ -0,0 +1,21 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/bar.kext/bar b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..b3bd60b
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..237c06f
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..5fe8196
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/foo.cpp b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/foo.cpp
new file mode 100644 (file)
index 0000000..83e5e67
--- /dev/null
@@ -0,0 +1,23 @@
+
+#include "foo.h"
+
+OSDefineMetaClassAndStructors( Foo, OSObject )
+
+// Index 0 has been replaced with a method
+OSMetaClassDefineReservedUsed(Foo, 0)
+OSMetaClassDefineReservedUnused( Foo, 1 )
+OSMetaClassDefineReservedUnused( Foo, 2 )
+OSMetaClassDefineReservedUnused( Foo, 3 )
+
+int Foo::foo() {
+       return 0;
+}
+
+int Foo::fooUsed0() {
+       return 0;
+}
+
+int foo() {
+       Foo* foo = new Foo();
+       return foo->foo() + foo->fooUsed0();
+}
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/foo.h b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/foo.h
new file mode 100644 (file)
index 0000000..31d6216
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include <Kernel/libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+class Foo : public OSObject
+{
+    OSDeclareDefaultStructors( Foo )
+    
+public:
+    virtual int foo();
+    
+#ifdef FOO_USED
+    OSMetaClassDeclareReservedUsed(Foo, 0);
+    virtual int fooUsed0();
+#else
+    OSMetaClassDeclareReservedUnused(Foo, 0);
+#endif
+    OSMetaClassDeclareReservedUnused(Foo, 1);
+    OSMetaClassDeclareReservedUnused(Foo, 2);
+    OSMetaClassDeclareReservedUnused(Foo, 3);
+};
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/main.cpp b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/main.cpp
new file mode 100644 (file)
index 0000000..b689f8a
--- /dev/null
@@ -0,0 +1,119 @@
+
+#include <libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+int __cxa_pure_virtual = 0;
+
+#if 0
+class OSObject : public OSMetaClassBase
+{
+    OSDeclareAbstractStructors(OSObject);
+
+public:
+       OSMetaClassDeclareReservedUnused(OSObject, 0);
+    OSMetaClassDeclareReservedUnused(OSObject, 1);
+    OSMetaClassDeclareReservedUnused(OSObject, 2);
+    OSMetaClassDeclareReservedUnused(OSObject, 3);
+    OSMetaClassDeclareReservedUnused(OSObject, 4);
+    OSMetaClassDeclareReservedUnused(OSObject, 5);
+    OSMetaClassDeclareReservedUnused(OSObject, 6);
+    OSMetaClassDeclareReservedUnused(OSObject, 7);
+    OSMetaClassDeclareReservedUnused(OSObject, 8);
+    OSMetaClassDeclareReservedUnused(OSObject, 9);
+    OSMetaClassDeclareReservedUnused(OSObject, 10);
+    OSMetaClassDeclareReservedUnused(OSObject, 11);
+    OSMetaClassDeclareReservedUnused(OSObject, 12);
+    OSMetaClassDeclareReservedUnused(OSObject, 13);
+    OSMetaClassDeclareReservedUnused(OSObject, 14);
+    OSMetaClassDeclareReservedUnused(OSObject, 15);
+};
+#endif
+
+// OSDefineMetaClassAndAbstractStructors(OSObject, 0);
+/* Class global data */
+OSObject::MetaClass OSObject::gMetaClass;
+const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass;
+const OSMetaClass * const OSObject::superClass = NULL;
+
+// Virtual Padding
+OSMetaClassDefineReservedUnused(OSObject, 0);
+OSMetaClassDefineReservedUnused(OSObject, 1);
+OSMetaClassDefineReservedUnused(OSObject, 2);
+OSMetaClassDefineReservedUnused(OSObject, 3);
+OSMetaClassDefineReservedUnused(OSObject, 4);
+OSMetaClassDefineReservedUnused(OSObject, 5);
+OSMetaClassDefineReservedUnused(OSObject, 6);
+OSMetaClassDefineReservedUnused(OSObject, 7);
+OSMetaClassDefineReservedUnused(OSObject, 8);
+OSMetaClassDefineReservedUnused(OSObject, 9);
+OSMetaClassDefineReservedUnused(OSObject, 10);
+OSMetaClassDefineReservedUnused(OSObject, 11);
+OSMetaClassDefineReservedUnused(OSObject, 12);
+OSMetaClassDefineReservedUnused(OSObject, 13);
+OSMetaClassDefineReservedUnused(OSObject, 14);
+OSMetaClassDefineReservedUnused(OSObject, 15);
+
+// struct IORPC { };
+
+// OSMetaClassBase
+OSMetaClassBase::OSMetaClassBase() { }
+OSMetaClassBase::~OSMetaClassBase() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { }
+kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; }
+bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; }
+
+// OSMetaClass
+OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { }
+OSMetaClass::~OSMetaClass() { }
+void OSMetaClass::reservedCalled(int ind) const { }
+void OSMetaClass::retain() const { }
+void OSMetaClass::release() const { }
+void OSMetaClass::release(int freeWhen) const { }
+int OSMetaClass::getRetainCount() const { return 0; }
+const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; }
+void OSMetaClass::taggedRetain(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; }
+void OSMetaClass::instanceConstructed() const { }
+
+// OSMetaClass reserved methods
+void OSMetaClass::_RESERVEDOSMetaClass0() { }
+void OSMetaClass::_RESERVEDOSMetaClass1() { }
+void OSMetaClass::_RESERVEDOSMetaClass2() { }
+void OSMetaClass::_RESERVEDOSMetaClass3() { }
+void OSMetaClass::_RESERVEDOSMetaClass4() { }
+void OSMetaClass::_RESERVEDOSMetaClass5() { }
+void OSMetaClass::_RESERVEDOSMetaClass6() { }
+void OSMetaClass::_RESERVEDOSMetaClass7() { }
+
+// OSObject::MetaClass
+OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { }
+OSObject *OSObject::MetaClass::alloc() const { return NULL; }
+kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; }
+
+// OSObject
+OSObject::OSObject(OSMetaClass const*) { }
+OSObject::~OSObject() { }
+const OSMetaClass* OSObject::getMetaClass() const { return NULL; }
+void OSObject::free() { }
+bool OSObject::init() { return false; }
+void OSObject::retain() const { }
+void OSObject::release() const { }
+void OSObject::release(int freeWhen) const { }
+int OSObject::getRetainCount() const { return 0; }
+void OSObject::taggedRetain(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSObject::serialize(OSSerialize * serializer) const { return false; }
+kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; }
+void* OSObject::operator new(unsigned long) { return (void*)1; }
+void OSObject::operator delete(void*, unsigned long) { return; }
+
+__attribute__((section(("__HIB, __text"))))
+extern "C" int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/main.kernel b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/main.kernel
new file mode 100755 (executable)
index 0000000..119060f
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/main.kernel differ
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/test.py b/testing/kernel-cache-tests/auxkc-vtable-patching-no-data-const/test.py
new file mode 100644 (file)
index 0000000..dd0a8ba
--- /dev/null
@@ -0,0 +1,73 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Note, this is the same as auxkc-vtable-patching but without __DATA_CONST.  This tests that we get the correct offsets
+# even when the vtables are in __DATA which will be mapped lower than the auxKC mach_header
+
+# This verifies that a kext can bind to another kext
+# foo.kext exports foo and bar.kext uses it
+# We put foo.kext in the base KC so that the patch slot in bar.kext has to know to use the correct fixup level in the fixup chain
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/auxkc-vtable-patching-no-data-const/main.kc", "/auxkc-vtable-patching-no-data-const/main.kernel", "/auxkc-vtable-patching-no-data-const/extensions", ["com.apple.foo"], [])
+    kernel_cache.analyze("/auxkc-vtable-patching-no-data-const/main.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/auxkc-vtable-patching-no-data-const/main.kc", ["-symbols", "-arch", "x86_64"])
+    
+    # From foo, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable
+    # Foo::foo()
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][6]["name"] == "__ZN3Foo3fooEv"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][6]["vmAddr"] == "0x16ED0"
+    # Foo::fooUsed0()
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][7]["name"] == "__ZN3Foo8fooUsed0Ev"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][7]["vmAddr"] == "0x16EF0"
+
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-vtable-patching-no-data-const/main.kc", ["-fixups", "-arch", "x86_64"])
+    
+    # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup
+    assert kernel_cache.dictionary()["fixups"]["0x1D150"] == "kc(0) + 0x16ED0"
+    # Then the following fixup should be to Foo::fooUsed0()
+    assert kernel_cache.dictionary()["fixups"]["0x1D158"] == "kc(0) + 0x16EF0"
+
+
+    # -----------------------------------------------------------
+    # Now build an aux cache using the baseline kernel collection
+    kernel_cache.buildAuxKernelCollection("x86_64", "/auxkc-vtable-patching-no-data-const/aux.kc", "/auxkc-vtable-patching-no-data-const/main.kc", "", "/auxkc-vtable-patching-no-data-const/extensions", ["com.apple.bar"], [])
+    kernel_cache.analyze("/auxkc-vtable-patching-no-data-const/aux.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar"
+
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/auxkc-vtable-patching-no-data-const/aux.kc", ["-symbols", "-arch", "x86_64"])
+    
+    # From bar, find the vtable and its override of foo()
+    # Bar::foo()
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["name"] == "__ZN3Bar3fooEv"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["vmAddr"] == "0xCF10"
+
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-vtable-patching-no-data-const/aux.kc", ["-fixups", "-arch", "x86_64"])
+
+    # Now in bar, again match the entry for its Bar::foo() symbol
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x150"] == "kc(3) + 0xCF10"
+    # And if the patching was correct, then following entry should be to Foo::fooUsed0()
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x158"] == "kc(0) + 0x12EF0"
+
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r--
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.cpp -o extensions/foo.kext/foo -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -DFOO_USED=1
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-no_data_const
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/SymbolSets.plist b/testing/kernel-cache-tests/auxkc-vtable-patching/SymbolSets.plist
new file mode 100644 (file)
index 0000000..57b9a95
--- /dev/null
@@ -0,0 +1,48 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.symbols</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTV8OSObject</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/bar.cpp b/testing/kernel-cache-tests/auxkc-vtable-patching/bar.cpp
new file mode 100644 (file)
index 0000000..a87b825
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include "foo.h"
+
+class Bar : public Foo
+{
+    OSDeclareDefaultStructors( Bar )
+    
+public:
+    virtual int foo();
+};
+
+OSDefineMetaClassAndStructors( Bar, Foo )
+
+int Bar::foo() {
+       return 1;
+}
+
+int bar() {
+       Bar* bar = new Bar();
+       return bar->foo();
+}
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..911622b
--- /dev/null
@@ -0,0 +1,21 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/bar.kext/bar b/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..0bdcc52
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..237c06f
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/foo.kext/foo b/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..5fe8196
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-patching/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/foo.cpp b/testing/kernel-cache-tests/auxkc-vtable-patching/foo.cpp
new file mode 100644 (file)
index 0000000..83e5e67
--- /dev/null
@@ -0,0 +1,23 @@
+
+#include "foo.h"
+
+OSDefineMetaClassAndStructors( Foo, OSObject )
+
+// Index 0 has been replaced with a method
+OSMetaClassDefineReservedUsed(Foo, 0)
+OSMetaClassDefineReservedUnused( Foo, 1 )
+OSMetaClassDefineReservedUnused( Foo, 2 )
+OSMetaClassDefineReservedUnused( Foo, 3 )
+
+int Foo::foo() {
+       return 0;
+}
+
+int Foo::fooUsed0() {
+       return 0;
+}
+
+int foo() {
+       Foo* foo = new Foo();
+       return foo->foo() + foo->fooUsed0();
+}
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/foo.h b/testing/kernel-cache-tests/auxkc-vtable-patching/foo.h
new file mode 100644 (file)
index 0000000..31d6216
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include <Kernel/libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+class Foo : public OSObject
+{
+    OSDeclareDefaultStructors( Foo )
+    
+public:
+    virtual int foo();
+    
+#ifdef FOO_USED
+    OSMetaClassDeclareReservedUsed(Foo, 0);
+    virtual int fooUsed0();
+#else
+    OSMetaClassDeclareReservedUnused(Foo, 0);
+#endif
+    OSMetaClassDeclareReservedUnused(Foo, 1);
+    OSMetaClassDeclareReservedUnused(Foo, 2);
+    OSMetaClassDeclareReservedUnused(Foo, 3);
+};
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/main.cpp b/testing/kernel-cache-tests/auxkc-vtable-patching/main.cpp
new file mode 100644 (file)
index 0000000..b689f8a
--- /dev/null
@@ -0,0 +1,119 @@
+
+#include <libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+int __cxa_pure_virtual = 0;
+
+#if 0
+class OSObject : public OSMetaClassBase
+{
+    OSDeclareAbstractStructors(OSObject);
+
+public:
+       OSMetaClassDeclareReservedUnused(OSObject, 0);
+    OSMetaClassDeclareReservedUnused(OSObject, 1);
+    OSMetaClassDeclareReservedUnused(OSObject, 2);
+    OSMetaClassDeclareReservedUnused(OSObject, 3);
+    OSMetaClassDeclareReservedUnused(OSObject, 4);
+    OSMetaClassDeclareReservedUnused(OSObject, 5);
+    OSMetaClassDeclareReservedUnused(OSObject, 6);
+    OSMetaClassDeclareReservedUnused(OSObject, 7);
+    OSMetaClassDeclareReservedUnused(OSObject, 8);
+    OSMetaClassDeclareReservedUnused(OSObject, 9);
+    OSMetaClassDeclareReservedUnused(OSObject, 10);
+    OSMetaClassDeclareReservedUnused(OSObject, 11);
+    OSMetaClassDeclareReservedUnused(OSObject, 12);
+    OSMetaClassDeclareReservedUnused(OSObject, 13);
+    OSMetaClassDeclareReservedUnused(OSObject, 14);
+    OSMetaClassDeclareReservedUnused(OSObject, 15);
+};
+#endif
+
+// OSDefineMetaClassAndAbstractStructors(OSObject, 0);
+/* Class global data */
+OSObject::MetaClass OSObject::gMetaClass;
+const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass;
+const OSMetaClass * const OSObject::superClass = NULL;
+
+// Virtual Padding
+OSMetaClassDefineReservedUnused(OSObject, 0);
+OSMetaClassDefineReservedUnused(OSObject, 1);
+OSMetaClassDefineReservedUnused(OSObject, 2);
+OSMetaClassDefineReservedUnused(OSObject, 3);
+OSMetaClassDefineReservedUnused(OSObject, 4);
+OSMetaClassDefineReservedUnused(OSObject, 5);
+OSMetaClassDefineReservedUnused(OSObject, 6);
+OSMetaClassDefineReservedUnused(OSObject, 7);
+OSMetaClassDefineReservedUnused(OSObject, 8);
+OSMetaClassDefineReservedUnused(OSObject, 9);
+OSMetaClassDefineReservedUnused(OSObject, 10);
+OSMetaClassDefineReservedUnused(OSObject, 11);
+OSMetaClassDefineReservedUnused(OSObject, 12);
+OSMetaClassDefineReservedUnused(OSObject, 13);
+OSMetaClassDefineReservedUnused(OSObject, 14);
+OSMetaClassDefineReservedUnused(OSObject, 15);
+
+// struct IORPC { };
+
+// OSMetaClassBase
+OSMetaClassBase::OSMetaClassBase() { }
+OSMetaClassBase::~OSMetaClassBase() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { }
+kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; }
+bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; }
+
+// OSMetaClass
+OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { }
+OSMetaClass::~OSMetaClass() { }
+void OSMetaClass::reservedCalled(int ind) const { }
+void OSMetaClass::retain() const { }
+void OSMetaClass::release() const { }
+void OSMetaClass::release(int freeWhen) const { }
+int OSMetaClass::getRetainCount() const { return 0; }
+const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; }
+void OSMetaClass::taggedRetain(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; }
+void OSMetaClass::instanceConstructed() const { }
+
+// OSMetaClass reserved methods
+void OSMetaClass::_RESERVEDOSMetaClass0() { }
+void OSMetaClass::_RESERVEDOSMetaClass1() { }
+void OSMetaClass::_RESERVEDOSMetaClass2() { }
+void OSMetaClass::_RESERVEDOSMetaClass3() { }
+void OSMetaClass::_RESERVEDOSMetaClass4() { }
+void OSMetaClass::_RESERVEDOSMetaClass5() { }
+void OSMetaClass::_RESERVEDOSMetaClass6() { }
+void OSMetaClass::_RESERVEDOSMetaClass7() { }
+
+// OSObject::MetaClass
+OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { }
+OSObject *OSObject::MetaClass::alloc() const { return NULL; }
+kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; }
+
+// OSObject
+OSObject::OSObject(OSMetaClass const*) { }
+OSObject::~OSObject() { }
+const OSMetaClass* OSObject::getMetaClass() const { return NULL; }
+void OSObject::free() { }
+bool OSObject::init() { return false; }
+void OSObject::retain() const { }
+void OSObject::release() const { }
+void OSObject::release(int freeWhen) const { }
+int OSObject::getRetainCount() const { return 0; }
+void OSObject::taggedRetain(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSObject::serialize(OSSerialize * serializer) const { return false; }
+kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; }
+void* OSObject::operator new(unsigned long) { return (void*)1; }
+void OSObject::operator delete(void*, unsigned long) { return; }
+
+__attribute__((section(("__HIB, __text"))))
+extern "C" int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/main.kernel b/testing/kernel-cache-tests/auxkc-vtable-patching/main.kernel
new file mode 100755 (executable)
index 0000000..119060f
Binary files /dev/null and b/testing/kernel-cache-tests/auxkc-vtable-patching/main.kernel differ
diff --git a/testing/kernel-cache-tests/auxkc-vtable-patching/test.py b/testing/kernel-cache-tests/auxkc-vtable-patching/test.py
new file mode 100644 (file)
index 0000000..d09f9fc
--- /dev/null
@@ -0,0 +1,70 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that a kext can bind to another kext
+# foo.kext exports foo and bar.kext uses it
+# We put foo.kext in the base KC so that the patch slot in bar.kext has to know to use the correct fixup level in the fixup chain
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/auxkc-vtable-patching/main.kc", "/auxkc-vtable-patching/main.kernel", "/auxkc-vtable-patching/extensions", ["com.apple.foo"], [])
+    kernel_cache.analyze("/auxkc-vtable-patching/main.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/auxkc-vtable-patching/main.kc", ["-symbols", "-arch", "x86_64"])
+    
+    # From foo, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable
+    # Foo::foo()
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][6]["name"] == "__ZN3Foo3fooEv"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][6]["vmAddr"] == "0x16ED0"
+    # Foo::fooUsed0()
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][7]["name"] == "__ZN3Foo8fooUsed0Ev"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][7]["vmAddr"] == "0x16EF0"
+
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-vtable-patching/main.kc", ["-fixups", "-arch", "x86_64"])
+    
+    # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup
+    assert kernel_cache.dictionary()["fixups"]["0x1D150"] == "kc(0) + 0x16ED0"
+    # Then the following fixup should be to Foo::fooUsed0()
+    assert kernel_cache.dictionary()["fixups"]["0x1D158"] == "kc(0) + 0x16EF0"
+
+
+    # -----------------------------------------------------------
+    # Now build an aux cache using the baseline kernel collection
+    kernel_cache.buildAuxKernelCollection("x86_64", "/auxkc-vtable-patching/aux.kc", "/auxkc-vtable-patching/main.kc", "", "/auxkc-vtable-patching/extensions", ["com.apple.bar"], [])
+    kernel_cache.analyze("/auxkc-vtable-patching/aux.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar"
+
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/auxkc-vtable-patching/aux.kc", ["-symbols", "-arch", "x86_64"])
+    
+    # From bar, find the vtable and its override of foo()
+    # Bar::foo()
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["name"] == "__ZN3Bar3fooEv"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["vmAddr"] == "0xCF10"
+
+
+    # Check the fixups
+    kernel_cache.analyze("/auxkc-vtable-patching/aux.kc", ["-fixups", "-arch", "x86_64"])
+
+    # Now in bar, again match the entry for its Bar::foo() symbol
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x10150"] == "kc(3) + 0xCF10"
+    # And if the patching was correct, then following entry should be to Foo::fooUsed0()
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x10158"] == "kc(0) + 0x12EF0"
+
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r--
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.cpp -o extensions/foo.kext/foo -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -DFOO_USED=1
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/bundle-ids/bar.c b/testing/kernel-cache-tests/bundle-ids/bar.c
new file mode 100644 (file)
index 0000000..691611e
--- /dev/null
@@ -0,0 +1,12 @@
+
+int g = 0;
+
+int bar() {
+       return g;
+}
+
+__typeof(&bar) barPtr = &bar;
+
+int baz() {
+       return barPtr();
+}
diff --git a/testing/kernel-cache-tests/bundle-ids/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/bundle-ids/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..77b3de8
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/bundle-ids/extensions/bar.kext/bar b/testing/kernel-cache-tests/bundle-ids/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..4467cee
Binary files /dev/null and b/testing/kernel-cache-tests/bundle-ids/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/bundle-ids/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/bundle-ids/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/bundle-ids/extensions/foo.kext/foo b/testing/kernel-cache-tests/bundle-ids/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..1e2e3c3
Binary files /dev/null and b/testing/kernel-cache-tests/bundle-ids/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/bundle-ids/foo.c b/testing/kernel-cache-tests/bundle-ids/foo.c
new file mode 100644 (file)
index 0000000..43ab00e
--- /dev/null
@@ -0,0 +1,7 @@
+
+int g = 0;
+int* gPtr = &g;
+
+int foo() {
+       return *gPtr;
+}
diff --git a/testing/kernel-cache-tests/bundle-ids/main.c b/testing/kernel-cache-tests/bundle-ids/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/bundle-ids/main.kernel b/testing/kernel-cache-tests/bundle-ids/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/bundle-ids/main.kernel differ
diff --git a/testing/kernel-cache-tests/bundle-ids/test.py b/testing/kernel-cache-tests/bundle-ids/test.py
new file mode 100644 (file)
index 0000000..cdaf2e3
--- /dev/null
@@ -0,0 +1,77 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that we are able to build a kernel collection with a kernel and kext's referenced as bundle id's
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/bundle-ids/main.kc", "/bundle-ids/main.kernel", "/bundle-ids/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/bundle-ids/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["cache-segments"]) == 6
+    assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x0"
+    assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xC000"
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0x18000"
+    assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x1C000"
+    assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0x20000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"].endswith("com.apple.kernel")
+    assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xC000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x10000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0x20000"
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"].endswith("bar")
+    assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0x14000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x1C000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["vmAddr"] == "0x20000"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][2]["name"].endswith("foo")
+    assert len(kernel_cache.dictionary()["dylibs"][2]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmAddr"] == "0x8000"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["vmAddr"] == "0x14030"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["vmAddr"] == "0x1C010"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][3]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][3]["vmAddr"] == "0x20000"
+
+    # Check the fixups
+    kernel_cache.analyze("/bundle-ids/main.kc", ["-fixups", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 2
+    assert kernel_cache.dictionary()["fixups"]["0x1C000"] == "kc(0) + 0x14000"
+    assert kernel_cache.dictionary()["fixups"]["0x1C010"] == "kc(0) + 0x1C018"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"].endswith("com.apple.kernel")
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"].endswith("bar")
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"].endswith("foo")
+    assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/bundle-libraries-arch/bar.c b/testing/kernel-cache-tests/bundle-libraries-arch/bar.c
new file mode 100644 (file)
index 0000000..2ad0d8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/bundle-libraries-arch/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/bundle-libraries-arch/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..49493c1
--- /dev/null
@@ -0,0 +1,21 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict/>
+       <key>OSBundleLibraries_arm64</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/bundle-libraries-arch/extensions/bar.kext/bar b/testing/kernel-cache-tests/bundle-libraries-arch/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..6288d91
Binary files /dev/null and b/testing/kernel-cache-tests/bundle-libraries-arch/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/bundle-libraries-arch/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/bundle-libraries-arch/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/bundle-libraries-arch/extensions/foo.kext/foo b/testing/kernel-cache-tests/bundle-libraries-arch/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..cd1fa31
Binary files /dev/null and b/testing/kernel-cache-tests/bundle-libraries-arch/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/bundle-libraries-arch/foo.c b/testing/kernel-cache-tests/bundle-libraries-arch/foo.c
new file mode 100644 (file)
index 0000000..5a6eec2
--- /dev/null
@@ -0,0 +1,4 @@
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/bundle-libraries-arch/main.c b/testing/kernel-cache-tests/bundle-libraries-arch/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/bundle-libraries-arch/main.kernel b/testing/kernel-cache-tests/bundle-libraries-arch/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/bundle-libraries-arch/main.kernel differ
diff --git a/testing/kernel-cache-tests/bundle-libraries-arch/test.py b/testing/kernel-cache-tests/bundle-libraries-arch/test.py
new file mode 100644 (file)
index 0000000..381eb20
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that OSBundleLibraries in bar's Info.plust appends the arch to find foo
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/bundle-libraries-arch/main.kc", "/bundle-libraries-arch/main.kernel", "/bundle-libraries-arch/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/bundle-libraries-arch/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+
+    # Check the fixups
+    kernel_cache.analyze("/bundle-libraries-arch/main.kc", ["-fixups", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 1
+    assert kernel_cache.dictionary()["fixups"]["0x18000"] == "kc(0) + 0x14020"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/codeless-kexts/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/codeless-kexts/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/codeless-kexts/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/codeless-kexts/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..fdef95d
--- /dev/null
@@ -0,0 +1,12 @@
+<?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>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/codeless-kexts/main.cpp b/testing/kernel-cache-tests/codeless-kexts/main.cpp
new file mode 100644 (file)
index 0000000..fbbaa3d
--- /dev/null
@@ -0,0 +1,5 @@
+
+
+extern "C" int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/codeless-kexts/main.kernel b/testing/kernel-cache-tests/codeless-kexts/main.kernel
new file mode 100755 (executable)
index 0000000..0426d15
Binary files /dev/null and b/testing/kernel-cache-tests/codeless-kexts/main.kernel differ
diff --git a/testing/kernel-cache-tests/codeless-kexts/test.py b/testing/kernel-cache-tests/codeless-kexts/test.py
new file mode 100644 (file)
index 0000000..2702e51
--- /dev/null
@@ -0,0 +1,37 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Code-less kext's have a plist, but no mach-o.  We still need to put them in the prelink info
+
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/codeless-kexts/main.kc", "/codeless-kexts/main.kernel", "/codeless-kexts/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/codeless-kexts/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["cache-segments"]) == 5
+    assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x0"
+    assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xC000"
+    assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x10000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x8000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0x10000"
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -std=c++11 -Wl,-static -mkernel -Wl,-fixup_chains -Wl,-kernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack
+
diff --git a/testing/kernel-cache-tests/ctf-arm64e/bar.c b/testing/kernel-cache-tests/ctf-arm64e/bar.c
new file mode 100644 (file)
index 0000000..691611e
--- /dev/null
@@ -0,0 +1,12 @@
+
+int g = 0;
+
+int bar() {
+       return g;
+}
+
+__typeof(&bar) barPtr = &bar;
+
+int baz() {
+       return barPtr();
+}
diff --git a/testing/kernel-cache-tests/ctf-arm64e/ctf.txt b/testing/kernel-cache-tests/ctf-arm64e/ctf.txt
new file mode 100644 (file)
index 0000000..1ed771b
--- /dev/null
@@ -0,0 +1,2048 @@
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
diff --git a/testing/kernel-cache-tests/ctf-arm64e/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/ctf-arm64e/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/ctf-arm64e/extensions/bar.kext/bar b/testing/kernel-cache-tests/ctf-arm64e/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..5d5b0e9
Binary files /dev/null and b/testing/kernel-cache-tests/ctf-arm64e/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/ctf-arm64e/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/ctf-arm64e/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/ctf-arm64e/extensions/foo.kext/foo b/testing/kernel-cache-tests/ctf-arm64e/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..e9599f1
Binary files /dev/null and b/testing/kernel-cache-tests/ctf-arm64e/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/ctf-arm64e/foo.c b/testing/kernel-cache-tests/ctf-arm64e/foo.c
new file mode 100644 (file)
index 0000000..43ab00e
--- /dev/null
@@ -0,0 +1,7 @@
+
+int g = 0;
+int* gPtr = &g;
+
+int foo() {
+       return *gPtr;
+}
diff --git a/testing/kernel-cache-tests/ctf-arm64e/main.cpp b/testing/kernel-cache-tests/ctf-arm64e/main.cpp
new file mode 100644 (file)
index 0000000..59f93f9
--- /dev/null
@@ -0,0 +1,37 @@
+
+int g = 0;
+
+static int func() {
+       return g;
+}
+
+struct S {
+       __typeof(&func) funcPtr;
+       __typeof(&func) funcPtr2;
+       int *p1;
+       __attribute__((aligned((16384)))) __typeof(&func) funcPtr3;
+       int *p2;
+};
+
+S s = { &func, &func, &g, &func, &g };
+
+struct __attribute__((packed)) PackedS {
+       int i;
+       __typeof(&func) funcPtr;        // aligned to 4
+       __typeof(&func) funcPtr2;       // aligned to 4
+       int j;
+       int *p1;                                        // aligned to 8
+       int k;
+       int *p2;                                        // aligned to 4
+};
+
+__attribute__((aligned((16384))))
+PackedS ps = { 0, &func, &func, 0, &g, 0, &g };
+
+// We want a section in __TEXT so that ctf_insert works
+__attribute__((section(("__TEXT,__const"))))
+int x = 1;
+
+extern "C" int _start() {
+       return s.funcPtr() + s.funcPtr2() + s.funcPtr3() + ps.funcPtr() + x;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/ctf-arm64e/main.kernel b/testing/kernel-cache-tests/ctf-arm64e/main.kernel
new file mode 100755 (executable)
index 0000000..78df4e1
Binary files /dev/null and b/testing/kernel-cache-tests/ctf-arm64e/main.kernel differ
diff --git a/testing/kernel-cache-tests/ctf-arm64e/test.py b/testing/kernel-cache-tests/ctf-arm64e/test.py
new file mode 100644 (file)
index 0000000..60e6822
--- /dev/null
@@ -0,0 +1,99 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This is the fixups-arm64e test, but with __CTF inserted so that we can see that CTF doesn't impact the result
+# Note the ctf.txt file is 16k just to ensure that if its vm addr wasn't updated, we'd exceed the size of __LINKEDIT with the __CTF
+
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64e", "/ctf-arm64e/main.kc", "/ctf-arm64e/main.kernel", "/ctf-arm64e/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/ctf-arm64e/main.kc", ["-layout", "-arch", "arm64e"])
+
+    assert len(kernel_cache.dictionary()["cache-segments"]) == 6
+    assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0xFFFFFFF007004000"
+    assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0xFFFFFFF007008000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xFFFFFFF007010000"
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xFFFFFFF00701C000"
+    assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0xFFFFFFF007020000"
+    assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0xFFFFFFF007030000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 5
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xFFFFFFF007010000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0xFFFFFFF007020000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0xFFFFFFF007014000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__CTF"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0xFFFFFFF007008000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmSize"] == "0x0"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["sections"][0]["vmAddr"] == "0xFFFFFFF007008000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["sections"][0]["vmSize"] == "0x0"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][4]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][4]["vmAddr"] == "0xFFFFFFF007030000"
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0xFFFFFFF007008000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0xFFFFFFF007018000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0xFFFFFFF00702C000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["vmAddr"] == "0xFFFFFFF007030000"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert len(kernel_cache.dictionary()["dylibs"][2]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmAddr"] == "0xFFFFFFF00700C000"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["vmAddr"] == "0xFFFFFFF007018040"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["vmAddr"] == "0xFFFFFFF00702C010"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][3]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][3]["vmAddr"] == "0xFFFFFFF007030000"
+
+    # Check the fixups
+    kernel_cache.analyze("/ctf-arm64e/main.kc", ["-fixups", "-arch", "arm64e"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 11
+    # main.kernel: S s = { &func, &func, &g, &func, &g };
+    assert kernel_cache.dictionary()["fixups"]["0x1C000"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)"
+    assert kernel_cache.dictionary()["fixups"]["0x1C008"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)"
+    assert kernel_cache.dictionary()["fixups"]["0x1C010"] == "kc(0) + 0xFFFFFFF00702802C"
+    assert kernel_cache.dictionary()["fixups"]["0x20000"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)"
+    assert kernel_cache.dictionary()["fixups"]["0x20008"] == "kc(0) + 0xFFFFFFF00702802C"
+    # main.kernel: PackedS ps = { 0, &func, &func, 0, &g, 0, &g };
+    assert kernel_cache.dictionary()["fixups"]["0x24004"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)"
+    assert kernel_cache.dictionary()["fixups"]["0x2400C"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)"
+    assert kernel_cache.dictionary()["fixups"]["0x24018"] == "kc(0) + 0xFFFFFFF00702802C"
+    assert kernel_cache.dictionary()["fixups"]["0x24024"] == "kc(0) + 0xFFFFFFF00702802C"
+    # bar.kext: __typeof(&bar) barPtr = &bar;
+    assert kernel_cache.dictionary()["fixups"]["0x28000"] == "kc(0) + 0xFFFFFFF007018000 auth(IA !addr 0)"
+    # foo.kext: int* gPtr = &g;
+    assert kernel_cache.dictionary()["fixups"]["0x28010"] == "kc(0) + 0xFFFFFFF00702C018"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none"
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -std=c++11 -Wl,-static -mkernel -Wl,-fixup_chains -Wl,-kernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel  -Wl,-image_base,0xfffffff007004000 -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack
+# [~]> xcrun -sdk macosx.internal ctf_insert main.kernel -arch arm64e ctf.txt -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-fixup_chains foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-fixup_chains bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/ctf-x86_64/bar.c b/testing/kernel-cache-tests/ctf-x86_64/bar.c
new file mode 100644 (file)
index 0000000..691611e
--- /dev/null
@@ -0,0 +1,12 @@
+
+int g = 0;
+
+int bar() {
+       return g;
+}
+
+__typeof(&bar) barPtr = &bar;
+
+int baz() {
+       return barPtr();
+}
diff --git a/testing/kernel-cache-tests/ctf-x86_64/ctf.txt b/testing/kernel-cache-tests/ctf-x86_64/ctf.txt
new file mode 100644 (file)
index 0000000..1ed771b
--- /dev/null
@@ -0,0 +1,2048 @@
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
+CTFdata
diff --git a/testing/kernel-cache-tests/ctf-x86_64/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/ctf-x86_64/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/ctf-x86_64/extensions/bar.kext/bar b/testing/kernel-cache-tests/ctf-x86_64/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..bb30537
Binary files /dev/null and b/testing/kernel-cache-tests/ctf-x86_64/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/ctf-x86_64/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/ctf-x86_64/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/ctf-x86_64/extensions/foo.kext/foo b/testing/kernel-cache-tests/ctf-x86_64/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..6c4d15b
Binary files /dev/null and b/testing/kernel-cache-tests/ctf-x86_64/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/ctf-x86_64/foo.c b/testing/kernel-cache-tests/ctf-x86_64/foo.c
new file mode 100644 (file)
index 0000000..43ab00e
--- /dev/null
@@ -0,0 +1,7 @@
+
+int g = 0;
+int* gPtr = &g;
+
+int foo() {
+       return *gPtr;
+}
diff --git a/testing/kernel-cache-tests/ctf-x86_64/main.cpp b/testing/kernel-cache-tests/ctf-x86_64/main.cpp
new file mode 100644 (file)
index 0000000..b3811a2
--- /dev/null
@@ -0,0 +1,36 @@
+
+int g = 0;
+
+static int func() {
+       return g;
+}
+
+struct S {
+       __typeof(&func) funcPtr;
+       __typeof(&func) funcPtr2;
+       int *p1;
+       __attribute__((aligned((16384)))) __typeof(&func) funcPtr3;
+       int *p2;
+};
+
+S s = { &func, &func, &g, &func, &g };
+
+struct __attribute__((packed)) PackedS {
+       int i;
+       __typeof(&func) funcPtr;        // aligned to 4
+       __typeof(&func) funcPtr2;       // aligned to 4
+       int j;
+       int *p1;                                        // aligned to 8
+       char k;
+       int *p2;                                        // aligned to 1
+};
+
+__attribute__((aligned((16384))))
+PackedS ps = { 0, &func, &func, 0, &g, 0, &g };
+
+__asm(".code32; .text; .section __HIB, __text; .globl _foo; _foo: movl _foo, %esp; ret");
+
+__attribute__((section(("__HIB, __text"))))
+extern "C" int _start() {
+       return s.funcPtr() + s.funcPtr2() + s.funcPtr3() + ps.funcPtr();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/ctf-x86_64/main.kernel b/testing/kernel-cache-tests/ctf-x86_64/main.kernel
new file mode 100755 (executable)
index 0000000..e7ee13e
Binary files /dev/null and b/testing/kernel-cache-tests/ctf-x86_64/main.kernel differ
diff --git a/testing/kernel-cache-tests/ctf-x86_64/test.py b/testing/kernel-cache-tests/ctf-x86_64/test.py
new file mode 100644 (file)
index 0000000..b9ac0f2
--- /dev/null
@@ -0,0 +1,102 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This is the fixups-x86_64 test, but with __CTF inserted so that we can see that CTF doesn't impact the result
+# Note the ctf.txt file is 16k just to ensure that if its vm addr wasn't updated, we'd exceed the size of __LINKEDIT with the __CTF
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/ctf-x86_64/main.kc", "/ctf-x86_64/main.kernel", "/ctf-x86_64/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/ctf-x86_64/main.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["cache-segments"]) == 7
+    assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0xFFFFFF8000200000"
+    assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0xFFFFFF8000204000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xFFFFFF8000204000"
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xFFFFFF8000208000"
+    assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0xFFFFFF800020C000"
+    assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__HIB"
+    assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0xFFFFFF8000100000"
+    assert kernel_cache.dictionary()["cache-segments"][6]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["cache-segments"][6]["vmAddr"] == "0xFFFFFF8000218000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 5
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xFFFFFF8000204000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0xFFFFFF800020C000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__HIB"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0xFFFFFF8000100000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__CTF"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0xFFFFFF8000204000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmSize"] == "0x0"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["sections"][0]["vmAddr"] == "0xFFFFFF8000204000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["sections"][0]["vmSize"] == "0x0"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][4]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][4]["vmAddr"] == "0xFFFFFF8000218000"
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0xFFFFFF8000205000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0xFFFFFF8000215000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0xFFFFFF8000218000"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert len(kernel_cache.dictionary()["dylibs"][2]["segments"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmAddr"] == "0xFFFFFF8000206000"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["vmAddr"] == "0xFFFFFF8000215010"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["vmAddr"] == "0xFFFFFF8000218000"
+
+    # Check the fixups
+    kernel_cache.analyze("/ctf-x86_64/main.kc", ["-fixups", "-arch", "x86_64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 12
+    # main.kernel: S s = { &func, &func, &g, &func, &g };
+    # _s is at 0xFFFFFF8000208000 which is offset 0x108000 from __HIB
+    assert kernel_cache.dictionary()["fixups"]["0x10C000"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x10C008"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x10C010"] == "kc(0) + 0xFFFFFF800021402C : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x110000"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x110008"] == "kc(0) + 0xFFFFFF800021402C : pointer64"
+    # main.kernel: PackedS ps = { 0, &func, &func, 0, &g, 0, &g };
+    # _ps is at 0xFFFFFF8000210000 which is offset 0x110000 from __HIB
+    assert kernel_cache.dictionary()["fixups"]["0x114004"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x11400C"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x114018"] == "kc(0) + 0xFFFFFF800021402C : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x114021"] == "kc(0) + 0xFFFFFF800021402C : pointer64"
+    # bar.kext: __typeof(&bar) barPtr = &bar;
+    # _barPtr is at 0xFFFFFF8000210030 which is offset 0x110030 from __HIB
+    assert kernel_cache.dictionary()["fixups"]["0x115000"] == "kc(0) + 0xFFFFFF8000205FE0"
+    # foo.kext: int* gPtr = &g;
+    # _gPtr is at 0xFFFFFF8000210040 which is offset 0x110040 from __HIB
+    assert kernel_cache.dictionary()["fixups"]["0x115010"] == "kc(0) + 0xFFFFFF8000215018"
+    # main.kernel: movl _foo, %esp
+    # The _foo reloc is at 0xFFFFFF8000100002 which is offset 0x2 from __HIB
+    assert kernel_cache.dictionary()["fixups"]["0x2"] == "kc(0) + 0x100000 : pointer32"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none"
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-kernel -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0xffffff8000200000 -Wl,-segaddr,__HIB,0xffffff8000100000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack
+# [~]> xcrun -sdk macosx.internal ctf_insert main.kernel -arch x86_64 ctf.txt -o main.kernel
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+
diff --git a/testing/kernel-cache-tests/data-const/bar.c b/testing/kernel-cache-tests/data-const/bar.c
new file mode 100644 (file)
index 0000000..3f90848
--- /dev/null
@@ -0,0 +1,8 @@
+
+int pack = 0;
+
+extern int foo();
+
+int bar() {
+       return foo() + pack;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/data-const/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/data-const/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/data-const/extensions/bar.kext/bar b/testing/kernel-cache-tests/data-const/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..5d8a757
Binary files /dev/null and b/testing/kernel-cache-tests/data-const/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/data-const/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/data-const/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/data-const/extensions/foo.kext/foo b/testing/kernel-cache-tests/data-const/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..af1710e
Binary files /dev/null and b/testing/kernel-cache-tests/data-const/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/data-const/foo.c b/testing/kernel-cache-tests/data-const/foo.c
new file mode 100644 (file)
index 0000000..acd9563
--- /dev/null
@@ -0,0 +1,6 @@
+
+int pack;
+
+int foo() {
+       return pack;
+}
diff --git a/testing/kernel-cache-tests/data-const/main.c b/testing/kernel-cache-tests/data-const/main.c
new file mode 100644 (file)
index 0000000..5bdda59
--- /dev/null
@@ -0,0 +1,7 @@
+
+int x = 0;
+int* g = &x;
+
+int _start() {
+       return *g;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/data-const/main.kernel b/testing/kernel-cache-tests/data-const/main.kernel
new file mode 100755 (executable)
index 0000000..d43c633
Binary files /dev/null and b/testing/kernel-cache-tests/data-const/main.kernel differ
diff --git a/testing/kernel-cache-tests/data-const/test.py b/testing/kernel-cache-tests/data-const/test.py
new file mode 100644 (file)
index 0000000..6d0bb30
--- /dev/null
@@ -0,0 +1,91 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# __DATA_CONST is r/o in xnu and r/w in the kext's but should be r/o in the final image
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/data-const/main.kc", "/data-const/main.kernel", "/data-const/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/data-const/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["cache-segments"]) == 7
+    assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x0"
+    assert kernel_cache.dictionary()["cache-segments"][0]["vmSize"] == "0x4000"
+    assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["cache-segments"][1]["vmSize"] == "0x8000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xC000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmSize"] == "0xC000"
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__DATA_CONST"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0x18000"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmSize"] == "0x8000"
+    assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__PRELINK_INFO"
+    assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x20000"
+    assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0x24000"
+    assert kernel_cache.dictionary()["cache-segments"][5]["vmSize"] == "0x8000"
+    assert kernel_cache.dictionary()["cache-segments"][6]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["cache-segments"][6]["vmAddr"] == "0x2C000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 5
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xC000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x24000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0x10000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__DATA_CONST"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0x18000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][4]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][4]["vmAddr"] == "0x2C000"
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 5
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0x14000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x28000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["name"] == "__DATA_CONST"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["vmAddr"] == "0x1C000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][4]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][4]["vmAddr"] == "0x2C000"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert len(kernel_cache.dictionary()["dylibs"][2]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmAddr"] == "0x8000"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["vmAddr"] == "0x14030"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["vmAddr"] == "0x28004"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][3]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][3]["vmAddr"] == "0x2C000"
+
+    # Check we have the correct addresses of the symbols being bound to
+    kernel_cache.analyze("/data-const/main.kc", ["-symbols", "-arch", "arm64"])
+    # int g = &x;
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["name"] == "_x"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["vmAddr"] == "0x24000"
+    # foo()
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["name"] == "_foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["vmAddr"] == "0x14030"
+
+    kernel_cache.analyze("/data-const/main.kc", ["-fixups", "-arch", "arm64"])
+    # int g = &x;
+    assert kernel_cache.dictionary()["fixups"]["0x18000"] == "kc(0) + 0x24000"
+    # foo()
+    assert kernel_cache.dictionary()["fixups"]["0x1C000"] == "kc(0) + 0x14030"
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie -Wl,-rename_section,__DATA,__data,__DATA_CONST,__data -Wl,-segprot,__DATA_CONST,r--,r-- main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/empty-bundle-dir/bar.c b/testing/kernel-cache-tests/empty-bundle-dir/bar.c
new file mode 100644 (file)
index 0000000..691611e
--- /dev/null
@@ -0,0 +1,12 @@
+
+int g = 0;
+
+int bar() {
+       return g;
+}
+
+__typeof(&bar) barPtr = &bar;
+
+int baz() {
+       return barPtr();
+}
diff --git a/testing/kernel-cache-tests/empty-bundle-dir/extensions/empty.kext/Info.plist b/testing/kernel-cache-tests/empty-bundle-dir/extensions/empty.kext/Info.plist
new file mode 100644 (file)
index 0000000..82cd176
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>empty</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.empty</string>
+       <key>CFBundleName</key>
+       <string>empty</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/empty-bundle-dir/extensions/empty.kext/PlugIns/bar.kext/Info.plist b/testing/kernel-cache-tests/empty-bundle-dir/extensions/empty.kext/PlugIns/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..77b3de8
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/empty-bundle-dir/extensions/empty.kext/PlugIns/bar.kext/bar b/testing/kernel-cache-tests/empty-bundle-dir/extensions/empty.kext/PlugIns/bar.kext/bar
new file mode 100755 (executable)
index 0000000..4467cee
Binary files /dev/null and b/testing/kernel-cache-tests/empty-bundle-dir/extensions/empty.kext/PlugIns/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/empty-bundle-dir/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/empty-bundle-dir/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/empty-bundle-dir/extensions/foo.kext/foo b/testing/kernel-cache-tests/empty-bundle-dir/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..1e2e3c3
Binary files /dev/null and b/testing/kernel-cache-tests/empty-bundle-dir/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/empty-bundle-dir/foo.c b/testing/kernel-cache-tests/empty-bundle-dir/foo.c
new file mode 100644 (file)
index 0000000..43ab00e
--- /dev/null
@@ -0,0 +1,7 @@
+
+int g = 0;
+int* gPtr = &g;
+
+int foo() {
+       return *gPtr;
+}
diff --git a/testing/kernel-cache-tests/empty-bundle-dir/main.c b/testing/kernel-cache-tests/empty-bundle-dir/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/empty-bundle-dir/main.kernel b/testing/kernel-cache-tests/empty-bundle-dir/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/empty-bundle-dir/main.kernel differ
diff --git a/testing/kernel-cache-tests/empty-bundle-dir/test.py b/testing/kernel-cache-tests/empty-bundle-dir/test.py
new file mode 100644 (file)
index 0000000..c20c570
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Make sure empty kext dirs don't crash
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/empty-bundle-dir/main.kc", "/empty-bundle-dir/main.kernel", "/empty-bundle-dir/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/empty-bundle-dir/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/empty.kext/PlugIns/bar.kext/bar
+# [~]> rm -r extensions/foo.kext/*.ld
+# [~]> rm -r extensions/empty.kext/PlugIns/bar.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/extra-prelink-info/main.c b/testing/kernel-cache-tests/extra-prelink-info/main.c
new file mode 100644 (file)
index 0000000..f545f5b
--- /dev/null
@@ -0,0 +1,13 @@
+
+// Add a large buffer so that we know we have a bunch of stuff in __DATA
+// and can more easily see that all the segments have moved around correctly, not just
+// got lucky that all are the same size
+__attribute__((used))
+char buffer[1024 * 16];
+
+int f = 0;
+int *gs[] = { &f, &f, 0, (int*)&buffer[0], (int*)&buffer[1] };
+
+int _start() {
+       return *gs[0] + *gs[1];;
+}
diff --git a/testing/kernel-cache-tests/extra-prelink-info/main.kernel b/testing/kernel-cache-tests/extra-prelink-info/main.kernel
new file mode 100755 (executable)
index 0000000..a27921c
Binary files /dev/null and b/testing/kernel-cache-tests/extra-prelink-info/main.kernel differ
diff --git a/testing/kernel-cache-tests/extra-prelink-info/prelink-info.plist b/testing/kernel-cache-tests/extra-prelink-info/prelink-info.plist
new file mode 100644 (file)
index 0000000..e244742
--- /dev/null
@@ -0,0 +1,15 @@
+<?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>key1</key>
+       <string>string value</string>
+       <key>key2</key>
+       <integer>500</integer>
+       <key>key3</key>
+       <array>
+               <string>element 0</string>
+               <string>element 1</string>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/extra-prelink-info/test.py b/testing/kernel-cache-tests/extra-prelink-info/test.py
new file mode 100755 (executable)
index 0000000..2576557
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/extra-prelink-info/main.kc", "/extra-prelink-info/main.kernel", None, [],
+                                       ["-prelink-info-extra", "$PWD/extra-prelink-info/prelink-info.plist"])
+
+    # Check the layout
+    kernel_cache.analyze("/extra-prelink-info/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["cache-segments"]) == 6
+    assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x0"
+    assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0x4000"
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel  -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack
+
diff --git a/testing/kernel-cache-tests/fixups-arm64e/bar.c b/testing/kernel-cache-tests/fixups-arm64e/bar.c
new file mode 100644 (file)
index 0000000..691611e
--- /dev/null
@@ -0,0 +1,12 @@
+
+int g = 0;
+
+int bar() {
+       return g;
+}
+
+__typeof(&bar) barPtr = &bar;
+
+int baz() {
+       return barPtr();
+}
diff --git a/testing/kernel-cache-tests/fixups-arm64e/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/fixups-arm64e/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/fixups-arm64e/extensions/bar.kext/bar b/testing/kernel-cache-tests/fixups-arm64e/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..5d5b0e9
Binary files /dev/null and b/testing/kernel-cache-tests/fixups-arm64e/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/fixups-arm64e/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/fixups-arm64e/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/fixups-arm64e/extensions/foo.kext/foo b/testing/kernel-cache-tests/fixups-arm64e/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..e9599f1
Binary files /dev/null and b/testing/kernel-cache-tests/fixups-arm64e/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/fixups-arm64e/foo.c b/testing/kernel-cache-tests/fixups-arm64e/foo.c
new file mode 100644 (file)
index 0000000..43ab00e
--- /dev/null
@@ -0,0 +1,7 @@
+
+int g = 0;
+int* gPtr = &g;
+
+int foo() {
+       return *gPtr;
+}
diff --git a/testing/kernel-cache-tests/fixups-arm64e/main.cpp b/testing/kernel-cache-tests/fixups-arm64e/main.cpp
new file mode 100644 (file)
index 0000000..e5217ce
--- /dev/null
@@ -0,0 +1,33 @@
+
+int g = 0;
+
+static int func() {
+       return g;
+}
+
+struct S {
+       __typeof(&func) funcPtr;
+       __typeof(&func) funcPtr2;
+       int *p1;
+       __attribute__((aligned((16384)))) __typeof(&func) funcPtr3;
+       int *p2;
+};
+
+S s = { &func, &func, &g, &func, &g };
+
+struct __attribute__((packed)) PackedS {
+       int i;
+       __typeof(&func) funcPtr;        // aligned to 4
+       __typeof(&func) funcPtr2;       // aligned to 4
+       int j;
+       int *p1;                                        // aligned to 8
+       int k;
+       int *p2;                                        // aligned to 4
+};
+
+__attribute__((aligned((16384))))
+PackedS ps = { 0, &func, &func, 0, &g, 0, &g };
+
+extern "C" int _start() {
+       return s.funcPtr() + s.funcPtr2() + s.funcPtr3() + ps.funcPtr();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/fixups-arm64e/main.kernel b/testing/kernel-cache-tests/fixups-arm64e/main.kernel
new file mode 100755 (executable)
index 0000000..0b24c4a
Binary files /dev/null and b/testing/kernel-cache-tests/fixups-arm64e/main.kernel differ
diff --git a/testing/kernel-cache-tests/fixups-arm64e/test.py b/testing/kernel-cache-tests/fixups-arm64e/test.py
new file mode 100644 (file)
index 0000000..a38b99b
--- /dev/null
@@ -0,0 +1,90 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64e", "/fixups-arm64e/main.kc", "/fixups-arm64e/main.kernel", "/fixups-arm64e/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/fixups-arm64e/main.kc", ["-layout", "-arch", "arm64e"])
+
+    assert len(kernel_cache.dictionary()["cache-segments"]) == 6
+    assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0xFFFFFFF007004000"
+    assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0xFFFFFFF007008000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xFFFFFFF007010000"
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xFFFFFFF00701C000"
+    assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0xFFFFFFF007020000"
+    assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0xFFFFFFF007030000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xFFFFFFF007010000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0xFFFFFFF007020000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0xFFFFFFF007014000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0xFFFFFFF007030000"
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0xFFFFFFF007008000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0xFFFFFFF007018000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0xFFFFFFF00702C000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["vmAddr"] == "0xFFFFFFF007030000"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert len(kernel_cache.dictionary()["dylibs"][2]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmAddr"] == "0xFFFFFFF00700C000"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["vmAddr"] == "0xFFFFFFF007018040"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["vmAddr"] == "0xFFFFFFF00702C010"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][3]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][3]["vmAddr"] == "0xFFFFFFF007030000"
+
+    # Check the fixups
+    kernel_cache.analyze("/fixups-arm64e/main.kc", ["-fixups", "-arch", "arm64e"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 11
+    # main.kernel: S s = { &func, &func, &g, &func, &g };
+    assert kernel_cache.dictionary()["fixups"]["0x1C000"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)"
+    assert kernel_cache.dictionary()["fixups"]["0x1C008"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)"
+    assert kernel_cache.dictionary()["fixups"]["0x1C010"] == "kc(0) + 0xFFFFFFF00702802C"
+    assert kernel_cache.dictionary()["fixups"]["0x20000"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)"
+    assert kernel_cache.dictionary()["fixups"]["0x20008"] == "kc(0) + 0xFFFFFFF00702802C"
+    # main.kernel: PackedS ps = { 0, &func, &func, 0, &g, 0, &g };
+    assert kernel_cache.dictionary()["fixups"]["0x24004"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)"
+    assert kernel_cache.dictionary()["fixups"]["0x2400C"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)"
+    assert kernel_cache.dictionary()["fixups"]["0x24018"] == "kc(0) + 0xFFFFFFF00702802C"
+    assert kernel_cache.dictionary()["fixups"]["0x24024"] == "kc(0) + 0xFFFFFFF00702802C"
+    # bar.kext: __typeof(&bar) barPtr = &bar;
+    assert kernel_cache.dictionary()["fixups"]["0x28000"] == "kc(0) + 0xFFFFFFF007018000 auth(IA !addr 0)"
+    # foo.kext: int* gPtr = &g;
+    assert kernel_cache.dictionary()["fixups"]["0x28010"] == "kc(0) + 0xFFFFFFF00702C018"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none"
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -std=c++11 -Wl,-static -mkernel -Wl,-fixup_chains -Wl,-kernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel  -Wl,-image_base,0xfffffff007004000 -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-fixup_chains foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-fixup_chains bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/fixups-x86_64-unaligned/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/fixups-x86_64-unaligned/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/fixups-x86_64-unaligned/extensions/foo.kext/foo b/testing/kernel-cache-tests/fixups-x86_64-unaligned/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..b248d69
Binary files /dev/null and b/testing/kernel-cache-tests/fixups-x86_64-unaligned/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/fixups-x86_64-unaligned/foo.c b/testing/kernel-cache-tests/fixups-x86_64-unaligned/foo.c
new file mode 100644 (file)
index 0000000..d883a14
--- /dev/null
@@ -0,0 +1,30 @@
+
+int g = 0;
+
+static int func() {
+       return g;
+}
+
+struct __attribute__((packed)) __attribute__((aligned((4096)))) PackedS {
+       int i;
+       __typeof(&func) funcPtr;        // aligned to 4
+       __typeof(&func) funcPtr2;       // aligned to 4
+       int j;
+       int *p1;                                        // aligned to 8
+       char k;
+       int *p2;                                        // aligned to 1
+};
+
+struct PackedS ps = { 0, &func, &func, 0, &g, 0, &g };
+
+struct PackedS ps_array[4] = {
+       { 0, &func, &func, 0, &g, 0, &g },
+       { 0, &func, &func, 0, &g, 0, &g },
+       { 0, &func, &func, 0, &g, 0, &g },
+       { 0, &func, &func, 0, &g, 0, &g }
+};
+
+
+int foo() {
+       return ps.funcPtr();
+}
diff --git a/testing/kernel-cache-tests/fixups-x86_64-unaligned/main.cpp b/testing/kernel-cache-tests/fixups-x86_64-unaligned/main.cpp
new file mode 100644 (file)
index 0000000..0f3943c
--- /dev/null
@@ -0,0 +1,5 @@
+
+__attribute__((section(("__HIB, __text"))))
+extern "C" int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/fixups-x86_64-unaligned/main.kernel b/testing/kernel-cache-tests/fixups-x86_64-unaligned/main.kernel
new file mode 100755 (executable)
index 0000000..ab172f1
Binary files /dev/null and b/testing/kernel-cache-tests/fixups-x86_64-unaligned/main.kernel differ
diff --git a/testing/kernel-cache-tests/fixups-x86_64-unaligned/test.py b/testing/kernel-cache-tests/fixups-x86_64-unaligned/test.py
new file mode 100644 (file)
index 0000000..1c71726
--- /dev/null
@@ -0,0 +1,61 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Test unaligned fixups in x86_64 kexts
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/fixups-x86_64-unaligned/main.kc", "/fixups-x86_64-unaligned/main.kernel", "/fixups-x86_64-unaligned/extensions", ["com.apple.foo"], [])
+    kernel_cache.analyze("/fixups-x86_64-unaligned/main.kc", ["-symbols", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    # int foo();
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][0]["name"] == "_foo"
+    foo_vmaddr_foo = kernel_cache.dictionary()["dylibs"][1]["global-symbols"][0]["vmAddr"]
+    # int g;
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][1]["name"] == "_g"
+    foo_vmaddr_g = kernel_cache.dictionary()["dylibs"][1]["global-symbols"][1]["vmAddr"]
+    # PackedS ps;
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][2]["name"] == "_ps"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][2]["vmAddr"] == "0x20C000"
+    # int func();
+    assert kernel_cache.dictionary()["dylibs"][1]["local-symbols"][0]["name"] == "_func"
+    foo_vmaddr_func = kernel_cache.dictionary()["dylibs"][1]["local-symbols"][0]["vmAddr"]
+
+    # Check the fixups
+    kernel_cache.analyze("/fixups-x86_64-unaligned/main.kc", ["-fixups", "-arch", "x86_64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 20
+    # foo.kext: PackedS ps = { 0, &func, &func, 0, &g, 0, &g };
+    # _ps is at 0x20C000 which is offset 0x10C000 from __HIB
+    assert kernel_cache.dictionary()["fixups"]["0x10C004"] == "kc(0) + " + foo_vmaddr_func
+    assert kernel_cache.dictionary()["fixups"]["0x10C00C"] == "kc(0) + " + foo_vmaddr_func
+    assert kernel_cache.dictionary()["fixups"]["0x10C018"] == "kc(0) + " + foo_vmaddr_g
+    assert kernel_cache.dictionary()["fixups"]["0x10C021"] == "kc(0) + " + foo_vmaddr_g
+    # foo.kext: PackedS ps_array = { { 0, &func, &func, 0, &g, 0, &g }, ... }
+    # _ps_array[0] is at 0x20D000 which is offset 0x10D000 from __HIB
+    assert kernel_cache.dictionary()["fixups"]["0x10D004"] == "kc(0) + " + foo_vmaddr_func
+    assert kernel_cache.dictionary()["fixups"]["0x10D00C"] == "kc(0) + " + foo_vmaddr_func
+    assert kernel_cache.dictionary()["fixups"]["0x10D018"] == "kc(0) + " + foo_vmaddr_g
+    assert kernel_cache.dictionary()["fixups"]["0x10D021"] == "kc(0) + " + foo_vmaddr_g
+    # _ps_array[1] is at 0x20E000 which is offset 0x10D000 from __HIB
+    assert kernel_cache.dictionary()["fixups"]["0x10E004"] == "kc(0) + " + foo_vmaddr_func
+    assert kernel_cache.dictionary()["fixups"]["0x10E00C"] == "kc(0) + " + foo_vmaddr_func
+    assert kernel_cache.dictionary()["fixups"]["0x10E018"] == "kc(0) + " + foo_vmaddr_g
+    assert kernel_cache.dictionary()["fixups"]["0x10E021"] == "kc(0) + " + foo_vmaddr_g
+    # _ps_array[2] is at 0x20F000 which is offset 0x10D000 from __HIB
+    assert kernel_cache.dictionary()["fixups"]["0x10F004"] == "kc(0) + " + foo_vmaddr_func
+    assert kernel_cache.dictionary()["fixups"]["0x10F00C"] == "kc(0) + " + foo_vmaddr_func
+    assert kernel_cache.dictionary()["fixups"]["0x10F018"] == "kc(0) + " + foo_vmaddr_g
+    assert kernel_cache.dictionary()["fixups"]["0x10F021"] == "kc(0) + " + foo_vmaddr_g
+    # _ps_array[3] is at 0x210000 which is offset 0x10D000 from __HIB
+    assert kernel_cache.dictionary()["fixups"]["0x110004"] == "kc(0) + " + foo_vmaddr_func
+    assert kernel_cache.dictionary()["fixups"]["0x11000C"] == "kc(0) + " + foo_vmaddr_func
+    assert kernel_cache.dictionary()["fixups"]["0x110018"] == "kc(0) + " + foo_vmaddr_g
+    assert kernel_cache.dictionary()["fixups"]["0x110021"] == "kc(0) + " + foo_vmaddr_g
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-kernel -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x200000 -Wl,-segaddr,__HIB,0x100000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+
diff --git a/testing/kernel-cache-tests/fixups-x86_64/bar.c b/testing/kernel-cache-tests/fixups-x86_64/bar.c
new file mode 100644 (file)
index 0000000..691611e
--- /dev/null
@@ -0,0 +1,12 @@
+
+int g = 0;
+
+int bar() {
+       return g;
+}
+
+__typeof(&bar) barPtr = &bar;
+
+int baz() {
+       return barPtr();
+}
diff --git a/testing/kernel-cache-tests/fixups-x86_64/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/fixups-x86_64/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/fixups-x86_64/extensions/bar.kext/bar b/testing/kernel-cache-tests/fixups-x86_64/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..bb30537
Binary files /dev/null and b/testing/kernel-cache-tests/fixups-x86_64/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/fixups-x86_64/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/fixups-x86_64/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/fixups-x86_64/extensions/foo.kext/foo b/testing/kernel-cache-tests/fixups-x86_64/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..6c4d15b
Binary files /dev/null and b/testing/kernel-cache-tests/fixups-x86_64/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/fixups-x86_64/foo.c b/testing/kernel-cache-tests/fixups-x86_64/foo.c
new file mode 100644 (file)
index 0000000..43ab00e
--- /dev/null
@@ -0,0 +1,7 @@
+
+int g = 0;
+int* gPtr = &g;
+
+int foo() {
+       return *gPtr;
+}
diff --git a/testing/kernel-cache-tests/fixups-x86_64/main.cpp b/testing/kernel-cache-tests/fixups-x86_64/main.cpp
new file mode 100644 (file)
index 0000000..b3811a2
--- /dev/null
@@ -0,0 +1,36 @@
+
+int g = 0;
+
+static int func() {
+       return g;
+}
+
+struct S {
+       __typeof(&func) funcPtr;
+       __typeof(&func) funcPtr2;
+       int *p1;
+       __attribute__((aligned((16384)))) __typeof(&func) funcPtr3;
+       int *p2;
+};
+
+S s = { &func, &func, &g, &func, &g };
+
+struct __attribute__((packed)) PackedS {
+       int i;
+       __typeof(&func) funcPtr;        // aligned to 4
+       __typeof(&func) funcPtr2;       // aligned to 4
+       int j;
+       int *p1;                                        // aligned to 8
+       char k;
+       int *p2;                                        // aligned to 1
+};
+
+__attribute__((aligned((16384))))
+PackedS ps = { 0, &func, &func, 0, &g, 0, &g };
+
+__asm(".code32; .text; .section __HIB, __text; .globl _foo; _foo: movl _foo, %esp; ret");
+
+__attribute__((section(("__HIB, __text"))))
+extern "C" int _start() {
+       return s.funcPtr() + s.funcPtr2() + s.funcPtr3() + ps.funcPtr();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/fixups-x86_64/main.kernel b/testing/kernel-cache-tests/fixups-x86_64/main.kernel
new file mode 100755 (executable)
index 0000000..f2ebee2
Binary files /dev/null and b/testing/kernel-cache-tests/fixups-x86_64/main.kernel differ
diff --git a/testing/kernel-cache-tests/fixups-x86_64/test.py b/testing/kernel-cache-tests/fixups-x86_64/test.py
new file mode 100644 (file)
index 0000000..890e7da
--- /dev/null
@@ -0,0 +1,94 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/fixups-x86_64/main.kc", "/fixups-x86_64/main.kernel", "/fixups-x86_64/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/fixups-x86_64/main.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["cache-segments"]) == 7
+    assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0xFFFFFF8000200000"
+    assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0xFFFFFF8000204000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xFFFFFF8000204000"
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xFFFFFF8000208000"
+    assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0xFFFFFF800020C000"
+    assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__HIB"
+    assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0xFFFFFF8000100000"
+    assert kernel_cache.dictionary()["cache-segments"][6]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["cache-segments"][6]["vmAddr"] == "0xFFFFFF8000218000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xFFFFFF8000204000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0xFFFFFF800020C000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__HIB"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0xFFFFFF8000100000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0xFFFFFF8000218000"
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0xFFFFFF8000205000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0xFFFFFF8000215000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0xFFFFFF8000218000"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert len(kernel_cache.dictionary()["dylibs"][2]["segments"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmAddr"] == "0xFFFFFF8000206000"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["vmAddr"] == "0xFFFFFF8000215010"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["vmAddr"] == "0xFFFFFF8000218000"
+
+    # Check the fixups
+    kernel_cache.analyze("/fixups-x86_64/main.kc", ["-fixups", "-arch", "x86_64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 12
+    # main.kernel: S s = { &func, &func, &g, &func, &g };
+    # _s is at 0xFFFFFF8000208000 which is offset 0x108000 from __HIB
+    assert kernel_cache.dictionary()["fixups"]["0x10C000"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x10C008"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x10C010"] == "kc(0) + 0xFFFFFF800021402C : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x110000"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x110008"] == "kc(0) + 0xFFFFFF800021402C : pointer64"
+    # main.kernel: PackedS ps = { 0, &func, &func, 0, &g, 0, &g };
+    # _ps is at 0xFFFFFF8000210000 which is offset 0x110000 from __HIB
+    assert kernel_cache.dictionary()["fixups"]["0x114004"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x11400C"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x114018"] == "kc(0) + 0xFFFFFF800021402C : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x114021"] == "kc(0) + 0xFFFFFF800021402C : pointer64"
+    # bar.kext: __typeof(&bar) barPtr = &bar;
+    # _barPtr is at 0xFFFFFF8000210030 which is offset 0x110030 from __HIB
+    assert kernel_cache.dictionary()["fixups"]["0x115000"] == "kc(0) + 0xFFFFFF8000205FE0"
+    # foo.kext: int* gPtr = &g;
+    # _gPtr is at 0xFFFFFF8000210040 which is offset 0x110040 from __HIB
+    assert kernel_cache.dictionary()["fixups"]["0x115010"] == "kc(0) + 0xFFFFFF8000215018"
+    # main.kernel: movl _foo, %esp
+    # The _foo reloc is at 0xFFFFFF8000100002 which is offset 0x2 from __HIB
+    assert kernel_cache.dictionary()["fixups"]["0x2"] == "kc(0) + 0x100000 : pointer32"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none"
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-kernel -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0xffffff8000200000 -Wl,-segaddr,__HIB,0xffffff8000100000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+
diff --git a/testing/kernel-cache-tests/hello-world-auxkc/bar.c b/testing/kernel-cache-tests/hello-world-auxkc/bar.c
new file mode 100644 (file)
index 0000000..2ad0d8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/hello-world-auxkc/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/hello-world-auxkc/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/hello-world-auxkc/extensions/bar.kext/bar b/testing/kernel-cache-tests/hello-world-auxkc/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..7eafc00
Binary files /dev/null and b/testing/kernel-cache-tests/hello-world-auxkc/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/hello-world-auxkc/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/hello-world-auxkc/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/hello-world-auxkc/extensions/foo.kext/foo b/testing/kernel-cache-tests/hello-world-auxkc/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..cd1fa31
Binary files /dev/null and b/testing/kernel-cache-tests/hello-world-auxkc/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/hello-world-auxkc/foo.c b/testing/kernel-cache-tests/hello-world-auxkc/foo.c
new file mode 100644 (file)
index 0000000..5a6eec2
--- /dev/null
@@ -0,0 +1,4 @@
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/hello-world-auxkc/main.c b/testing/kernel-cache-tests/hello-world-auxkc/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/hello-world-auxkc/main.kernel b/testing/kernel-cache-tests/hello-world-auxkc/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/hello-world-auxkc/main.kernel differ
diff --git a/testing/kernel-cache-tests/hello-world-auxkc/test.py b/testing/kernel-cache-tests/hello-world-auxkc/test.py
new file mode 100644 (file)
index 0000000..bb55681
--- /dev/null
@@ -0,0 +1,91 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Verify that an auxKC has a reverse vmAddr order
+
+def check(kernel_cache):
+    # First build a kernel collection
+    kernel_cache.buildKernelCollection("arm64", "/hello-world-auxkc/main.kc", "/hello-world-auxkc/main.kernel", "/hello-world-auxkc/extensions", [], [])
+    kernel_cache.analyze("/hello-world-auxkc/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["cache-segments"]) == 5
+    assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x0"
+    assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xC000"
+    assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x10000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x8000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0x10000"
+
+    # Now build an aux cache using the baseline kernel collection
+    kernel_cache.buildAuxKernelCollection("arm64", "/hello-world-auxkc/aux.kc", "/hello-world-auxkc/main.kc", "", "/hello-world-auxkc/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/hello-world-auxkc/aux.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["cache-segments"]) == 6
+    assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x8000"
+    assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0xC000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0x14000"
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0x0"
+    assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0x18000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xC000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x14000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0x18000"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0x10000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0x14020"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x18000"
+
+    # Check the fixups
+    kernel_cache.analyze("/hello-world-auxkc/aux.kc", ["-fixups", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 1
+    assert kernel_cache.dictionary()["fixups"]["0x4000"] == "kc(3) + 0x14020"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/hello-world-kernel/main.c b/testing/kernel-cache-tests/hello-world-kernel/main.c
new file mode 100644 (file)
index 0000000..f545f5b
--- /dev/null
@@ -0,0 +1,13 @@
+
+// Add a large buffer so that we know we have a bunch of stuff in __DATA
+// and can more easily see that all the segments have moved around correctly, not just
+// got lucky that all are the same size
+__attribute__((used))
+char buffer[1024 * 16];
+
+int f = 0;
+int *gs[] = { &f, &f, 0, (int*)&buffer[0], (int*)&buffer[1] };
+
+int _start() {
+       return *gs[0] + *gs[1];;
+}
diff --git a/testing/kernel-cache-tests/hello-world-kernel/main.kernel b/testing/kernel-cache-tests/hello-world-kernel/main.kernel
new file mode 100755 (executable)
index 0000000..a27921c
Binary files /dev/null and b/testing/kernel-cache-tests/hello-world-kernel/main.kernel differ
diff --git a/testing/kernel-cache-tests/hello-world-kernel/test.py b/testing/kernel-cache-tests/hello-world-kernel/test.py
new file mode 100755 (executable)
index 0000000..0fe437d
--- /dev/null
@@ -0,0 +1,55 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/hello-world-kernel/main.kc", "/hello-world-kernel/main.kernel", None, [], [])
+
+    # Check the layout
+    kernel_cache.analyze("/hello-world-kernel/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["cache-segments"]) == 6
+    assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x0"
+    assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xC000"
+    assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x10000"
+    assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0x18000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x10000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0x8000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0x18000"
+
+    # Check the entry point
+    kernel_cache.analyze("/hello-world-kernel/main.kc", ["-entrypoint", "-arch", "arm64"])
+    assert kernel_cache.dictionary()["entrypoint"] == "0x8000"
+
+    # Check the fixups
+    kernel_cache.analyze("/hello-world-kernel/main.kc", ["-fixups", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 4
+    assert kernel_cache.dictionary()["fixups"]["0x10000"] == "kc(0) + 0x10028"
+    assert kernel_cache.dictionary()["fixups"]["0x10008"] == "kc(0) + 0x10028"
+    assert kernel_cache.dictionary()["fixups"]["0x10018"] == "kc(0) + 0x1002C"
+    assert kernel_cache.dictionary()["fixups"]["0x10020"] == "kc(0) + 0x1002D"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel  -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack
+
diff --git a/testing/kernel-cache-tests/hello-world-pageablekc/bar.c b/testing/kernel-cache-tests/hello-world-pageablekc/bar.c
new file mode 100644 (file)
index 0000000..0a34978
--- /dev/null
@@ -0,0 +1,9 @@
+
+int x = 0;
+int *p = &x;
+
+extern int foo();
+
+int bar() {
+       return foo() + *p;
+}
diff --git a/testing/kernel-cache-tests/hello-world-pageablekc/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/hello-world-pageablekc/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/hello-world-pageablekc/extensions/bar.kext/bar b/testing/kernel-cache-tests/hello-world-pageablekc/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..f2afedf
Binary files /dev/null and b/testing/kernel-cache-tests/hello-world-pageablekc/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/hello-world-pageablekc/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/hello-world-pageablekc/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/hello-world-pageablekc/extensions/foo.kext/foo b/testing/kernel-cache-tests/hello-world-pageablekc/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..4b5f159
Binary files /dev/null and b/testing/kernel-cache-tests/hello-world-pageablekc/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/hello-world-pageablekc/foo.c b/testing/kernel-cache-tests/hello-world-pageablekc/foo.c
new file mode 100644 (file)
index 0000000..27dec52
--- /dev/null
@@ -0,0 +1,7 @@
+
+int x = 0;
+int *p = &x;
+
+int foo() {
+       return *p;
+}
diff --git a/testing/kernel-cache-tests/hello-world-pageablekc/main.c b/testing/kernel-cache-tests/hello-world-pageablekc/main.c
new file mode 100644 (file)
index 0000000..1023537
--- /dev/null
@@ -0,0 +1,7 @@
+
+int x;
+int *p = &x;
+
+int _start() {
+       return *p;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/hello-world-pageablekc/main.kernel b/testing/kernel-cache-tests/hello-world-pageablekc/main.kernel
new file mode 100755 (executable)
index 0000000..b15dbc6
Binary files /dev/null and b/testing/kernel-cache-tests/hello-world-pageablekc/main.kernel differ
diff --git a/testing/kernel-cache-tests/hello-world-pageablekc/test.py b/testing/kernel-cache-tests/hello-world-pageablekc/test.py
new file mode 100644 (file)
index 0000000..e585454
--- /dev/null
@@ -0,0 +1,106 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Verify that pageableKC has nothing packed on the same page
+
+def check(kernel_cache):
+    # First build a kernel collection
+    kernel_cache.buildKernelCollection("arm64", "/hello-world-pageablekc/main.kc", "/hello-world-pageablekc/main.kernel", "/hello-world-pageablekc/extensions", [], [])
+    kernel_cache.analyze("/hello-world-pageablekc/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["cache-segments"]) == 6
+    assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x0"
+    assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xC000"
+    assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x10000"
+    assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0x14000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x10000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0x8000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0x14000"
+
+    # Now build an pageable cache using the baseline kernel collection
+    kernel_cache.buildPageableKernelCollection("arm64", "/hello-world-pageablekc/pageable.kc", "/hello-world-pageablekc/main.kc", "/hello-world-pageablekc/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/hello-world-pageablekc/pageable.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["cache-segments"]) == 6
+    assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x0"
+    assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xC000"
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0x14000"
+    assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x18000"
+    assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0x20000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0xC000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0x18000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0x20000"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0x8000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0x10000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x1C000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["vmAddr"] == "0x20000"
+
+    # Check the fixups
+    kernel_cache.analyze("/hello-world-pageablekc/pageable.kc", ["-fixups", "-arch", "arm64"])
+    assert kernel_cache.dictionary()["fixups"] == ""
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["fixups"]) == 2
+    # extern int foo()
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x14000"] == "kc(1) + 0x10000"
+    # int* p = &x;
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x14008"] == "kc(1) + 0x18010"
+
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["fixups"]) == 1
+    # int* p = &x;
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"]["0x14000"] == "kc(1) + 0x1C008"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/implicit-dependencies/bar.c b/testing/kernel-cache-tests/implicit-dependencies/bar.c
new file mode 100644 (file)
index 0000000..2ad0d8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/implicit-dependencies/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/implicit-dependencies/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/implicit-dependencies/extensions/bar.kext/bar b/testing/kernel-cache-tests/implicit-dependencies/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..6288d91
Binary files /dev/null and b/testing/kernel-cache-tests/implicit-dependencies/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/implicit-dependencies/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/implicit-dependencies/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/implicit-dependencies/extensions/foo.kext/foo b/testing/kernel-cache-tests/implicit-dependencies/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..cd1fa31
Binary files /dev/null and b/testing/kernel-cache-tests/implicit-dependencies/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/implicit-dependencies/foo.c b/testing/kernel-cache-tests/implicit-dependencies/foo.c
new file mode 100644 (file)
index 0000000..5a6eec2
--- /dev/null
@@ -0,0 +1,4 @@
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/implicit-dependencies/main.c b/testing/kernel-cache-tests/implicit-dependencies/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/implicit-dependencies/main.kernel b/testing/kernel-cache-tests/implicit-dependencies/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/implicit-dependencies/main.kernel differ
diff --git a/testing/kernel-cache-tests/implicit-dependencies/test.py b/testing/kernel-cache-tests/implicit-dependencies/test.py
new file mode 100644 (file)
index 0000000..ddefa40
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# foo.kext is not listed on the command line, but should be found by the dependency in the plist in bar.kext
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/implicit-dependencies/main.kc", "/implicit-dependencies/main.kernel", "/implicit-dependencies/extensions", ["com.apple.bar"], [])
+    kernel_cache.analyze("/implicit-dependencies/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kernel-base-address-x86_64/main.c b/testing/kernel-cache-tests/kernel-base-address-x86_64/main.c
new file mode 100644 (file)
index 0000000..6e90f69
--- /dev/null
@@ -0,0 +1,19 @@
+
+int g = 2;
+
+int foo() {
+       return g;
+}
+
+__typeof(&foo) fooPtr = &foo;
+
+__attribute__((section(("__HIB, __data"))))
+int f = 1;
+
+__attribute__((section(("__HIB, __data"))))
+int* fPtr  = &f;
+
+__attribute__((section(("__HIB, __text"))))
+int _start() {
+       return f + fooPtr();
+}
diff --git a/testing/kernel-cache-tests/kernel-base-address-x86_64/main.kernel b/testing/kernel-cache-tests/kernel-base-address-x86_64/main.kernel
new file mode 100755 (executable)
index 0000000..d38d6fd
Binary files /dev/null and b/testing/kernel-cache-tests/kernel-base-address-x86_64/main.kernel differ
diff --git a/testing/kernel-cache-tests/kernel-base-address-x86_64/main.kernel.ld_m2hzX0 b/testing/kernel-cache-tests/kernel-base-address-x86_64/main.kernel.ld_m2hzX0
new file mode 100644 (file)
index 0000000..1b5d53d
Binary files /dev/null and b/testing/kernel-cache-tests/kernel-base-address-x86_64/main.kernel.ld_m2hzX0 differ
diff --git a/testing/kernel-cache-tests/kernel-base-address-x86_64/test.py b/testing/kernel-cache-tests/kernel-base-address-x86_64/test.py
new file mode 100755 (executable)
index 0000000..0d2408b
--- /dev/null
@@ -0,0 +1,55 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/kernel-base-address-x86_64/main.kc", "/kernel-base-address-x86_64/main.kernel", None, [], [])
+
+    # Check the layout
+    kernel_cache.analyze("/kernel-base-address-x86_64/main.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["cache-segments"]) == 7
+    assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0xFFFFFF8000200000"
+    assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0xFFFFFF8000204000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xFFFFFF8000204000"
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xFFFFFF8000208000"
+    assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0xFFFFFF800020C000"
+    assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__HIB"
+    assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0xFFFFFF8000100000"
+    assert kernel_cache.dictionary()["cache-segments"][6]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["cache-segments"][6]["vmAddr"] == "0xFFFFFF8000210000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xFFFFFF8000204000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0xFFFFFF800020C000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__HIB"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0xFFFFFF8000100000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0xFFFFFF8000210000"
+
+    # Check the entry point
+    kernel_cache.analyze("/kernel-base-address-x86_64/main.kc", ["-entrypoint", "-arch", "x86_64"])
+    assert kernel_cache.dictionary()["entrypoint"] == "0xFFFFFF8000100000"
+
+    # Check the fixups
+    kernel_cache.analyze("/kernel-base-address-x86_64/main.kc", ["-fixups", "-arch", "x86_64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 2
+    assert kernel_cache.dictionary()["fixups"]["0x10C008"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x30"] == "kc(0) + 0xFFFFFF8000100028 : pointer64"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0xffffff8000200000 -Wl,-segaddr,__HIB,0xffffff8000100000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack
+
diff --git a/testing/kernel-cache-tests/kernel-base-address/main.c b/testing/kernel-cache-tests/kernel-base-address/main.c
new file mode 100644 (file)
index 0000000..f545f5b
--- /dev/null
@@ -0,0 +1,13 @@
+
+// Add a large buffer so that we know we have a bunch of stuff in __DATA
+// and can more easily see that all the segments have moved around correctly, not just
+// got lucky that all are the same size
+__attribute__((used))
+char buffer[1024 * 16];
+
+int f = 0;
+int *gs[] = { &f, &f, 0, (int*)&buffer[0], (int*)&buffer[1] };
+
+int _start() {
+       return *gs[0] + *gs[1];;
+}
diff --git a/testing/kernel-cache-tests/kernel-base-address/main.kernel b/testing/kernel-cache-tests/kernel-base-address/main.kernel
new file mode 100755 (executable)
index 0000000..e9d9d00
Binary files /dev/null and b/testing/kernel-cache-tests/kernel-base-address/main.kernel differ
diff --git a/testing/kernel-cache-tests/kernel-base-address/test.py b/testing/kernel-cache-tests/kernel-base-address/test.py
new file mode 100755 (executable)
index 0000000..45de5de
--- /dev/null
@@ -0,0 +1,55 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/kernel-base-address/main.kc", "/kernel-base-address/main.kernel", None, [], [])
+
+    # Check the layout
+    kernel_cache.analyze("/kernel-base-address/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["cache-segments"]) == 6
+    assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0xFFFFFFF007004000"
+    assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0xFFFFFFF007008000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xFFFFFFF007008000"
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xFFFFFFF007010000"
+    assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0xFFFFFFF007014000"
+    assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0xFFFFFFF00701C000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xFFFFFFF007008000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0xFFFFFFF007014000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0xFFFFFFF00700C000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0xFFFFFFF00701C000"
+
+    # Check the entry point
+    kernel_cache.analyze("/kernel-base-address/main.kc", ["-entrypoint", "-arch", "arm64"])
+    assert kernel_cache.dictionary()["entrypoint"] == "0xFFFFFFF00700C000"
+
+    # Check the fixups
+    kernel_cache.analyze("/kernel-base-address/main.kc", ["-fixups", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 4
+    assert kernel_cache.dictionary()["fixups"]["0x10000"] == "kc(0) + 0xFFFFFFF007014028"
+    assert kernel_cache.dictionary()["fixups"]["0x10008"] == "kc(0) + 0xFFFFFFF007014028"
+    assert kernel_cache.dictionary()["fixups"]["0x10018"] == "kc(0) + 0xFFFFFFF00701402C"
+    assert kernel_cache.dictionary()["fixups"]["0x10020"] == "kc(0) + 0xFFFFFFF00701402D"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel  -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -Wl,-image_base,0xfffffff007004000
+
diff --git a/testing/kernel-cache-tests/kernel-uuid/main.c b/testing/kernel-cache-tests/kernel-uuid/main.c
new file mode 100644 (file)
index 0000000..f545f5b
--- /dev/null
@@ -0,0 +1,13 @@
+
+// Add a large buffer so that we know we have a bunch of stuff in __DATA
+// and can more easily see that all the segments have moved around correctly, not just
+// got lucky that all are the same size
+__attribute__((used))
+char buffer[1024 * 16];
+
+int f = 0;
+int *gs[] = { &f, &f, 0, (int*)&buffer[0], (int*)&buffer[1] };
+
+int _start() {
+       return *gs[0] + *gs[1];;
+}
diff --git a/testing/kernel-cache-tests/kernel-uuid/main.kernel b/testing/kernel-cache-tests/kernel-uuid/main.kernel
new file mode 100755 (executable)
index 0000000..a27921c
Binary files /dev/null and b/testing/kernel-cache-tests/kernel-uuid/main.kernel differ
diff --git a/testing/kernel-cache-tests/kernel-uuid/test.py b/testing/kernel-cache-tests/kernel-uuid/test.py
new file mode 100755 (executable)
index 0000000..4847ad1
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/kernel-uuid/main.kc", "/kernel-uuid/main.kernel", None, [], [])
+
+    # Check the UUID
+    kernel_cache.analyze("/kernel-uuid/main.kc", ["-uuid", "-arch", "arm64"])
+    assert kernel_cache.dictionary()["uuid"] != "00000000-0000-0000-0000-000000000000"
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel  -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack
+
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/SymbolSets.plist b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/SymbolSets.plist
new file mode 100644 (file)
index 0000000..e80d5d5
--- /dev/null
@@ -0,0 +1,56 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.symbols</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTV8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN3Foo</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolName</key>
+                                       <string>__ZTV3Foo</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/bar.cpp b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/bar.cpp
new file mode 100644 (file)
index 0000000..2082782
--- /dev/null
@@ -0,0 +1,23 @@
+
+#include "foo.h"
+
+class Bar : public Foo
+{
+    OSDeclareDefaultStructors( Bar )
+    
+public:
+    virtual int foo();
+
+    virtual int replaced_with_fooUsed0();
+};
+
+OSDefineMetaClassAndStructors( Bar, Foo )
+
+int Bar::foo() {
+       return 1;
+}
+
+int bar() {
+       Bar* bar = new Bar();
+       return bar->foo();
+}
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..917ef6c
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/extensions/bar.kext/bar b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..4c4073d
Binary files /dev/null and b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/foo.cpp b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/foo.cpp
new file mode 100644 (file)
index 0000000..83e5e67
--- /dev/null
@@ -0,0 +1,23 @@
+
+#include "foo.h"
+
+OSDefineMetaClassAndStructors( Foo, OSObject )
+
+// Index 0 has been replaced with a method
+OSMetaClassDefineReservedUsed(Foo, 0)
+OSMetaClassDefineReservedUnused( Foo, 1 )
+OSMetaClassDefineReservedUnused( Foo, 2 )
+OSMetaClassDefineReservedUnused( Foo, 3 )
+
+int Foo::foo() {
+       return 0;
+}
+
+int Foo::fooUsed0() {
+       return 0;
+}
+
+int foo() {
+       Foo* foo = new Foo();
+       return foo->foo() + foo->fooUsed0();
+}
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/foo.h b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/foo.h
new file mode 100644 (file)
index 0000000..31d6216
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include <Kernel/libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+class Foo : public OSObject
+{
+    OSDeclareDefaultStructors( Foo )
+    
+public:
+    virtual int foo();
+    
+#ifdef FOO_USED
+    OSMetaClassDeclareReservedUsed(Foo, 0);
+    virtual int fooUsed0();
+#else
+    OSMetaClassDeclareReservedUnused(Foo, 0);
+#endif
+    OSMetaClassDeclareReservedUnused(Foo, 1);
+    OSMetaClassDeclareReservedUnused(Foo, 2);
+    OSMetaClassDeclareReservedUnused(Foo, 3);
+};
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/main.cpp b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/main.cpp
new file mode 100644 (file)
index 0000000..b1056d6
--- /dev/null
@@ -0,0 +1,105 @@
+
+#include <libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+int __cxa_pure_virtual = 0;
+
+#if 0
+class OSObject : public OSMetaClassBase
+{
+    OSDeclareAbstractStructors(OSObject);
+
+public:
+       OSMetaClassDeclareReservedUnused(OSObject, 0);
+    OSMetaClassDeclareReservedUnused(OSObject, 1);
+    OSMetaClassDeclareReservedUnused(OSObject, 2);
+    OSMetaClassDeclareReservedUnused(OSObject, 3);
+    OSMetaClassDeclareReservedUnused(OSObject, 4);
+    OSMetaClassDeclareReservedUnused(OSObject, 5);
+    OSMetaClassDeclareReservedUnused(OSObject, 6);
+    OSMetaClassDeclareReservedUnused(OSObject, 7);
+    OSMetaClassDeclareReservedUnused(OSObject, 8);
+    OSMetaClassDeclareReservedUnused(OSObject, 9);
+    OSMetaClassDeclareReservedUnused(OSObject, 10);
+    OSMetaClassDeclareReservedUnused(OSObject, 11);
+    OSMetaClassDeclareReservedUnused(OSObject, 12);
+    OSMetaClassDeclareReservedUnused(OSObject, 13);
+    OSMetaClassDeclareReservedUnused(OSObject, 14);
+    OSMetaClassDeclareReservedUnused(OSObject, 15);
+};
+#endif
+
+// OSDefineMetaClassAndAbstractStructors(OSObject, 0);
+/* Class global data */
+OSObject::MetaClass OSObject::gMetaClass;
+const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass;
+const OSMetaClass * const OSObject::superClass = NULL;
+
+// Virtual Padding
+OSMetaClassDefineReservedUnused(OSObject, 0);
+OSMetaClassDefineReservedUnused(OSObject, 1);
+OSMetaClassDefineReservedUnused(OSObject, 2);
+OSMetaClassDefineReservedUnused(OSObject, 3);
+OSMetaClassDefineReservedUnused(OSObject, 4);
+OSMetaClassDefineReservedUnused(OSObject, 5);
+OSMetaClassDefineReservedUnused(OSObject, 6);
+OSMetaClassDefineReservedUnused(OSObject, 7);
+OSMetaClassDefineReservedUnused(OSObject, 8);
+OSMetaClassDefineReservedUnused(OSObject, 9);
+OSMetaClassDefineReservedUnused(OSObject, 10);
+OSMetaClassDefineReservedUnused(OSObject, 11);
+OSMetaClassDefineReservedUnused(OSObject, 12);
+OSMetaClassDefineReservedUnused(OSObject, 13);
+OSMetaClassDefineReservedUnused(OSObject, 14);
+OSMetaClassDefineReservedUnused(OSObject, 15);
+
+// struct IORPC { };
+
+// OSMetaClassBase
+OSMetaClassBase::OSMetaClassBase() { }
+OSMetaClassBase::~OSMetaClassBase() { }
+kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; }
+bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; }
+
+// OSMetaClass
+OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { }
+OSMetaClass::~OSMetaClass() { }
+void OSMetaClass::reservedCalled(int ind) const { }
+void OSMetaClass::retain() const { }
+void OSMetaClass::release() const { }
+void OSMetaClass::release(int freeWhen) const { }
+int OSMetaClass::getRetainCount() const { return 0; }
+const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; }
+void OSMetaClass::taggedRetain(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; }
+void OSMetaClass::instanceConstructed() const { }
+
+// OSObject::MetaClass
+OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { }
+OSObject *OSObject::MetaClass::alloc() const { return NULL; }
+kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; }
+
+// OSObject
+OSObject::OSObject(OSMetaClass const*) { }
+OSObject::~OSObject() { }
+const OSMetaClass* OSObject::getMetaClass() const { return NULL; }
+void OSObject::free() { }
+bool OSObject::init() { return false; }
+void OSObject::retain() const { }
+void OSObject::release() const { }
+void OSObject::release(int freeWhen) const { }
+int OSObject::getRetainCount() const { return 0; }
+void OSObject::taggedRetain(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSObject::serialize(OSSerialize * serializer) const { return false; }
+kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; }
+void* OSObject::operator new(unsigned long) { return (void*)1; }
+void OSObject::operator delete(void*, unsigned long) { return; }
+
+__attribute__((section(("__TEXT_EXEC, __text"))))
+extern "C" int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/main.kernel b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/main.kernel
new file mode 100755 (executable)
index 0000000..1cee7ea
Binary files /dev/null and b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/main.kernel differ
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/test.py b/testing/kernel-cache-tests/kernel-vtable-patching-arm64e/test.py
new file mode 100644 (file)
index 0000000..426fadd
--- /dev/null
@@ -0,0 +1,86 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This tests verifies that the vtable in bar.kext is patched
+# But also that this can be done against a subclass in the kernel, not just
+
+# Note this is the same as kernel-vtable-patching but with a large base address and chained fixups
+
+def findGlobalSymbolVMAddr(kernel_cache, dylib_index, symbol_name):
+    for symbol_and_addr in kernel_cache.dictionary()["dylibs"][dylib_index]["global-symbols"]:
+        if symbol_and_addr["name"] == symbol_name:
+            return symbol_and_addr["vmAddr"]
+    return None
+
+def findFixupVMAddr(kernel_cache, fixup_name):
+    for fixup_vmaddr, fixup_target in kernel_cache.dictionary()["fixups"].iteritems():
+        if fixup_target == fixup_name:
+            return fixup_vmaddr
+    return None
+
+def offsetVMAddr(vmAddr, offset):
+    het_int = int(vmAddr, 16)
+    het_int = het_int + offset
+    return ''.join([ '0x', hex(het_int).upper()[2:] ])
+
+def check(kernel_cache):
+    enableLogging = False
+    kernel_cache.buildKernelCollection("arm64e", "/kernel-vtable-patching-arm64e/main.kc", "/kernel-vtable-patching-arm64e/main.kernel", "/kernel-vtable-patching-arm64e/extensions", ["com.apple.bar"], [])
+    kernel_cache.analyze("/kernel-vtable-patching-arm64e/main.kc", ["-layout", "-arch", "arm64e"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/kernel-vtable-patching-arm64e/main.kc", ["-symbols", "-arch", "arm64e"])
+    
+    # From foo, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable
+    # Foo::foo()
+    fooClassFooVMAddr = findGlobalSymbolVMAddr(kernel_cache, 0, "__ZN3Foo3fooEv")
+    if enableLogging:
+        print "fooClassFooVMAddr: " + fooClassFooVMAddr
+
+    # Foo::fooUsed0()
+    fooClassUsed0VMAddr = findGlobalSymbolVMAddr(kernel_cache, 0, "__ZN3Foo8fooUsed0Ev")
+    if enableLogging:
+        print "fooClassUsed0VMAddr: " + fooClassUsed0VMAddr
+    
+    # From bar, find the vtable and its override of foo()
+    # Bar::foo()
+    barClassFooVMAddr = findGlobalSymbolVMAddr(kernel_cache, 1, "__ZN3Bar3fooEv")
+    if enableLogging:
+        print "barClassFooVMAddr: " + barClassFooVMAddr
+
+
+    # Check the fixups
+    kernel_cache.analyze("/kernel-vtable-patching-arm64e/main.kc", ["-fixups", "-arch", "arm64e"])
+    
+    # foo.kext
+    # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup
+    fooFooFixupAddr = findFixupVMAddr(kernel_cache, "kc(0) + " + fooClassFooVMAddr + " auth(IA addr 49764)")
+    if enableLogging:
+        print "fooFooFixupAddr: " + fooFooFixupAddr
+    # Then the following fixup should be to Foo::fooUsed0()
+    fooFooNextFixupAddr = offsetVMAddr(fooFooFixupAddr, 8)
+    if enableLogging:
+        print "fooFooNextFixupAddr: " + fooFooNextFixupAddr
+    assert kernel_cache.dictionary()["fixups"][fooFooNextFixupAddr] == "kc(0) + " + fooClassUsed0VMAddr + " auth(IA addr 61962)"
+
+    # bar.kext
+    # Now in bar, again match the entry for its Bar::foo() symbol
+    barFooFixupAddr = findFixupVMAddr(kernel_cache, "kc(0) + " + barClassFooVMAddr + " auth(IA addr 49764)")
+    if enableLogging:
+        print "barFooFixupAddr: " + barFooFixupAddr
+    # And if the patching was correct, then following entry should be to Foo::fooUsed0()
+    barFooNextFixupAddr = offsetVMAddr(barFooFixupAddr, 8)
+    if enableLogging:
+        print "barFooNextFixupAddr: " + barFooNextFixupAddr
+    assert kernel_cache.dictionary()["fixups"][barFooNextFixupAddr] == "kc(0) + " + fooClassUsed0VMAddr + " auth(IA addr 61962)"
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.cpp foo.cpp -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel  -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -Wl,-image_base,0xfffffff000000000 -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r--  -DFOO_USED=1 -Wl,-kernel -Wl,-fixup_chains
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-fixup_chains
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-error/SymbolSets.plist b/testing/kernel-cache-tests/kernel-vtable-patching-error/SymbolSets.plist
new file mode 100644 (file)
index 0000000..e80d5d5
--- /dev/null
@@ -0,0 +1,56 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.symbols</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTV8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN3Foo</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolName</key>
+                                       <string>__ZTV3Foo</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-error/bar.cpp b/testing/kernel-cache-tests/kernel-vtable-patching-error/bar.cpp
new file mode 100644 (file)
index 0000000..a87b825
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include "foo.h"
+
+class Bar : public Foo
+{
+    OSDeclareDefaultStructors( Bar )
+    
+public:
+    virtual int foo();
+};
+
+OSDefineMetaClassAndStructors( Bar, Foo )
+
+int Bar::foo() {
+       return 1;
+}
+
+int bar() {
+       Bar* bar = new Bar();
+       return bar->foo();
+}
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-error/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kernel-vtable-patching-error/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..917ef6c
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-error/extensions/bar.kext/bar b/testing/kernel-cache-tests/kernel-vtable-patching-error/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..b3bd60b
Binary files /dev/null and b/testing/kernel-cache-tests/kernel-vtable-patching-error/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-error/foo.h b/testing/kernel-cache-tests/kernel-vtable-patching-error/foo.h
new file mode 100644 (file)
index 0000000..31d6216
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include <Kernel/libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+class Foo : public OSObject
+{
+    OSDeclareDefaultStructors( Foo )
+    
+public:
+    virtual int foo();
+    
+#ifdef FOO_USED
+    OSMetaClassDeclareReservedUsed(Foo, 0);
+    virtual int fooUsed0();
+#else
+    OSMetaClassDeclareReservedUnused(Foo, 0);
+#endif
+    OSMetaClassDeclareReservedUnused(Foo, 1);
+    OSMetaClassDeclareReservedUnused(Foo, 2);
+    OSMetaClassDeclareReservedUnused(Foo, 3);
+};
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-error/main.cpp b/testing/kernel-cache-tests/kernel-vtable-patching-error/main.cpp
new file mode 100644 (file)
index 0000000..b689f8a
--- /dev/null
@@ -0,0 +1,119 @@
+
+#include <libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+int __cxa_pure_virtual = 0;
+
+#if 0
+class OSObject : public OSMetaClassBase
+{
+    OSDeclareAbstractStructors(OSObject);
+
+public:
+       OSMetaClassDeclareReservedUnused(OSObject, 0);
+    OSMetaClassDeclareReservedUnused(OSObject, 1);
+    OSMetaClassDeclareReservedUnused(OSObject, 2);
+    OSMetaClassDeclareReservedUnused(OSObject, 3);
+    OSMetaClassDeclareReservedUnused(OSObject, 4);
+    OSMetaClassDeclareReservedUnused(OSObject, 5);
+    OSMetaClassDeclareReservedUnused(OSObject, 6);
+    OSMetaClassDeclareReservedUnused(OSObject, 7);
+    OSMetaClassDeclareReservedUnused(OSObject, 8);
+    OSMetaClassDeclareReservedUnused(OSObject, 9);
+    OSMetaClassDeclareReservedUnused(OSObject, 10);
+    OSMetaClassDeclareReservedUnused(OSObject, 11);
+    OSMetaClassDeclareReservedUnused(OSObject, 12);
+    OSMetaClassDeclareReservedUnused(OSObject, 13);
+    OSMetaClassDeclareReservedUnused(OSObject, 14);
+    OSMetaClassDeclareReservedUnused(OSObject, 15);
+};
+#endif
+
+// OSDefineMetaClassAndAbstractStructors(OSObject, 0);
+/* Class global data */
+OSObject::MetaClass OSObject::gMetaClass;
+const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass;
+const OSMetaClass * const OSObject::superClass = NULL;
+
+// Virtual Padding
+OSMetaClassDefineReservedUnused(OSObject, 0);
+OSMetaClassDefineReservedUnused(OSObject, 1);
+OSMetaClassDefineReservedUnused(OSObject, 2);
+OSMetaClassDefineReservedUnused(OSObject, 3);
+OSMetaClassDefineReservedUnused(OSObject, 4);
+OSMetaClassDefineReservedUnused(OSObject, 5);
+OSMetaClassDefineReservedUnused(OSObject, 6);
+OSMetaClassDefineReservedUnused(OSObject, 7);
+OSMetaClassDefineReservedUnused(OSObject, 8);
+OSMetaClassDefineReservedUnused(OSObject, 9);
+OSMetaClassDefineReservedUnused(OSObject, 10);
+OSMetaClassDefineReservedUnused(OSObject, 11);
+OSMetaClassDefineReservedUnused(OSObject, 12);
+OSMetaClassDefineReservedUnused(OSObject, 13);
+OSMetaClassDefineReservedUnused(OSObject, 14);
+OSMetaClassDefineReservedUnused(OSObject, 15);
+
+// struct IORPC { };
+
+// OSMetaClassBase
+OSMetaClassBase::OSMetaClassBase() { }
+OSMetaClassBase::~OSMetaClassBase() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { }
+kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; }
+bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; }
+
+// OSMetaClass
+OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { }
+OSMetaClass::~OSMetaClass() { }
+void OSMetaClass::reservedCalled(int ind) const { }
+void OSMetaClass::retain() const { }
+void OSMetaClass::release() const { }
+void OSMetaClass::release(int freeWhen) const { }
+int OSMetaClass::getRetainCount() const { return 0; }
+const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; }
+void OSMetaClass::taggedRetain(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; }
+void OSMetaClass::instanceConstructed() const { }
+
+// OSMetaClass reserved methods
+void OSMetaClass::_RESERVEDOSMetaClass0() { }
+void OSMetaClass::_RESERVEDOSMetaClass1() { }
+void OSMetaClass::_RESERVEDOSMetaClass2() { }
+void OSMetaClass::_RESERVEDOSMetaClass3() { }
+void OSMetaClass::_RESERVEDOSMetaClass4() { }
+void OSMetaClass::_RESERVEDOSMetaClass5() { }
+void OSMetaClass::_RESERVEDOSMetaClass6() { }
+void OSMetaClass::_RESERVEDOSMetaClass7() { }
+
+// OSObject::MetaClass
+OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { }
+OSObject *OSObject::MetaClass::alloc() const { return NULL; }
+kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; }
+
+// OSObject
+OSObject::OSObject(OSMetaClass const*) { }
+OSObject::~OSObject() { }
+const OSMetaClass* OSObject::getMetaClass() const { return NULL; }
+void OSObject::free() { }
+bool OSObject::init() { return false; }
+void OSObject::retain() const { }
+void OSObject::release() const { }
+void OSObject::release(int freeWhen) const { }
+int OSObject::getRetainCount() const { return 0; }
+void OSObject::taggedRetain(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSObject::serialize(OSSerialize * serializer) const { return false; }
+kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; }
+void* OSObject::operator new(unsigned long) { return (void*)1; }
+void OSObject::operator delete(void*, unsigned long) { return; }
+
+__attribute__((section(("__HIB, __text"))))
+extern "C" int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-error/main.kernel b/testing/kernel-cache-tests/kernel-vtable-patching-error/main.kernel
new file mode 100755 (executable)
index 0000000..77cac7b
Binary files /dev/null and b/testing/kernel-cache-tests/kernel-vtable-patching-error/main.kernel differ
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-error/test.py b/testing/kernel-cache-tests/kernel-vtable-patching-error/test.py
new file mode 100644 (file)
index 0000000..49c8be3
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Bar has a superclass in Foo, but we don't export that symbol.  This causes the vtable patcher to fail
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/kernel-vtable-patching-error/main.kc", "/kernel-vtable-patching-error/main.kernel", "/kernel-vtable-patching-error/extensions", ["com.apple.bar"], ["-json-errors"])
+
+    assert len(kernel_cache.dictionary()) == 1
+    # bar
+    assert kernel_cache.dictionary()[0]["id"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()[0]["errors"]) == 1
+    assert kernel_cache.dictionary()[0]["errors"][0] == "Cannot find symbol for metaclass pointed to by '__ZN3Bar10superClassE'.  Expected symbol '__ZN3Foo10gMetaClassE' to be defined in another kext"
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r--  -DFOO_USED=1
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/SymbolSets.plist b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/SymbolSets.plist
new file mode 100644 (file)
index 0000000..e80d5d5
--- /dev/null
@@ -0,0 +1,56 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.symbols</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTV8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN3Foo</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolName</key>
+                                       <string>__ZTV3Foo</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/bar.cpp b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/bar.cpp
new file mode 100644 (file)
index 0000000..a87b825
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include "foo.h"
+
+class Bar : public Foo
+{
+    OSDeclareDefaultStructors( Bar )
+    
+public:
+    virtual int foo();
+};
+
+OSDefineMetaClassAndStructors( Bar, Foo )
+
+int Bar::foo() {
+       return 1;
+}
+
+int bar() {
+       Bar* bar = new Bar();
+       return bar->foo();
+}
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..917ef6c
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/extensions/bar.kext/bar b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..b3bd60b
Binary files /dev/null and b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/foo.cpp b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/foo.cpp
new file mode 100644 (file)
index 0000000..83e5e67
--- /dev/null
@@ -0,0 +1,23 @@
+
+#include "foo.h"
+
+OSDefineMetaClassAndStructors( Foo, OSObject )
+
+// Index 0 has been replaced with a method
+OSMetaClassDefineReservedUsed(Foo, 0)
+OSMetaClassDefineReservedUnused( Foo, 1 )
+OSMetaClassDefineReservedUnused( Foo, 2 )
+OSMetaClassDefineReservedUnused( Foo, 3 )
+
+int Foo::foo() {
+       return 0;
+}
+
+int Foo::fooUsed0() {
+       return 0;
+}
+
+int foo() {
+       Foo* foo = new Foo();
+       return foo->foo() + foo->fooUsed0();
+}
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/foo.h b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/foo.h
new file mode 100644 (file)
index 0000000..31d6216
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include <Kernel/libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+class Foo : public OSObject
+{
+    OSDeclareDefaultStructors( Foo )
+    
+public:
+    virtual int foo();
+    
+#ifdef FOO_USED
+    OSMetaClassDeclareReservedUsed(Foo, 0);
+    virtual int fooUsed0();
+#else
+    OSMetaClassDeclareReservedUnused(Foo, 0);
+#endif
+    OSMetaClassDeclareReservedUnused(Foo, 1);
+    OSMetaClassDeclareReservedUnused(Foo, 2);
+    OSMetaClassDeclareReservedUnused(Foo, 3);
+};
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/main.cpp b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/main.cpp
new file mode 100644 (file)
index 0000000..b689f8a
--- /dev/null
@@ -0,0 +1,119 @@
+
+#include <libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+int __cxa_pure_virtual = 0;
+
+#if 0
+class OSObject : public OSMetaClassBase
+{
+    OSDeclareAbstractStructors(OSObject);
+
+public:
+       OSMetaClassDeclareReservedUnused(OSObject, 0);
+    OSMetaClassDeclareReservedUnused(OSObject, 1);
+    OSMetaClassDeclareReservedUnused(OSObject, 2);
+    OSMetaClassDeclareReservedUnused(OSObject, 3);
+    OSMetaClassDeclareReservedUnused(OSObject, 4);
+    OSMetaClassDeclareReservedUnused(OSObject, 5);
+    OSMetaClassDeclareReservedUnused(OSObject, 6);
+    OSMetaClassDeclareReservedUnused(OSObject, 7);
+    OSMetaClassDeclareReservedUnused(OSObject, 8);
+    OSMetaClassDeclareReservedUnused(OSObject, 9);
+    OSMetaClassDeclareReservedUnused(OSObject, 10);
+    OSMetaClassDeclareReservedUnused(OSObject, 11);
+    OSMetaClassDeclareReservedUnused(OSObject, 12);
+    OSMetaClassDeclareReservedUnused(OSObject, 13);
+    OSMetaClassDeclareReservedUnused(OSObject, 14);
+    OSMetaClassDeclareReservedUnused(OSObject, 15);
+};
+#endif
+
+// OSDefineMetaClassAndAbstractStructors(OSObject, 0);
+/* Class global data */
+OSObject::MetaClass OSObject::gMetaClass;
+const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass;
+const OSMetaClass * const OSObject::superClass = NULL;
+
+// Virtual Padding
+OSMetaClassDefineReservedUnused(OSObject, 0);
+OSMetaClassDefineReservedUnused(OSObject, 1);
+OSMetaClassDefineReservedUnused(OSObject, 2);
+OSMetaClassDefineReservedUnused(OSObject, 3);
+OSMetaClassDefineReservedUnused(OSObject, 4);
+OSMetaClassDefineReservedUnused(OSObject, 5);
+OSMetaClassDefineReservedUnused(OSObject, 6);
+OSMetaClassDefineReservedUnused(OSObject, 7);
+OSMetaClassDefineReservedUnused(OSObject, 8);
+OSMetaClassDefineReservedUnused(OSObject, 9);
+OSMetaClassDefineReservedUnused(OSObject, 10);
+OSMetaClassDefineReservedUnused(OSObject, 11);
+OSMetaClassDefineReservedUnused(OSObject, 12);
+OSMetaClassDefineReservedUnused(OSObject, 13);
+OSMetaClassDefineReservedUnused(OSObject, 14);
+OSMetaClassDefineReservedUnused(OSObject, 15);
+
+// struct IORPC { };
+
+// OSMetaClassBase
+OSMetaClassBase::OSMetaClassBase() { }
+OSMetaClassBase::~OSMetaClassBase() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { }
+kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; }
+bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; }
+
+// OSMetaClass
+OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { }
+OSMetaClass::~OSMetaClass() { }
+void OSMetaClass::reservedCalled(int ind) const { }
+void OSMetaClass::retain() const { }
+void OSMetaClass::release() const { }
+void OSMetaClass::release(int freeWhen) const { }
+int OSMetaClass::getRetainCount() const { return 0; }
+const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; }
+void OSMetaClass::taggedRetain(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; }
+void OSMetaClass::instanceConstructed() const { }
+
+// OSMetaClass reserved methods
+void OSMetaClass::_RESERVEDOSMetaClass0() { }
+void OSMetaClass::_RESERVEDOSMetaClass1() { }
+void OSMetaClass::_RESERVEDOSMetaClass2() { }
+void OSMetaClass::_RESERVEDOSMetaClass3() { }
+void OSMetaClass::_RESERVEDOSMetaClass4() { }
+void OSMetaClass::_RESERVEDOSMetaClass5() { }
+void OSMetaClass::_RESERVEDOSMetaClass6() { }
+void OSMetaClass::_RESERVEDOSMetaClass7() { }
+
+// OSObject::MetaClass
+OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { }
+OSObject *OSObject::MetaClass::alloc() const { return NULL; }
+kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; }
+
+// OSObject
+OSObject::OSObject(OSMetaClass const*) { }
+OSObject::~OSObject() { }
+const OSMetaClass* OSObject::getMetaClass() const { return NULL; }
+void OSObject::free() { }
+bool OSObject::init() { return false; }
+void OSObject::retain() const { }
+void OSObject::release() const { }
+void OSObject::release(int freeWhen) const { }
+int OSObject::getRetainCount() const { return 0; }
+void OSObject::taggedRetain(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSObject::serialize(OSSerialize * serializer) const { return false; }
+kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; }
+void* OSObject::operator new(unsigned long) { return (void*)1; }
+void OSObject::operator delete(void*, unsigned long) { return; }
+
+__attribute__((section(("__HIB, __text"))))
+extern "C" int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/main.kernel b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/main.kernel
new file mode 100755 (executable)
index 0000000..c72998c
Binary files /dev/null and b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/main.kernel differ
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/test.py b/testing/kernel-cache-tests/kernel-vtable-patching-large-base-addr/test.py
new file mode 100644 (file)
index 0000000..1b02f4b
--- /dev/null
@@ -0,0 +1,53 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This tests verifies that the vtable in bar.kext is patched
+# But also that this can be done against a subclass in the kernel, not just
+
+# Note this is the same as kernel-vtable-patching but with a large base address
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/kernel-vtable-patching-large-base-addr/main.kc", "/kernel-vtable-patching-large-base-addr/main.kernel", "/kernel-vtable-patching-large-base-addr/extensions", ["com.apple.bar"], [])
+    kernel_cache.analyze("/kernel-vtable-patching-large-base-addr/main.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/kernel-vtable-patching-large-base-addr/main.kc", ["-symbols", "-arch", "x86_64"])
+    
+    # From foo, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable
+    # Foo::foo()
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][27]["name"] == "__ZN3Foo3fooEv"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][27]["vmAddr"] == "0xFFFFFF8000205EC0"
+    # Foo::fooUsed0()
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][28]["name"] == "__ZN3Foo8fooUsed0Ev"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][28]["vmAddr"] == "0xFFFFFF8000205EE0"
+    
+    # From bar, find the vtable and its override of foo()
+    # Bar::foo()
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][3]["name"] == "__ZN3Bar3fooEv"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][3]["vmAddr"] == "0xFFFFFF8000206F10"
+
+
+    # Check the fixups
+    kernel_cache.analyze("/kernel-vtable-patching-large-base-addr/main.kc", ["-fixups", "-arch", "x86_64"])
+    
+    # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup
+    assert kernel_cache.dictionary()["fixups"]["0x108500"] == "kc(0) + 0xFFFFFF8000205EC0 : pointer64"
+    # Then the following fixup should be to Foo::fooUsed0()
+    assert kernel_cache.dictionary()["fixups"]["0x108508"] == "kc(0) + 0xFFFFFF8000205EE0 : pointer64"
+
+    # Now in bar, again match the entry for its Bar::foo() symbol
+    assert kernel_cache.dictionary()["fixups"]["0x111150"] == "kc(0) + 0xFFFFFF8000206F10"
+    # And if the patching was correct, then following entry should be to Foo::fooUsed0()
+    assert kernel_cache.dictionary()["fixups"]["0x111158"] == "kc(0) + 0xFFFFFF8000205EE0"
+
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp foo.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0xffffff8000200000 -Wl,-segaddr,__HIB,0xffffff8000100000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r--  -DFOO_USED=1
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching/SymbolSets.plist b/testing/kernel-cache-tests/kernel-vtable-patching/SymbolSets.plist
new file mode 100644 (file)
index 0000000..e80d5d5
--- /dev/null
@@ -0,0 +1,56 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.symbols</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTV8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN3Foo</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolName</key>
+                                       <string>__ZTV3Foo</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching/bar.cpp b/testing/kernel-cache-tests/kernel-vtable-patching/bar.cpp
new file mode 100644 (file)
index 0000000..a87b825
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include "foo.h"
+
+class Bar : public Foo
+{
+    OSDeclareDefaultStructors( Bar )
+    
+public:
+    virtual int foo();
+};
+
+OSDefineMetaClassAndStructors( Bar, Foo )
+
+int Bar::foo() {
+       return 1;
+}
+
+int bar() {
+       Bar* bar = new Bar();
+       return bar->foo();
+}
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kernel-vtable-patching/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..917ef6c
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching/extensions/bar.kext/bar b/testing/kernel-cache-tests/kernel-vtable-patching/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..b3bd60b
Binary files /dev/null and b/testing/kernel-cache-tests/kernel-vtable-patching/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching/foo.cpp b/testing/kernel-cache-tests/kernel-vtable-patching/foo.cpp
new file mode 100644 (file)
index 0000000..83e5e67
--- /dev/null
@@ -0,0 +1,23 @@
+
+#include "foo.h"
+
+OSDefineMetaClassAndStructors( Foo, OSObject )
+
+// Index 0 has been replaced with a method
+OSMetaClassDefineReservedUsed(Foo, 0)
+OSMetaClassDefineReservedUnused( Foo, 1 )
+OSMetaClassDefineReservedUnused( Foo, 2 )
+OSMetaClassDefineReservedUnused( Foo, 3 )
+
+int Foo::foo() {
+       return 0;
+}
+
+int Foo::fooUsed0() {
+       return 0;
+}
+
+int foo() {
+       Foo* foo = new Foo();
+       return foo->foo() + foo->fooUsed0();
+}
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching/foo.h b/testing/kernel-cache-tests/kernel-vtable-patching/foo.h
new file mode 100644 (file)
index 0000000..31d6216
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include <Kernel/libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+class Foo : public OSObject
+{
+    OSDeclareDefaultStructors( Foo )
+    
+public:
+    virtual int foo();
+    
+#ifdef FOO_USED
+    OSMetaClassDeclareReservedUsed(Foo, 0);
+    virtual int fooUsed0();
+#else
+    OSMetaClassDeclareReservedUnused(Foo, 0);
+#endif
+    OSMetaClassDeclareReservedUnused(Foo, 1);
+    OSMetaClassDeclareReservedUnused(Foo, 2);
+    OSMetaClassDeclareReservedUnused(Foo, 3);
+};
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching/main.cpp b/testing/kernel-cache-tests/kernel-vtable-patching/main.cpp
new file mode 100644 (file)
index 0000000..b689f8a
--- /dev/null
@@ -0,0 +1,119 @@
+
+#include <libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+int __cxa_pure_virtual = 0;
+
+#if 0
+class OSObject : public OSMetaClassBase
+{
+    OSDeclareAbstractStructors(OSObject);
+
+public:
+       OSMetaClassDeclareReservedUnused(OSObject, 0);
+    OSMetaClassDeclareReservedUnused(OSObject, 1);
+    OSMetaClassDeclareReservedUnused(OSObject, 2);
+    OSMetaClassDeclareReservedUnused(OSObject, 3);
+    OSMetaClassDeclareReservedUnused(OSObject, 4);
+    OSMetaClassDeclareReservedUnused(OSObject, 5);
+    OSMetaClassDeclareReservedUnused(OSObject, 6);
+    OSMetaClassDeclareReservedUnused(OSObject, 7);
+    OSMetaClassDeclareReservedUnused(OSObject, 8);
+    OSMetaClassDeclareReservedUnused(OSObject, 9);
+    OSMetaClassDeclareReservedUnused(OSObject, 10);
+    OSMetaClassDeclareReservedUnused(OSObject, 11);
+    OSMetaClassDeclareReservedUnused(OSObject, 12);
+    OSMetaClassDeclareReservedUnused(OSObject, 13);
+    OSMetaClassDeclareReservedUnused(OSObject, 14);
+    OSMetaClassDeclareReservedUnused(OSObject, 15);
+};
+#endif
+
+// OSDefineMetaClassAndAbstractStructors(OSObject, 0);
+/* Class global data */
+OSObject::MetaClass OSObject::gMetaClass;
+const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass;
+const OSMetaClass * const OSObject::superClass = NULL;
+
+// Virtual Padding
+OSMetaClassDefineReservedUnused(OSObject, 0);
+OSMetaClassDefineReservedUnused(OSObject, 1);
+OSMetaClassDefineReservedUnused(OSObject, 2);
+OSMetaClassDefineReservedUnused(OSObject, 3);
+OSMetaClassDefineReservedUnused(OSObject, 4);
+OSMetaClassDefineReservedUnused(OSObject, 5);
+OSMetaClassDefineReservedUnused(OSObject, 6);
+OSMetaClassDefineReservedUnused(OSObject, 7);
+OSMetaClassDefineReservedUnused(OSObject, 8);
+OSMetaClassDefineReservedUnused(OSObject, 9);
+OSMetaClassDefineReservedUnused(OSObject, 10);
+OSMetaClassDefineReservedUnused(OSObject, 11);
+OSMetaClassDefineReservedUnused(OSObject, 12);
+OSMetaClassDefineReservedUnused(OSObject, 13);
+OSMetaClassDefineReservedUnused(OSObject, 14);
+OSMetaClassDefineReservedUnused(OSObject, 15);
+
+// struct IORPC { };
+
+// OSMetaClassBase
+OSMetaClassBase::OSMetaClassBase() { }
+OSMetaClassBase::~OSMetaClassBase() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { }
+kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; }
+bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; }
+
+// OSMetaClass
+OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { }
+OSMetaClass::~OSMetaClass() { }
+void OSMetaClass::reservedCalled(int ind) const { }
+void OSMetaClass::retain() const { }
+void OSMetaClass::release() const { }
+void OSMetaClass::release(int freeWhen) const { }
+int OSMetaClass::getRetainCount() const { return 0; }
+const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; }
+void OSMetaClass::taggedRetain(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; }
+void OSMetaClass::instanceConstructed() const { }
+
+// OSMetaClass reserved methods
+void OSMetaClass::_RESERVEDOSMetaClass0() { }
+void OSMetaClass::_RESERVEDOSMetaClass1() { }
+void OSMetaClass::_RESERVEDOSMetaClass2() { }
+void OSMetaClass::_RESERVEDOSMetaClass3() { }
+void OSMetaClass::_RESERVEDOSMetaClass4() { }
+void OSMetaClass::_RESERVEDOSMetaClass5() { }
+void OSMetaClass::_RESERVEDOSMetaClass6() { }
+void OSMetaClass::_RESERVEDOSMetaClass7() { }
+
+// OSObject::MetaClass
+OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { }
+OSObject *OSObject::MetaClass::alloc() const { return NULL; }
+kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; }
+
+// OSObject
+OSObject::OSObject(OSMetaClass const*) { }
+OSObject::~OSObject() { }
+const OSMetaClass* OSObject::getMetaClass() const { return NULL; }
+void OSObject::free() { }
+bool OSObject::init() { return false; }
+void OSObject::retain() const { }
+void OSObject::release() const { }
+void OSObject::release(int freeWhen) const { }
+int OSObject::getRetainCount() const { return 0; }
+void OSObject::taggedRetain(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSObject::serialize(OSSerialize * serializer) const { return false; }
+kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; }
+void* OSObject::operator new(unsigned long) { return (void*)1; }
+void OSObject::operator delete(void*, unsigned long) { return; }
+
+__attribute__((section(("__HIB, __text"))))
+extern "C" int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching/main.kernel b/testing/kernel-cache-tests/kernel-vtable-patching/main.kernel
new file mode 100755 (executable)
index 0000000..5999f32
Binary files /dev/null and b/testing/kernel-cache-tests/kernel-vtable-patching/main.kernel differ
diff --git a/testing/kernel-cache-tests/kernel-vtable-patching/test.py b/testing/kernel-cache-tests/kernel-vtable-patching/test.py
new file mode 100644 (file)
index 0000000..635705f
--- /dev/null
@@ -0,0 +1,51 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This tests verifies that the vtable in bar.kext is patched
+# But also that this can be done against a subclass in the kernel, not just 
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/kernel-vtable-patching/main.kc", "/kernel-vtable-patching/main.kernel", "/kernel-vtable-patching/extensions", ["com.apple.bar"], [])
+    kernel_cache.analyze("/kernel-vtable-patching/main.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/kernel-vtable-patching/main.kc", ["-symbols", "-arch", "x86_64"])
+    
+    # From foo, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable
+    # Foo::foo()
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][27]["name"] == "__ZN3Foo3fooEv"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][27]["vmAddr"] == "0x15EC0"
+    # Foo::fooUsed0()
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][28]["name"] == "__ZN3Foo8fooUsed0Ev"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][28]["vmAddr"] == "0x15EE0"
+    
+    # From bar, find the vtable and its override of foo()
+    # Bar::foo()
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][3]["name"] == "__ZN3Bar3fooEv"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][3]["vmAddr"] == "0x16F10"
+
+
+    # Check the fixups
+    kernel_cache.analyze("/kernel-vtable-patching/main.kc", ["-fixups", "-arch", "x86_64"])
+    
+    # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup
+    assert kernel_cache.dictionary()["fixups"]["0x14500"] == "kc(0) + 0x15EC0 : pointer64"
+    # Then the following fixup should be to Foo::fooUsed0()
+    assert kernel_cache.dictionary()["fixups"]["0x14508"] == "kc(0) + 0x15EE0 : pointer64"
+
+    # Now in bar, again match the entry for its Bar::foo() symbol
+    assert kernel_cache.dictionary()["fixups"]["0x1D150"] == "kc(0) + 0x16F10"
+    # And if the patching was correct, then following entry should be to Foo::fooUsed0()
+    assert kernel_cache.dictionary()["fixups"]["0x1D158"] == "kc(0) + 0x15EE0"
+
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp foo.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r--  -DFOO_USED=1
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kext-bind-missing-dep/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-missing-dep/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..1111344
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.bar</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-missing-dep/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-bind-missing-dep/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..aef0390
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-missing-dep/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/kext-bind-missing-dep/foo.c b/testing/kernel-cache-tests/kext-bind-missing-dep/foo.c
new file mode 100644 (file)
index 0000000..d8f3634
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int bar;
+
+int foo() {
+       return bar;
+}
diff --git a/testing/kernel-cache-tests/kext-bind-missing-dep/main.c b/testing/kernel-cache-tests/kext-bind-missing-dep/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-bind-missing-dep/main.kernel b/testing/kernel-cache-tests/kext-bind-missing-dep/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-missing-dep/main.kernel differ
diff --git a/testing/kernel-cache-tests/kext-bind-missing-dep/test.py b/testing/kernel-cache-tests/kext-bind-missing-dep/test.py
new file mode 100644 (file)
index 0000000..34a3170
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Check that we get sensible errors on the bad kext
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/kext-bind-missing-dep/main.kc", "/kext-bind-missing-dep/main.kernel", "/kext-bind-missing-dep/extensions", ["com.apple.foo"], ["-json-errors"])
+
+    assert len(kernel_cache.dictionary()) == 1
+    assert kernel_cache.dictionary()[0]["id"] == "com.apple.foo"
+    assert len(kernel_cache.dictionary()[0]["errors"]) == 1
+    assert kernel_cache.dictionary()[0]["errors"][0] == "Failed to bind '_bar' as could not find a kext with 'com.apple.bar' bundle-id"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/bar.c b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/bar.c
new file mode 100644 (file)
index 0000000..2ad0d8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..261f5ad
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..65356c1
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/foo.c b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/foo.c
new file mode 100644 (file)
index 0000000..5a6eec2
--- /dev/null
@@ -0,0 +1,4 @@
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/main.c b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/main.c
new file mode 100644 (file)
index 0000000..30b797d
--- /dev/null
@@ -0,0 +1,9 @@
+
+int func() {
+       return 0;
+}
+
+__typeof(&func) funcPtr = &func;
+int _start() {
+       return funcPtr();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/main.kernel b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/main.kernel
new file mode 100755 (executable)
index 0000000..e8b37a3
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/main.kernel differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/test.py b/testing/kernel-cache-tests/kext-bind-to-kext-arm64-chains/test.py
new file mode 100644 (file)
index 0000000..2ddd899
--- /dev/null
@@ -0,0 +1,50 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This tests that kexts can bind to each other using DYLD_CHAINED_PTR_64_OFFSET
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/kext-bind-to-kext-arm64-chains/main.kc", "/kext-bind-to-kext-arm64-chains/main.kernel", "/kext-bind-to-kext-arm64-chains/extensions", ["com.apple.foo", "com.apple.bar"], [])
+
+    # layout
+    kernel_cache.analyze("/kext-bind-to-kext-arm64-chains/main.kc", ["-layout", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA_CONST"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0xFFFFFFF00701C000"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+
+    # Symbols
+    kernel_cache.analyze("/kext-bind-to-kext-arm64-chains/main.kc", ["-symbols", "-arch", "arm64"])
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["name"] == "_foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["vmAddr"] == "0xFFFFFFF007018020"
+
+    # Check the fixups
+    kernel_cache.analyze("/kext-bind-to-kext-arm64-chains/main.kc", ["-fixups", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 2
+    # bar.kext: extern int foo();
+    assert kernel_cache.dictionary()["fixups"]["0x20000"] == "kc(0) + 0xFFFFFFF007014000"
+    # main.kernel: __typeof(&func) funcPtr = &func;
+    assert kernel_cache.dictionary()["fixups"]["0x18000"] == "kc(0) + 0xFFFFFFF007018020"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -Wl,-fixup_chains -Wl,-kernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel  -Wl,-image_base,0xfffffff007004000 -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-fixup_chains foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-fixup_chains bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/bar.c b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/bar.c
new file mode 100644 (file)
index 0000000..388b52a
--- /dev/null
@@ -0,0 +1,9 @@
+
+extern int foo();
+extern int f;
+
+__typeof(&foo) fooPtr = &foo;
+
+int bar() {
+       return foo() + fooPtr() + f;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..2827ca5
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..ee50afc
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/foo.c b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/foo.c
new file mode 100644 (file)
index 0000000..21cece2
--- /dev/null
@@ -0,0 +1,6 @@
+
+int f = 0;
+
+int foo() {
+       return f;
+}
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/main.c b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/main.c
new file mode 100644 (file)
index 0000000..28207ed
--- /dev/null
@@ -0,0 +1,10 @@
+
+int f = 0;
+int func() {
+       return 0;
+}
+
+__typeof(&func) funcPtr = &func;
+int _start() {
+       return funcPtr() + f;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/main.kernel b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/main.kernel
new file mode 100755 (executable)
index 0000000..20a53df
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/main.kernel differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/test.py b/testing/kernel-cache-tests/kext-bind-to-kext-arm64e-chains/test.py
new file mode 100644 (file)
index 0000000..f085fdd
--- /dev/null
@@ -0,0 +1,61 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This tests that kexts can bind to each other using DYLD_CHAINED_PTR_ARM64E_KERNEL
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64e", "/kext-bind-to-kext-arm64e-chains/main.kc", "/kext-bind-to-kext-arm64e-chains/main.kernel", "/kext-bind-to-kext-arm64e-chains/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/kext-bind-to-kext-arm64e-chains/main.kc", ["-layout", "-arch", "arm64e"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["name"] == "__DATA_CONST"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][3]["vmAddr"] == "0xFFFFFFF00701C000"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+
+    # Symbols
+    kernel_cache.analyze("/kext-bind-to-kext-arm64e-chains/main.kc", ["-symbols", "-arch", "arm64e"])
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][1]["name"] == "_fooPtr"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][1]["vmAddr"] == "0xFFFFFFF007028000"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["name"] == "_f"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["vmAddr"] == "0xFFFFFFF007028008"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][1]["name"] == "_foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][1]["vmAddr"] == "0xFFFFFFF007018070"
+
+    # Check the fixups
+    kernel_cache.analyze("/kext-bind-to-kext-arm64e-chains/main.kc", ["-fixups", "-arch", "arm64e"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 4
+    # __DATA_CONST
+    # bar.kext: extern int foo();
+    # bar.kext: extern int f;
+    assert kernel_cache.dictionary()["fixups"]["0x18000"] == "kc(0) + 0xFFFFFFF007018070 auth(IA addr 0)"
+    assert kernel_cache.dictionary()["fixups"]["0x18008"] == "kc(0) + 0xFFFFFFF007028008"
+    # __DATA
+    # main.kernel: __typeof(&func) funcPtr = &func;
+    assert kernel_cache.dictionary()["fixups"]["0x20000"] == "kc(0) + 0xFFFFFFF007014000 auth(IA !addr 0)"
+    # bar.kext: __typeof(&foo) fooPtr = &foo;
+    assert kernel_cache.dictionary()["fixups"]["0x24000"] == "kc(0) + 0xFFFFFFF007018070 auth(IA !addr 0)"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-static -mkernel -Wl,-fixup_chains -Wl,-kernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel  -Wl,-image_base,0xfffffff007004000 -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-fixup_chains foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-fixup_chains bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/bar.c b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/bar.c
new file mode 100644 (file)
index 0000000..3fa0fab
--- /dev/null
@@ -0,0 +1,7 @@
+
+extern int foo();
+extern int baz;
+
+int bar() {
+       return foo() + baz;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..423d808
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..8615d2c
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/foo.c b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/foo.c
new file mode 100644 (file)
index 0000000..7671e86
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int baz;
+
+int foo() {
+       return baz;
+}
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/main.c b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/main.kernel b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/main.kernel differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/test.py b/testing/kernel-cache-tests/kext-bind-to-kext-missing-symbol/test.py
new file mode 100644 (file)
index 0000000..1aa511e
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that we throw errors on each kext which is missing a symbol
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/kext-bind-to-kext-missing-symbol/main.kc", "/kext-bind-to-kext-missing-symbol/main.kernel", "/kext-bind-to-kext-missing-symbol/extensions", ["com.apple.foo", "com.apple.bar"], ["-json-errors"])
+
+    assert len(kernel_cache.dictionary()) == 2
+    # bar.kext
+    assert kernel_cache.dictionary()[0]["id"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()[0]["errors"]) == 1
+    assert kernel_cache.dictionary()[0]["errors"][0] == "Failed to bind '_baz' in 'com.apple.bar' (at offset 0x0 in __DATA, __got) as could not find a kext which exports this symbol"
+    # bar.kext
+    assert kernel_cache.dictionary()[1]["id"] == "com.apple.foo"
+    assert len(kernel_cache.dictionary()[1]["errors"]) == 1
+    assert kernel_cache.dictionary()[1]["errors"][0] == "Failed to bind '_baz' in 'com.apple.foo' (at offset 0x0 in __DATA, __got) as could not find a kext which exports this symbol"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/bar.c b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/bar.c
new file mode 100644 (file)
index 0000000..e381442
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo;
+
+int bar() {
+       return foo;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..3a9ad1d
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..1f0515b
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/foo.c b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/foo.c
new file mode 100644 (file)
index 0000000..42e0a8e
--- /dev/null
@@ -0,0 +1,2 @@
+
+int foo;
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/main.c b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/main.kernel b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/main.kernel
new file mode 100755 (executable)
index 0000000..cdc3eba
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/main.kernel differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/test.py b/testing/kernel-cache-tests/kext-bind-to-kext-old-section-type/test.py
new file mode 100644 (file)
index 0000000..f926ee6
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This tests that the very old bar.kext can be parsed.  It's __got section has a S_NON_LAZY_SYMBOL_POINTERS type,
+# but newer linkers changed to just S_REGULAR.
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/kext-bind-to-kext-old-section-type/main.kc", "/kext-bind-to-kext-old-section-type/main.kernel", "/kext-bind-to-kext-old-section-type/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/kext-bind-to-kext-old-section-type/main.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][2]["segments"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["vmAddr"] == "0x19000"
+
+    kernel_cache.analyze("/kext-bind-to-kext-old-section-type/main.kc", ["-symbols", "-arch", "x86_64"])
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][0]["name"] == "_foo"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][0]["vmAddr"] == "0x14000"
+
+    # Check the fixups
+    kernel_cache.analyze("/kext-bind-to-kext-old-section-type/main.kc", ["-fixups", "-arch", "x86_64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 1
+    assert kernel_cache.dictionary()["fixups"]["0x15000"] == "kc(0) + 0x14000"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none"
+
+
+# Note, bar.kext has to be linked with a very old linker from 10.7 to get the __got section with S_NON_LAZY_SYMBOL_POINTERS.
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -Wl,-kernel -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__HIB,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -Wl,-segprot,__HIB,r-x,r-x -Wl,-image_base,0x8000 -Wl,-segaddr,__HIB,0x4000
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar -fuse-ld=...
+
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/SymbolSets.plist b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/SymbolSets.plist
new file mode 100644 (file)
index 0000000..9ecc3fe
--- /dev/null
@@ -0,0 +1,11 @@
+<?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>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>kpi</key>
+       <array>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/bar.c b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/bar.c
new file mode 100644 (file)
index 0000000..2ad0d8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..96e0388
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>not.com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..7eafc00
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..bf5dc5e
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/foo.c b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/foo.c
new file mode 100644 (file)
index 0000000..5a6eec2
--- /dev/null
@@ -0,0 +1,4 @@
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/main.c b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/main.kernel b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/main.kernel differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/test.py b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set-error/test.py
new file mode 100644 (file)
index 0000000..37cea02
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that an non-Apple kext cannot bind to another Apple kext with a symbol set in the kext
+# foo.kext exports foo and bar.kext uses it
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/kext-bind-to-kext-symbol-set-error/main.kc", "/kext-bind-to-kext-symbol-set-error/main.kernel", "/kext-bind-to-kext-symbol-set-error/extensions", ["com.apple.foo", "not.com.apple.bar"], ["-json-errors"])
+
+    assert len(kernel_cache.dictionary()) == 1
+    assert kernel_cache.dictionary()[0]["id"] == "not.com.apple.bar"
+    assert len(kernel_cache.dictionary()[0]["errors"]) == 1
+    assert kernel_cache.dictionary()[0]["errors"][0] == "Failed to bind '_foo' in 'not.com.apple.bar' (at offset 0x0 in __DATA, __got) as could not find a kext which exports this symbol"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r--
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/SymbolSets.plist b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/SymbolSets.plist
new file mode 100644 (file)
index 0000000..d60c2d8
--- /dev/null
@@ -0,0 +1,13 @@
+<?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>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>Symbols</key>
+       <array>
+               <string>_foo</string>
+               <string>_missingFoo</string>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/bar.c b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/bar.c
new file mode 100644 (file)
index 0000000..2ad0d8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/exports.txt b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/exports.txt
new file mode 100644 (file)
index 0000000..8037631
--- /dev/null
@@ -0,0 +1,2 @@
+_foo
+_missingFoo
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..96e0388
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>not.com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..7eafc00
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..2a84e6e
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/foo.c b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/foo.c
new file mode 100644 (file)
index 0000000..5a6eec2
--- /dev/null
@@ -0,0 +1,4 @@
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/main.c b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/main.kernel b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/main.kernel differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/test.py b/testing/kernel-cache-tests/kext-bind-to-kext-symbol-set/test.py
new file mode 100644 (file)
index 0000000..b83eae1
--- /dev/null
@@ -0,0 +1,47 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that an non-Apple kext can bind to an Apple kext with a symbol set in the kext
+# foo.kext exports foo and bar.kext uses it
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/kext-bind-to-kext-symbol-set/main.kc", "/kext-bind-to-kext-symbol-set/main.kernel", "/kext-bind-to-kext-symbol-set/extensions", ["com.apple.foo", "not.com.apple.bar"], [])
+    kernel_cache.analyze("/kext-bind-to-kext-symbol-set/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "not.com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x1C000"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+
+    kernel_cache.analyze("/kext-bind-to-kext-symbol-set/main.kc", ["-symbols", "-arch", "arm64"])
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["name"] == "_foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["vmAddr"] == "0x14020"
+
+    # Check the fixups
+    kernel_cache.analyze("/kext-bind-to-kext-symbol-set/main.kc", ["-fixups", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 1
+    assert kernel_cache.dictionary()["fixups"]["0x1C000"] == "kc(0) + 0x14020"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "not.com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none"
+
+
+# [~]> ../kext-symbols.sh SymbolSets.plist ./extensions/foo.kext/Info.plist exports.txt
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r--
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext/bar.c b/testing/kernel-cache-tests/kext-bind-to-kext/bar.c
new file mode 100644 (file)
index 0000000..2ad0d8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-bind-to-kext/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..7eafc00
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kext/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-bind-to-kext/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..cd1fa31
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext/foo.c b/testing/kernel-cache-tests/kext-bind-to-kext/foo.c
new file mode 100644 (file)
index 0000000..5a6eec2
--- /dev/null
@@ -0,0 +1,4 @@
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext/main.c b/testing/kernel-cache-tests/kext-bind-to-kext/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext/main.kernel b/testing/kernel-cache-tests/kext-bind-to-kext/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kext/main.kernel differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kext/test.py b/testing/kernel-cache-tests/kext-bind-to-kext/test.py
new file mode 100644 (file)
index 0000000..19bce34
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that a kext can bind to another kext
+# foo.kext exports foo and bar.kext uses it
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/kext-bind-to-kext/main.kc", "/kext-bind-to-kext/main.kernel", "/kext-bind-to-kext/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/kext-bind-to-kext/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x1C000"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+
+    kernel_cache.analyze("/kext-bind-to-kext/main.kc", ["-symbols", "-arch", "arm64"])
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["name"] == "_foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["vmAddr"] == "0x14020"
+
+    # Check the fixups
+    kernel_cache.analyze("/kext-bind-to-kext/main.kc", ["-fixups", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 1
+    assert kernel_cache.dictionary()["fixups"]["0x1C000"] == "kc(0) + 0x14020"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/SymbolSets.plist b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/SymbolSets.plist
new file mode 100644 (file)
index 0000000..25926e6
--- /dev/null
@@ -0,0 +1,24 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.kpi.private</string>
+                       <key>CFBundleVersion</key>
+                       <string>1.0.0</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>1.0.0</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolName</key>
+                                       <string>_foo</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/bar.c b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/bar.c
new file mode 100644 (file)
index 0000000..2ad0d8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2a0b2db
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>not.com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.kpi.private</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..7eafc00
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/main.c b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/main.kernel b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/main.kernel
new file mode 100755 (executable)
index 0000000..ae9427d
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/main.kernel differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/test.py b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set-error/test.py
new file mode 100644 (file)
index 0000000..636decf
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that an non-Apple kext cannot bind to the com.apple.kpi.private symbol set
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/kext-bind-to-kpi-private-symbol-set-error/main.kc", "/kext-bind-to-kpi-private-symbol-set-error/main.kernel", "/kext-bind-to-kpi-private-symbol-set-error/extensions", ["com.apple.foo", "not.com.apple.bar"], ["-json-errors"])
+
+    assert len(kernel_cache.dictionary()) == 1
+    assert kernel_cache.dictionary()[0]["id"] == "not.com.apple.bar"
+    assert len(kernel_cache.dictionary()[0]["errors"]) == 1
+    assert kernel_cache.dictionary()[0]["errors"][0] == "Failed to bind '_foo' in 'not.com.apple.bar' (at offset 0x0 in __DATA, __got) as could not find a kext which exports this symbol"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r--
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/SymbolSets.plist b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/SymbolSets.plist
new file mode 100644 (file)
index 0000000..25926e6
--- /dev/null
@@ -0,0 +1,24 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.kpi.private</string>
+                       <key>CFBundleVersion</key>
+                       <string>1.0.0</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>1.0.0</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolName</key>
+                                       <string>_foo</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/bar.c b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/bar.c
new file mode 100644 (file)
index 0000000..2ad0d8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/exports.txt b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/exports.txt
new file mode 100644 (file)
index 0000000..8037631
--- /dev/null
@@ -0,0 +1,2 @@
+_foo
+_missingFoo
diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..649a915
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.kpi.private</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..7eafc00
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/main.c b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/main.c
new file mode 100644 (file)
index 0000000..4ea1b0a
--- /dev/null
@@ -0,0 +1,8 @@
+
+int _start() {
+       return 0;
+}
+
+int foo() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/main.kernel b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/main.kernel
new file mode 100755 (executable)
index 0000000..77bf56d
Binary files /dev/null and b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/main.kernel differ
diff --git a/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/test.py b/testing/kernel-cache-tests/kext-bind-to-kpi-private-symbol-set/test.py
new file mode 100644 (file)
index 0000000..cd3f0b0
--- /dev/null
@@ -0,0 +1,41 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that an Apple kext can bind to KPI.private with a symbol set in the kext
+# com.apple.kpi.private exports foo and bar.kext uses it
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/kext-bind-to-kpi-private-symbol-set/main.kc", "/kext-bind-to-kpi-private-symbol-set/main.kernel", "/kext-bind-to-kpi-private-symbol-set/extensions", ["com.apple.bar"], [])
+    kernel_cache.analyze("/kext-bind-to-kpi-private-symbol-set/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x18000"
+
+    kernel_cache.analyze("/kext-bind-to-kpi-private-symbol-set/main.kc", ["-symbols", "-arch", "arm64"])
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["name"] == "_foo"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["vmAddr"] == "0xC00C"
+
+    # Check the fixups
+    kernel_cache.analyze("/kext-bind-to-kpi-private-symbol-set/main.kc", ["-fixups", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 1
+    assert kernel_cache.dictionary()["fixups"]["0x18000"] == "kc(0) + 0xC00C"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r--
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kext-missing-weak-bind/SymbolSets.plist b/testing/kernel-cache-tests/kext-missing-weak-bind/SymbolSets.plist
new file mode 100644 (file)
index 0000000..df941be
--- /dev/null
@@ -0,0 +1,24 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.libkern</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolName</key>
+                                       <string>_gOSKextUnresolved</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-missing-weak-bind/bar.c b/testing/kernel-cache-tests/kext-missing-weak-bind/bar.c
new file mode 100644 (file)
index 0000000..9e23395
--- /dev/null
@@ -0,0 +1,12 @@
+
+__attribute__((weak))
+extern int weakValue;
+
+extern int gOSKextUnresolved;
+
+int bar() {
+       // Missing weak import test
+       if ( &weakValue != &gOSKextUnresolved )
+               return 0;
+       return weakValue;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..7e9987d
--- /dev/null
@@ -0,0 +1,21 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+               <key>com.apple.libkern</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..920f5e6
Binary files /dev/null and b/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..7a43b38
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.libkern</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..f29c662
Binary files /dev/null and b/testing/kernel-cache-tests/kext-missing-weak-bind/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/kext-missing-weak-bind/foo.c b/testing/kernel-cache-tests/kext-missing-weak-bind/foo.c
new file mode 100644 (file)
index 0000000..9e23395
--- /dev/null
@@ -0,0 +1,12 @@
+
+__attribute__((weak))
+extern int weakValue;
+
+extern int gOSKextUnresolved;
+
+int bar() {
+       // Missing weak import test
+       if ( &weakValue != &gOSKextUnresolved )
+               return 0;
+       return weakValue;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-missing-weak-bind/main.c b/testing/kernel-cache-tests/kext-missing-weak-bind/main.c
new file mode 100644 (file)
index 0000000..31fd572
--- /dev/null
@@ -0,0 +1,7 @@
+
+// We need this symbol to bind missing weak imports to
+int gOSKextUnresolved = 0;
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-missing-weak-bind/main.kernel b/testing/kernel-cache-tests/kext-missing-weak-bind/main.kernel
new file mode 100755 (executable)
index 0000000..476d3aa
Binary files /dev/null and b/testing/kernel-cache-tests/kext-missing-weak-bind/main.kernel differ
diff --git a/testing/kernel-cache-tests/kext-missing-weak-bind/test.py b/testing/kernel-cache-tests/kext-missing-weak-bind/test.py
new file mode 100644 (file)
index 0000000..42b0022
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Check that weak binds can be missing, so long as we check for the magic symbol
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/kext-missing-weak-bind/main.kc", "/kext-missing-weak-bind/main.kernel", "/kext-missing-weak-bind/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/kext-missing-weak-bind/main.kc", ["-layout", "-arch", "arm64"])
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__DATA_CONST"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0x18000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+
+    # Symbols
+    kernel_cache.analyze("/kext-missing-weak-bind/main.kc", ["-symbols", "-arch", "arm64"])
+    # kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["name"] == "_gOSKextUnresolved"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["vmAddr"] == "0x20000"
+
+    # Check the fixups
+    kernel_cache.analyze("/kext-missing-weak-bind/main.kc", ["-fixups", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 4
+    assert kernel_cache.dictionary()["fixups"]["0x18000"] == "kc(0) + 0x20000"
+    assert kernel_cache.dictionary()["fixups"]["0x18008"] == "kc(0) + 0x20000"
+    assert kernel_cache.dictionary()["fixups"]["0x18010"] == "kc(0) + 0x20000"
+    assert kernel_cache.dictionary()["fixups"]["0x18018"] == "kc(0) + 0x20000"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar -Wl,-fixup_chains
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kext-receipts/large.txt b/testing/kernel-cache-tests/kext-receipts/large.txt
new file mode 100644 (file)
index 0000000..190423f
--- /dev/null
@@ -0,0 +1,100 @@
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
diff --git a/testing/kernel-cache-tests/kext-receipts/large2.txt b/testing/kernel-cache-tests/kext-receipts/large2.txt
new file mode 100644 (file)
index 0000000..4ab65cc
--- /dev/null
@@ -0,0 +1 @@
+large data
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-receipts/main.c b/testing/kernel-cache-tests/kext-receipts/main.c
new file mode 100644 (file)
index 0000000..f545f5b
--- /dev/null
@@ -0,0 +1,13 @@
+
+// Add a large buffer so that we know we have a bunch of stuff in __DATA
+// and can more easily see that all the segments have moved around correctly, not just
+// got lucky that all are the same size
+__attribute__((used))
+char buffer[1024 * 16];
+
+int f = 0;
+int *gs[] = { &f, &f, 0, (int*)&buffer[0], (int*)&buffer[1] };
+
+int _start() {
+       return *gs[0] + *gs[1];;
+}
diff --git a/testing/kernel-cache-tests/kext-receipts/main.kernel b/testing/kernel-cache-tests/kext-receipts/main.kernel
new file mode 100755 (executable)
index 0000000..a27921c
Binary files /dev/null and b/testing/kernel-cache-tests/kext-receipts/main.kernel differ
diff --git a/testing/kernel-cache-tests/kext-receipts/small.txt b/testing/kernel-cache-tests/kext-receipts/small.txt
new file mode 100644 (file)
index 0000000..71f64db
--- /dev/null
@@ -0,0 +1 @@
+small data
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-receipts/test.py b/testing/kernel-cache-tests/kext-receipts/test.py
new file mode 100755 (executable)
index 0000000..ca7d574
--- /dev/null
@@ -0,0 +1,78 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/kext-receipts/main.kc", "/kext-receipts/main.kernel", None, [],
+                                       ["-sectcreate", "__SMALL", "", "$PWD/kext-receipts/small.txt",
+                                        "-sectcreate", "__SIXTEEN_CHARSS", "__sixteen_chaars", "$PWD/kext-receipts/large.txt",
+                                        "-sectcreate", "__SIXTEEN_CHARSS", "__large2", "$PWD/kext-receipts/large2.txt"])
+
+    # Check the layout
+    kernel_cache.analyze("/kext-receipts/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["cache-segments"]) == 8
+    assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0x0"
+    assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0x4000"
+
+    # small.txt
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__SMALL"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xC000"
+    assert not "sections" in kernel_cache.dictionary()["cache-segments"][3]
+
+    # large.txt, then large2.txt
+    assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__SIXTEEN_CHARSS"
+    assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0x10000"
+    assert kernel_cache.dictionary()["cache-segments"][4]["vmSize"] == "0x4000"
+    assert len(kernel_cache.dictionary()["cache-segments"][4]["sections"]) == 2
+    assert kernel_cache.dictionary()["cache-segments"][4]["sections"][0]["name"] == "__sixteen_chaars";
+    assert kernel_cache.dictionary()["cache-segments"][4]["sections"][0]["vmAddr"] == "0x10000";
+    assert kernel_cache.dictionary()["cache-segments"][4]["sections"][0]["vmEnd"] == "0x10124";
+    assert kernel_cache.dictionary()["cache-segments"][4]["sections"][0]["vmSize"] == "0x124";
+    assert kernel_cache.dictionary()["cache-segments"][4]["sections"][1]["name"] == "__large2";
+    assert kernel_cache.dictionary()["cache-segments"][4]["sections"][1]["vmAddr"] == "0x10124";
+    assert kernel_cache.dictionary()["cache-segments"][4]["sections"][1]["vmEnd"] == "0x1012E";
+    assert kernel_cache.dictionary()["cache-segments"][4]["sections"][1]["vmSize"] == "0xA";
+
+    assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__PRELINK_INFO"
+    assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0x14000"
+    assert kernel_cache.dictionary()["cache-segments"][6]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["cache-segments"][6]["vmAddr"] == "0x18000"
+    assert kernel_cache.dictionary()["cache-segments"][7]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["cache-segments"][7]["vmAddr"] == "0x20000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0x4000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x18000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0x8000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0x20000"
+
+    # Check the entry point
+    kernel_cache.analyze("/kext-receipts/main.kc", ["-entrypoint", "-arch", "arm64"])
+    assert kernel_cache.dictionary()["entrypoint"] == "0x8000"
+
+    # Check the fixups
+    kernel_cache.analyze("/kext-receipts/main.kc", ["-fixups", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 4
+    assert kernel_cache.dictionary()["fixups"]["0x18000"] == "kc(0) + 0x18028"
+    assert kernel_cache.dictionary()["fixups"]["0x18008"] == "kc(0) + 0x18028"
+    assert kernel_cache.dictionary()["fixups"]["0x18018"] == "kc(0) + 0x1802C"
+    assert kernel_cache.dictionary()["fixups"]["0x18020"] == "kc(0) + 0x1802D"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -o main.kernel  -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack
+
diff --git a/testing/kernel-cache-tests/kext-relative-paths/bar.c b/testing/kernel-cache-tests/kext-relative-paths/bar.c
new file mode 100644 (file)
index 0000000..2ad0d8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-relative-paths/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-relative-paths/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-relative-paths/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-relative-paths/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..7eafc00
Binary files /dev/null and b/testing/kernel-cache-tests/kext-relative-paths/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kext-relative-paths/extensions/foo.kext/Contents/Info.plist b/testing/kernel-cache-tests/kext-relative-paths/extensions/foo.kext/Contents/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-relative-paths/extensions/foo.kext/Contents/MacOS/foo b/testing/kernel-cache-tests/kext-relative-paths/extensions/foo.kext/Contents/MacOS/foo
new file mode 100755 (executable)
index 0000000..cd1fa31
Binary files /dev/null and b/testing/kernel-cache-tests/kext-relative-paths/extensions/foo.kext/Contents/MacOS/foo differ
diff --git a/testing/kernel-cache-tests/kext-relative-paths/foo.c b/testing/kernel-cache-tests/kext-relative-paths/foo.c
new file mode 100644 (file)
index 0000000..5a6eec2
--- /dev/null
@@ -0,0 +1,4 @@
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/kext-relative-paths/main.c b/testing/kernel-cache-tests/kext-relative-paths/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-relative-paths/main.kernel b/testing/kernel-cache-tests/kext-relative-paths/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/kext-relative-paths/main.kernel differ
diff --git a/testing/kernel-cache-tests/kext-relative-paths/test.py b/testing/kernel-cache-tests/kext-relative-paths/test.py
new file mode 100644 (file)
index 0000000..2625b0d
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Foo has a macOS style bundle layout while bar is an iOS style layout.  Make sure we
+# get the relative path right in each case
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/kext-relative-paths/main.kc", "/kext-relative-paths/main.kernel", "/kext-relative-paths/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/kext-relative-paths/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["relativePath"] == "bar"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["relativePath"] == "Contents/MacOS/foo"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/Contents/MacOS/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/SymbolSets.plist b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/SymbolSets.plist
new file mode 100644 (file)
index 0000000..57b9a95
--- /dev/null
@@ -0,0 +1,48 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.symbols</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTV8OSObject</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/bar.cpp b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/bar.cpp
new file mode 100644 (file)
index 0000000..d144562
--- /dev/null
@@ -0,0 +1,13 @@
+
+#include "bar.h"
+
+OSDefineMetaClassAndStructors( Bar, Foo )
+
+int Bar::foo() {
+       return 1;
+}
+
+int bar() {
+       Bar* bar = new Bar();
+       return bar->foo();
+}
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/bar.h b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/bar.h
new file mode 100644 (file)
index 0000000..cd0713d
--- /dev/null
@@ -0,0 +1,10 @@
+
+#include "foo.h"
+
+class Bar : public Foo
+{
+    OSDeclareDefaultStructors( Bar )
+    
+public:
+    virtual int foo();
+};
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/baz.cpp b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/baz.cpp
new file mode 100644 (file)
index 0000000..e6ddc3f
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include "bar.h"
+
+class Baz : public Bar
+{
+    OSDeclareDefaultStructors( Baz )
+    
+public:
+    virtual int foo();
+};
+
+OSDefineMetaClassAndStructors( Baz, Bar )
+
+int Baz::foo() {
+       return 1;
+}
+
+int baz() {
+       Baz* baz = new Baz();
+       return baz->foo();
+}
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..911622b
--- /dev/null
@@ -0,0 +1,21 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..19fb1b6
Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/baz.kext/Info.plist b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/baz.kext/Info.plist
new file mode 100644 (file)
index 0000000..073175d
--- /dev/null
@@ -0,0 +1,23 @@
+<?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>CFBundleExecutable</key>
+       <string>baz</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.baz</string>
+       <key>CFBundleName</key>
+       <string>baz</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.bar</key>
+               <string>1.0</string>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/baz.kext/baz b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/baz.kext/baz
new file mode 100755 (executable)
index 0000000..8f1bd69
Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/baz.kext/baz differ
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..237c06f
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..30c7f3d
Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/foo.cpp b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/foo.cpp
new file mode 100644 (file)
index 0000000..13f66ef
--- /dev/null
@@ -0,0 +1,29 @@
+
+#include "foo.h"
+
+OSDefineMetaClassAndStructors( Foo, OSObject )
+
+// Redefine this just so that we can write tests
+#undef OSMetaClassDefineReservedUnused
+#define OSMetaClassDefineReservedUnused(className, index)       \
+void className ::_RESERVED ## className ## index ()             \
+       { gMetaClass.reservedCalled(index); }
+
+// Index 0 has been replaced with a method
+OSMetaClassDefineReservedUsed(Foo, 0)
+OSMetaClassDefineReservedUnused( Foo, 1 )
+OSMetaClassDefineReservedUnused( Foo, 2 )
+OSMetaClassDefineReservedUnused( Foo, 3 )
+
+int Foo::foo() {
+       return 0;
+}
+
+int Foo::fooUsed0() {
+       return 0;
+}
+
+int foo() {
+       Foo* foo = new Foo();
+       return foo->foo() + foo->fooUsed0();
+}
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/foo.h b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/foo.h
new file mode 100644 (file)
index 0000000..401eeb4
--- /dev/null
@@ -0,0 +1,27 @@
+
+#include <Kernel/libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+// Redefine this just so that we can write tests
+#undef OSMetaClassDeclareReservedUnused
+#define OSMetaClassDeclareReservedUnused(className, index)        \
+    private:                                                      \
+    virtual void _RESERVED ## className ## index ()
+
+class Foo : public OSObject
+{
+    OSDeclareDefaultStructors( Foo )
+    
+public:
+    virtual int foo();
+    
+#ifdef FOO_USED
+    OSMetaClassDeclareReservedUsed(Foo, 0);
+    virtual int fooUsed0();
+#else
+    OSMetaClassDeclareReservedUnused(Foo, 0);
+#endif
+    OSMetaClassDeclareReservedUnused(Foo, 1);
+    OSMetaClassDeclareReservedUnused(Foo, 2);
+    OSMetaClassDeclareReservedUnused(Foo, 3);
+};
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/main.cpp b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/main.cpp
new file mode 100644 (file)
index 0000000..3cd8622
--- /dev/null
@@ -0,0 +1,108 @@
+
+#include <libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+int __cxa_pure_virtual = 0;
+
+#if 0
+class OSObject : public OSMetaClassBase
+{
+    OSDeclareAbstractStructors(OSObject);
+
+public:
+       OSMetaClassDeclareReservedUnused(OSObject, 0);
+    OSMetaClassDeclareReservedUnused(OSObject, 1);
+    OSMetaClassDeclareReservedUnused(OSObject, 2);
+    OSMetaClassDeclareReservedUnused(OSObject, 3);
+    OSMetaClassDeclareReservedUnused(OSObject, 4);
+    OSMetaClassDeclareReservedUnused(OSObject, 5);
+    OSMetaClassDeclareReservedUnused(OSObject, 6);
+    OSMetaClassDeclareReservedUnused(OSObject, 7);
+    OSMetaClassDeclareReservedUnused(OSObject, 8);
+    OSMetaClassDeclareReservedUnused(OSObject, 9);
+    OSMetaClassDeclareReservedUnused(OSObject, 10);
+    OSMetaClassDeclareReservedUnused(OSObject, 11);
+    OSMetaClassDeclareReservedUnused(OSObject, 12);
+    OSMetaClassDeclareReservedUnused(OSObject, 13);
+    OSMetaClassDeclareReservedUnused(OSObject, 14);
+    OSMetaClassDeclareReservedUnused(OSObject, 15);
+};
+#endif
+
+// OSDefineMetaClassAndAbstractStructors(OSObject, 0);
+/* Class global data */
+OSObject::MetaClass OSObject::gMetaClass;
+const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass;
+const OSMetaClass * const OSObject::superClass = NULL;
+
+// Virtual Padding
+OSMetaClassDefineReservedUnused(OSObject, 0);
+OSMetaClassDefineReservedUnused(OSObject, 1);
+OSMetaClassDefineReservedUnused(OSObject, 2);
+OSMetaClassDefineReservedUnused(OSObject, 3);
+OSMetaClassDefineReservedUnused(OSObject, 4);
+OSMetaClassDefineReservedUnused(OSObject, 5);
+OSMetaClassDefineReservedUnused(OSObject, 6);
+OSMetaClassDefineReservedUnused(OSObject, 7);
+OSMetaClassDefineReservedUnused(OSObject, 8);
+OSMetaClassDefineReservedUnused(OSObject, 9);
+OSMetaClassDefineReservedUnused(OSObject, 10);
+OSMetaClassDefineReservedUnused(OSObject, 11);
+OSMetaClassDefineReservedUnused(OSObject, 12);
+OSMetaClassDefineReservedUnused(OSObject, 13);
+OSMetaClassDefineReservedUnused(OSObject, 14);
+OSMetaClassDefineReservedUnused(OSObject, 15);
+
+// struct IORPC { };
+
+// OSMetaClassBase
+OSMetaClassBase::OSMetaClassBase() { }
+OSMetaClassBase::~OSMetaClassBase() { }
+kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; }
+bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; }
+
+// OSMetaClass
+OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { }
+OSMetaClass::~OSMetaClass() { }
+void OSMetaClass::reservedCalled(int ind) const { }
+void OSMetaClass::retain() const { }
+void OSMetaClass::release() const { }
+void OSMetaClass::release(int freeWhen) const { }
+int OSMetaClass::getRetainCount() const { return 0; }
+const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; }
+void OSMetaClass::taggedRetain(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; }
+void OSMetaClass::instanceConstructed() const { }
+
+// OSMetaClass reserved methods
+// Thee are none of these for arm64e
+
+// OSObject::MetaClass
+OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { }
+OSObject *OSObject::MetaClass::alloc() const { return NULL; }
+kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; }
+
+// OSObject
+OSObject::OSObject(OSMetaClass const*) { }
+OSObject::~OSObject() { }
+const OSMetaClass* OSObject::getMetaClass() const { return NULL; }
+void OSObject::free() { }
+bool OSObject::init() { return false; }
+void OSObject::retain() const { }
+void OSObject::release() const { }
+void OSObject::release(int freeWhen) const { }
+int OSObject::getRetainCount() const { return 0; }
+void OSObject::taggedRetain(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSObject::serialize(OSSerialize * serializer) const { return false; }
+kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; }
+void* OSObject::operator new(unsigned long) { return (void*)1; }
+void OSObject::operator delete(void*, unsigned long) { return; }
+
+__attribute__((section(("__TEXT_EXEC, __text"))))
+extern "C" int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/main.kernel b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/main.kernel
new file mode 100755 (executable)
index 0000000..0fb7e40
Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/main.kernel differ
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-arm64e/test.py b/testing/kernel-cache-tests/kext-vtable-patching-arm64e/test.py
new file mode 100644 (file)
index 0000000..297cd0e
--- /dev/null
@@ -0,0 +1,68 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that a kext can bind to another kext
+# foo.kext exports foo and bar.kext uses it
+# bar.kext then exports foo and baz.kext binds to it
+
+# Note this is the same as the kext-vtable-patching test, just with arm64e so ptrauth on the fixups
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64e", "/kext-vtable-patching-arm64e/main.kc", "/kext-vtable-patching-arm64e/main.kernel", "/kext-vtable-patching-arm64e/extensions", ["com.apple.foo", "com.apple.bar", "com.apple.baz"], [])
+    kernel_cache.analyze("/kext-vtable-patching-arm64e/main.kc", ["-layout", "-arch", "arm64e"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.baz"
+    assert kernel_cache.dictionary()["dylibs"][3]["name"] == "com.apple.foo"
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/kext-vtable-patching-arm64e/main.kc", ["-symbols", "-arch", "arm64e"])
+    
+    # From bar, find the vtable and its override of foo()
+    # Bar::foo()
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][3]["name"] == "__ZN3Bar3fooEv"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][3]["vmAddr"] == "0x2036C"
+    
+    # From baz, find the vtable and its override of foo()
+    # Baz::foo()
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][3]["name"] == "__ZN3Baz3fooEv"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][3]["vmAddr"] == "0x2085C"
+    
+    # From foo, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable
+    # Foo::foo()
+    assert kernel_cache.dictionary()["dylibs"][3]["global-symbols"][6]["name"] == "__ZN3Foo3fooEv"
+    assert kernel_cache.dictionary()["dylibs"][3]["global-symbols"][6]["vmAddr"] == "0x20DE8"
+    # Foo::fooUsed0()
+    assert kernel_cache.dictionary()["dylibs"][3]["global-symbols"][7]["name"] == "__ZN3Foo8fooUsed0Ev"
+    assert kernel_cache.dictionary()["dylibs"][3]["global-symbols"][7]["vmAddr"] == "0x20E00"
+
+
+    # Check the fixups
+    kernel_cache.analyze("/kext-vtable-patching-arm64e/main.kc", ["-fixups", "-arch", "arm64e"])
+    
+    # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup
+    assert kernel_cache.dictionary()["fixups"]["0x20480"] == "kc(0) + 0x20DE8 auth(IA addr 49764)"
+    # Then the following fixup should be to Foo::fooUsed0()
+    assert kernel_cache.dictionary()["fixups"]["0x20488"] == "kc(0) + 0x20E00 auth(IA addr 61962)"
+
+    # Now in bar, again match the entry for its Bar::foo() symbol
+    assert kernel_cache.dictionary()["fixups"]["0x200E8"] == "kc(0) + 0x2036C auth(IA addr 49764)"
+    # And if the patching was correct, then following entry should be to Foo::fooUsed0()
+    assert kernel_cache.dictionary()["fixups"]["0x200F0"] == "kc(0) + 0x20E00 auth(IA addr 61962)"
+
+    # Now in baz, again match the entry for its Baz::foo() symbol
+    assert kernel_cache.dictionary()["fixups"]["0x202B0"] == "kc(0) + 0x2085C auth(IA addr 49764)"
+    # And if the patching was correct, then following entry should be to Foo::fooUsed0()
+    assert kernel_cache.dictionary()["fixups"]["0x202B8"] == "kc(0) + 0x20E00 auth(IA addr 61962)"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r-- -Wl,-fixup_chains -Wl,-kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.cpp -o extensions/foo.kext/foo -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -DFOO_USED=1
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const baz.cpp -o extensions/baz.kext/baz -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/SymbolSets.plist b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/SymbolSets.plist
new file mode 100644 (file)
index 0000000..57b9a95
--- /dev/null
@@ -0,0 +1,48 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.symbols</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTV8OSObject</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/bar.cpp b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/bar.cpp
new file mode 100644 (file)
index 0000000..a87b825
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include "foo.h"
+
+class Bar : public Foo
+{
+    OSDeclareDefaultStructors( Bar )
+    
+public:
+    virtual int foo();
+};
+
+OSDefineMetaClassAndStructors( Bar, Foo )
+
+int Bar::foo() {
+       return 1;
+}
+
+int bar() {
+       Bar* bar = new Bar();
+       return bar->foo();
+}
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..911622b
--- /dev/null
@@ -0,0 +1,21 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..111490e
Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..237c06f
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..9483e1f
Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/foo.cpp b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/foo.cpp
new file mode 100644 (file)
index 0000000..83e5e67
--- /dev/null
@@ -0,0 +1,23 @@
+
+#include "foo.h"
+
+OSDefineMetaClassAndStructors( Foo, OSObject )
+
+// Index 0 has been replaced with a method
+OSMetaClassDefineReservedUsed(Foo, 0)
+OSMetaClassDefineReservedUnused( Foo, 1 )
+OSMetaClassDefineReservedUnused( Foo, 2 )
+OSMetaClassDefineReservedUnused( Foo, 3 )
+
+int Foo::foo() {
+       return 0;
+}
+
+int Foo::fooUsed0() {
+       return 0;
+}
+
+int foo() {
+       Foo* foo = new Foo();
+       return foo->foo() + foo->fooUsed0();
+}
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/foo.h b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/foo.h
new file mode 100644 (file)
index 0000000..67aff00
--- /dev/null
@@ -0,0 +1,22 @@
+
+#include <Kernel/libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+class Foo : public OSObject
+{
+    OSDeclareDefaultStructors( Foo )
+    
+public:
+    virtual int foo();
+    
+#ifdef FOO_USED
+    OSMetaClassDeclareReservedUsed(Foo, 0);
+    virtual int fooUsed0();
+
+    OSMetaClassDeclareReservedUnused(Foo, 1);
+    OSMetaClassDeclareReservedUnused(Foo, 2);
+    OSMetaClassDeclareReservedUnused(Foo, 3);
+#else
+    OSMetaClassDeclareReservedUnused(Foo, 0);
+#endif
+};
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/main.cpp b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/main.cpp
new file mode 100644 (file)
index 0000000..b689f8a
--- /dev/null
@@ -0,0 +1,119 @@
+
+#include <libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+int __cxa_pure_virtual = 0;
+
+#if 0
+class OSObject : public OSMetaClassBase
+{
+    OSDeclareAbstractStructors(OSObject);
+
+public:
+       OSMetaClassDeclareReservedUnused(OSObject, 0);
+    OSMetaClassDeclareReservedUnused(OSObject, 1);
+    OSMetaClassDeclareReservedUnused(OSObject, 2);
+    OSMetaClassDeclareReservedUnused(OSObject, 3);
+    OSMetaClassDeclareReservedUnused(OSObject, 4);
+    OSMetaClassDeclareReservedUnused(OSObject, 5);
+    OSMetaClassDeclareReservedUnused(OSObject, 6);
+    OSMetaClassDeclareReservedUnused(OSObject, 7);
+    OSMetaClassDeclareReservedUnused(OSObject, 8);
+    OSMetaClassDeclareReservedUnused(OSObject, 9);
+    OSMetaClassDeclareReservedUnused(OSObject, 10);
+    OSMetaClassDeclareReservedUnused(OSObject, 11);
+    OSMetaClassDeclareReservedUnused(OSObject, 12);
+    OSMetaClassDeclareReservedUnused(OSObject, 13);
+    OSMetaClassDeclareReservedUnused(OSObject, 14);
+    OSMetaClassDeclareReservedUnused(OSObject, 15);
+};
+#endif
+
+// OSDefineMetaClassAndAbstractStructors(OSObject, 0);
+/* Class global data */
+OSObject::MetaClass OSObject::gMetaClass;
+const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass;
+const OSMetaClass * const OSObject::superClass = NULL;
+
+// Virtual Padding
+OSMetaClassDefineReservedUnused(OSObject, 0);
+OSMetaClassDefineReservedUnused(OSObject, 1);
+OSMetaClassDefineReservedUnused(OSObject, 2);
+OSMetaClassDefineReservedUnused(OSObject, 3);
+OSMetaClassDefineReservedUnused(OSObject, 4);
+OSMetaClassDefineReservedUnused(OSObject, 5);
+OSMetaClassDefineReservedUnused(OSObject, 6);
+OSMetaClassDefineReservedUnused(OSObject, 7);
+OSMetaClassDefineReservedUnused(OSObject, 8);
+OSMetaClassDefineReservedUnused(OSObject, 9);
+OSMetaClassDefineReservedUnused(OSObject, 10);
+OSMetaClassDefineReservedUnused(OSObject, 11);
+OSMetaClassDefineReservedUnused(OSObject, 12);
+OSMetaClassDefineReservedUnused(OSObject, 13);
+OSMetaClassDefineReservedUnused(OSObject, 14);
+OSMetaClassDefineReservedUnused(OSObject, 15);
+
+// struct IORPC { };
+
+// OSMetaClassBase
+OSMetaClassBase::OSMetaClassBase() { }
+OSMetaClassBase::~OSMetaClassBase() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { }
+kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; }
+bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; }
+
+// OSMetaClass
+OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { }
+OSMetaClass::~OSMetaClass() { }
+void OSMetaClass::reservedCalled(int ind) const { }
+void OSMetaClass::retain() const { }
+void OSMetaClass::release() const { }
+void OSMetaClass::release(int freeWhen) const { }
+int OSMetaClass::getRetainCount() const { return 0; }
+const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; }
+void OSMetaClass::taggedRetain(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; }
+void OSMetaClass::instanceConstructed() const { }
+
+// OSMetaClass reserved methods
+void OSMetaClass::_RESERVEDOSMetaClass0() { }
+void OSMetaClass::_RESERVEDOSMetaClass1() { }
+void OSMetaClass::_RESERVEDOSMetaClass2() { }
+void OSMetaClass::_RESERVEDOSMetaClass3() { }
+void OSMetaClass::_RESERVEDOSMetaClass4() { }
+void OSMetaClass::_RESERVEDOSMetaClass5() { }
+void OSMetaClass::_RESERVEDOSMetaClass6() { }
+void OSMetaClass::_RESERVEDOSMetaClass7() { }
+
+// OSObject::MetaClass
+OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { }
+OSObject *OSObject::MetaClass::alloc() const { return NULL; }
+kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; }
+
+// OSObject
+OSObject::OSObject(OSMetaClass const*) { }
+OSObject::~OSObject() { }
+const OSMetaClass* OSObject::getMetaClass() const { return NULL; }
+void OSObject::free() { }
+bool OSObject::init() { return false; }
+void OSObject::retain() const { }
+void OSObject::release() const { }
+void OSObject::release(int freeWhen) const { }
+int OSObject::getRetainCount() const { return 0; }
+void OSObject::taggedRetain(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSObject::serialize(OSSerialize * serializer) const { return false; }
+kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; }
+void* OSObject::operator new(unsigned long) { return (void*)1; }
+void OSObject::operator delete(void*, unsigned long) { return; }
+
+__attribute__((section(("__HIB, __text"))))
+extern "C" int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/main.kernel b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/main.kernel
new file mode 100755 (executable)
index 0000000..119060f
Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/main.kernel differ
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/test.py b/testing/kernel-cache-tests/kext-vtable-patching-error-small-vtable/test.py
new file mode 100644 (file)
index 0000000..572a025
--- /dev/null
@@ -0,0 +1,20 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Try to patch a vtable, but the parent vtable is too small
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/kext-vtable-patching-error-small-vtable/main.kc", "/kext-vtable-patching-error-small-vtable/main.kernel", "/kext-vtable-patching-error-small-vtable/extensions", ["com.apple.foo", "com.apple.bar"], ["-json-errors"])
+
+    assert len(kernel_cache.dictionary()) == 1
+    assert kernel_cache.dictionary()[0]["id"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()[0]["errors"]) == 1
+    assert kernel_cache.dictionary()[0]["errors"][0] == "Malformed vtable.  Super class '__ZTV3Foo' has 40 entries vs subclass '__ZTV3Bar' with 37 entries"
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r--
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.cpp -o extensions/foo.kext/foo -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -DFOO_USED=1
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/SymbolSets.plist b/testing/kernel-cache-tests/kext-vtable-patching-overrides/SymbolSets.plist
new file mode 100644 (file)
index 0000000..57b9a95
--- /dev/null
@@ -0,0 +1,48 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.symbols</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTV8OSObject</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/bar.cpp b/testing/kernel-cache-tests/kext-vtable-patching-overrides/bar.cpp
new file mode 100644 (file)
index 0000000..3985772
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include "foo.h"
+
+class Bar : public FooSub
+{
+    OSDeclareDefaultStructors( Bar )
+    
+public:
+    virtual int foo();
+};
+
+OSDefineMetaClassAndStructors( Bar, FooSub )
+
+int Bar::foo() {
+       return 1;
+}
+
+int bar() {
+       Bar* bar = new Bar();
+       return bar->foo();
+}
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..911622b
--- /dev/null
@@ -0,0 +1,21 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..11d535e
Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..237c06f
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..2a6f8e2
Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching-overrides/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/foo.cpp b/testing/kernel-cache-tests/kext-vtable-patching-overrides/foo.cpp
new file mode 100644 (file)
index 0000000..a82a5f7
--- /dev/null
@@ -0,0 +1,37 @@
+
+#include "foo.h"
+
+OSDefineMetaClassAndStructors( Foo, OSObject )
+
+// Index 0 has been replaced with a method
+OSMetaClassDefineReservedUsed(Foo, 0)
+OSMetaClassDefineReservedUnused( Foo, 1 )
+OSMetaClassDefineReservedUnused( Foo, 2 )
+OSMetaClassDefineReservedUnused( Foo, 3 )
+
+int Foo::foo() {
+       return 0;
+}
+
+int Foo::fooOverride() {
+       return 0;
+}
+
+int Foo::fooUsed0() {
+       return 0;
+}
+
+OSDefineMetaClassAndStructors( FooSub, Foo )
+
+int FooSub::foo() {
+       return 0;
+}
+
+int FooSub::fooOverride() {
+       return 0;
+}
+
+int foo() {
+       Foo* foo = new Foo();
+       return foo->foo() + foo->fooUsed0();
+}
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/foo.h b/testing/kernel-cache-tests/kext-vtable-patching-overrides/foo.h
new file mode 100644 (file)
index 0000000..826eccc
--- /dev/null
@@ -0,0 +1,35 @@
+
+#include <Kernel/libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+class Foo : public OSObject
+{
+    OSDeclareDefaultStructors( Foo )
+    
+public:
+    virtual int foo();
+    virtual int fooOverride();
+    
+#ifdef FOO_USED
+    OSMetaClassDeclareReservedUsed(Foo, 0);
+    virtual int fooUsed0();
+#else
+    OSMetaClassDeclareReservedUnused(Foo, 0);
+#endif
+    OSMetaClassDeclareReservedUnused(Foo, 1);
+    OSMetaClassDeclareReservedUnused(Foo, 2);
+    OSMetaClassDeclareReservedUnused(Foo, 3);
+};
+
+class FooSub : public Foo
+{
+       OSDeclareDefaultStructors( FooSub )
+
+public:
+       virtual int foo();
+
+#ifdef FOO_OVERRIDE
+       virtual int fooOverride();
+#endif
+
+};
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/main.cpp b/testing/kernel-cache-tests/kext-vtable-patching-overrides/main.cpp
new file mode 100644 (file)
index 0000000..b689f8a
--- /dev/null
@@ -0,0 +1,119 @@
+
+#include <libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+int __cxa_pure_virtual = 0;
+
+#if 0
+class OSObject : public OSMetaClassBase
+{
+    OSDeclareAbstractStructors(OSObject);
+
+public:
+       OSMetaClassDeclareReservedUnused(OSObject, 0);
+    OSMetaClassDeclareReservedUnused(OSObject, 1);
+    OSMetaClassDeclareReservedUnused(OSObject, 2);
+    OSMetaClassDeclareReservedUnused(OSObject, 3);
+    OSMetaClassDeclareReservedUnused(OSObject, 4);
+    OSMetaClassDeclareReservedUnused(OSObject, 5);
+    OSMetaClassDeclareReservedUnused(OSObject, 6);
+    OSMetaClassDeclareReservedUnused(OSObject, 7);
+    OSMetaClassDeclareReservedUnused(OSObject, 8);
+    OSMetaClassDeclareReservedUnused(OSObject, 9);
+    OSMetaClassDeclareReservedUnused(OSObject, 10);
+    OSMetaClassDeclareReservedUnused(OSObject, 11);
+    OSMetaClassDeclareReservedUnused(OSObject, 12);
+    OSMetaClassDeclareReservedUnused(OSObject, 13);
+    OSMetaClassDeclareReservedUnused(OSObject, 14);
+    OSMetaClassDeclareReservedUnused(OSObject, 15);
+};
+#endif
+
+// OSDefineMetaClassAndAbstractStructors(OSObject, 0);
+/* Class global data */
+OSObject::MetaClass OSObject::gMetaClass;
+const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass;
+const OSMetaClass * const OSObject::superClass = NULL;
+
+// Virtual Padding
+OSMetaClassDefineReservedUnused(OSObject, 0);
+OSMetaClassDefineReservedUnused(OSObject, 1);
+OSMetaClassDefineReservedUnused(OSObject, 2);
+OSMetaClassDefineReservedUnused(OSObject, 3);
+OSMetaClassDefineReservedUnused(OSObject, 4);
+OSMetaClassDefineReservedUnused(OSObject, 5);
+OSMetaClassDefineReservedUnused(OSObject, 6);
+OSMetaClassDefineReservedUnused(OSObject, 7);
+OSMetaClassDefineReservedUnused(OSObject, 8);
+OSMetaClassDefineReservedUnused(OSObject, 9);
+OSMetaClassDefineReservedUnused(OSObject, 10);
+OSMetaClassDefineReservedUnused(OSObject, 11);
+OSMetaClassDefineReservedUnused(OSObject, 12);
+OSMetaClassDefineReservedUnused(OSObject, 13);
+OSMetaClassDefineReservedUnused(OSObject, 14);
+OSMetaClassDefineReservedUnused(OSObject, 15);
+
+// struct IORPC { };
+
+// OSMetaClassBase
+OSMetaClassBase::OSMetaClassBase() { }
+OSMetaClassBase::~OSMetaClassBase() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { }
+kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; }
+bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; }
+
+// OSMetaClass
+OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { }
+OSMetaClass::~OSMetaClass() { }
+void OSMetaClass::reservedCalled(int ind) const { }
+void OSMetaClass::retain() const { }
+void OSMetaClass::release() const { }
+void OSMetaClass::release(int freeWhen) const { }
+int OSMetaClass::getRetainCount() const { return 0; }
+const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; }
+void OSMetaClass::taggedRetain(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; }
+void OSMetaClass::instanceConstructed() const { }
+
+// OSMetaClass reserved methods
+void OSMetaClass::_RESERVEDOSMetaClass0() { }
+void OSMetaClass::_RESERVEDOSMetaClass1() { }
+void OSMetaClass::_RESERVEDOSMetaClass2() { }
+void OSMetaClass::_RESERVEDOSMetaClass3() { }
+void OSMetaClass::_RESERVEDOSMetaClass4() { }
+void OSMetaClass::_RESERVEDOSMetaClass5() { }
+void OSMetaClass::_RESERVEDOSMetaClass6() { }
+void OSMetaClass::_RESERVEDOSMetaClass7() { }
+
+// OSObject::MetaClass
+OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { }
+OSObject *OSObject::MetaClass::alloc() const { return NULL; }
+kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; }
+
+// OSObject
+OSObject::OSObject(OSMetaClass const*) { }
+OSObject::~OSObject() { }
+const OSMetaClass* OSObject::getMetaClass() const { return NULL; }
+void OSObject::free() { }
+bool OSObject::init() { return false; }
+void OSObject::retain() const { }
+void OSObject::release() const { }
+void OSObject::release(int freeWhen) const { }
+int OSObject::getRetainCount() const { return 0; }
+void OSObject::taggedRetain(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSObject::serialize(OSSerialize * serializer) const { return false; }
+kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; }
+void* OSObject::operator new(unsigned long) { return (void*)1; }
+void OSObject::operator delete(void*, unsigned long) { return; }
+
+__attribute__((section(("__HIB, __text"))))
+extern "C" int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/main.kernel b/testing/kernel-cache-tests/kext-vtable-patching-overrides/main.kernel
new file mode 100755 (executable)
index 0000000..119060f
Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching-overrides/main.kernel differ
diff --git a/testing/kernel-cache-tests/kext-vtable-patching-overrides/test.py b/testing/kernel-cache-tests/kext-vtable-patching-overrides/test.py
new file mode 100644 (file)
index 0000000..67a237c
--- /dev/null
@@ -0,0 +1,78 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that a kext can bind to another kext
+# foo.kext exports foo and bar.kext uses it
+# This is the same as kext-vtable-patching but checks that overrides of methods in parent classes
+# are propagated to child classes
+# Foo defines fooOverride().  
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/kext-vtable-patching-overrides/main.kc", "/kext-vtable-patching-overrides/main.kernel", "/kext-vtable-patching-overrides/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/kext-vtable-patching-overrides/main.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/kext-vtable-patching-overrides/main.kc", ["-symbols", "-arch", "x86_64"])
+    
+    # From bar, find the vtable and its override of foo()
+    # Bar::foo()
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][3]["name"] == "__ZN3Bar3fooEv"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][3]["vmAddr"] == "0x24F10"
+    
+    # From foo, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable
+    # Foo::fooOverride()
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][3]["name"] == "__ZN3Foo11fooOverrideEv"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][3]["vmAddr"] == "0x26BA0"
+    # Foo::foo()
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][7]["name"] == "__ZN3Foo3fooEv"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][7]["vmAddr"] == "0x26B80"
+    # Foo::fooUsed0()
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][8]["name"] == "__ZN3Foo8fooUsed0Ev"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][8]["vmAddr"] == "0x26BC0"
+
+    # From foo
+    # FooSub::fooOverride()
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][21]["name"] == "__ZN6FooSub11fooOverrideEv"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][21]["vmAddr"] == "0x26EA0"
+    # Foo::foo()
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][22]["name"] == "__ZN6FooSub3fooEv"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][22]["vmAddr"] == "0x26E80"
+
+
+    # Check the fixups
+    kernel_cache.analyze("/kext-vtable-patching-overrides/main.kc", ["-fixups", "-arch", "x86_64"])
+    
+    # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup
+    assert kernel_cache.dictionary()["fixups"]["0x23150"] == "kc(0) + 0x26B80"
+    # Then the following fixup should be to Foo::fooOverride()
+    assert kernel_cache.dictionary()["fixups"]["0x23158"] == "kc(0) + 0x26BA0"
+    # Then the following fixup should be to Foo::fooUsed0()
+    assert kernel_cache.dictionary()["fixups"]["0x23160"] == "kc(0) + 0x26BC0"
+    
+    # In vtable for FooSub, we match the entry for FooSub::foo() by looking for its value on the RHS of the fixup
+    assert kernel_cache.dictionary()["fixups"]["0x233A8"] == "kc(0) + 0x26E80"
+    # Then the following fixup should be to FooSub::fooOverride()
+    assert kernel_cache.dictionary()["fixups"]["0x233B0"] == "kc(0) + 0x26EA0"
+    # Then the following fixup should be to Foo::fooUsed0()
+    assert kernel_cache.dictionary()["fixups"]["0x233B8"] == "kc(0) + 0x26BC0"
+
+    # Now in bar, again match the entry for its Bar::foo() symbol
+    assert kernel_cache.dictionary()["fixups"]["0x21150"] == "kc(0) + 0x24F10"
+    # Then the following fixup should be to FooSub::fooOverride()
+    assert kernel_cache.dictionary()["fixups"]["0x21158"] == "kc(0) + 0x26EA0"
+    # And if the patching was correct, then following entry should be to Foo::fooUsed0()
+    assert kernel_cache.dictionary()["fixups"]["0x21160"] == "kc(0) + 0x26BC0"
+
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r--
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-no_data_const foo.cpp -o extensions/foo.kext/foo -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -DFOO_USED=1 -DFOO_OVERRIDE=1
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-no_data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kext-vtable-patching/SymbolSets.plist b/testing/kernel-cache-tests/kext-vtable-patching/SymbolSets.plist
new file mode 100644 (file)
index 0000000..57b9a95
--- /dev/null
@@ -0,0 +1,48 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.symbols</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTV8OSObject</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-vtable-patching/bar.cpp b/testing/kernel-cache-tests/kext-vtable-patching/bar.cpp
new file mode 100644 (file)
index 0000000..a87b825
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include "foo.h"
+
+class Bar : public Foo
+{
+    OSDeclareDefaultStructors( Bar )
+    
+public:
+    virtual int foo();
+};
+
+OSDefineMetaClassAndStructors( Bar, Foo )
+
+int Bar::foo() {
+       return 1;
+}
+
+int bar() {
+       Bar* bar = new Bar();
+       return bar->foo();
+}
diff --git a/testing/kernel-cache-tests/kext-vtable-patching/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-vtable-patching/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..911622b
--- /dev/null
@@ -0,0 +1,21 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-vtable-patching/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-vtable-patching/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..b3bd60b
Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kext-vtable-patching/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-vtable-patching/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..237c06f
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-vtable-patching/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-vtable-patching/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..5fe8196
Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/kext-vtable-patching/foo.cpp b/testing/kernel-cache-tests/kext-vtable-patching/foo.cpp
new file mode 100644 (file)
index 0000000..83e5e67
--- /dev/null
@@ -0,0 +1,23 @@
+
+#include "foo.h"
+
+OSDefineMetaClassAndStructors( Foo, OSObject )
+
+// Index 0 has been replaced with a method
+OSMetaClassDefineReservedUsed(Foo, 0)
+OSMetaClassDefineReservedUnused( Foo, 1 )
+OSMetaClassDefineReservedUnused( Foo, 2 )
+OSMetaClassDefineReservedUnused( Foo, 3 )
+
+int Foo::foo() {
+       return 0;
+}
+
+int Foo::fooUsed0() {
+       return 0;
+}
+
+int foo() {
+       Foo* foo = new Foo();
+       return foo->foo() + foo->fooUsed0();
+}
diff --git a/testing/kernel-cache-tests/kext-vtable-patching/foo.h b/testing/kernel-cache-tests/kext-vtable-patching/foo.h
new file mode 100644 (file)
index 0000000..31d6216
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include <Kernel/libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+class Foo : public OSObject
+{
+    OSDeclareDefaultStructors( Foo )
+    
+public:
+    virtual int foo();
+    
+#ifdef FOO_USED
+    OSMetaClassDeclareReservedUsed(Foo, 0);
+    virtual int fooUsed0();
+#else
+    OSMetaClassDeclareReservedUnused(Foo, 0);
+#endif
+    OSMetaClassDeclareReservedUnused(Foo, 1);
+    OSMetaClassDeclareReservedUnused(Foo, 2);
+    OSMetaClassDeclareReservedUnused(Foo, 3);
+};
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-vtable-patching/main.cpp b/testing/kernel-cache-tests/kext-vtable-patching/main.cpp
new file mode 100644 (file)
index 0000000..b689f8a
--- /dev/null
@@ -0,0 +1,119 @@
+
+#include <libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+int __cxa_pure_virtual = 0;
+
+#if 0
+class OSObject : public OSMetaClassBase
+{
+    OSDeclareAbstractStructors(OSObject);
+
+public:
+       OSMetaClassDeclareReservedUnused(OSObject, 0);
+    OSMetaClassDeclareReservedUnused(OSObject, 1);
+    OSMetaClassDeclareReservedUnused(OSObject, 2);
+    OSMetaClassDeclareReservedUnused(OSObject, 3);
+    OSMetaClassDeclareReservedUnused(OSObject, 4);
+    OSMetaClassDeclareReservedUnused(OSObject, 5);
+    OSMetaClassDeclareReservedUnused(OSObject, 6);
+    OSMetaClassDeclareReservedUnused(OSObject, 7);
+    OSMetaClassDeclareReservedUnused(OSObject, 8);
+    OSMetaClassDeclareReservedUnused(OSObject, 9);
+    OSMetaClassDeclareReservedUnused(OSObject, 10);
+    OSMetaClassDeclareReservedUnused(OSObject, 11);
+    OSMetaClassDeclareReservedUnused(OSObject, 12);
+    OSMetaClassDeclareReservedUnused(OSObject, 13);
+    OSMetaClassDeclareReservedUnused(OSObject, 14);
+    OSMetaClassDeclareReservedUnused(OSObject, 15);
+};
+#endif
+
+// OSDefineMetaClassAndAbstractStructors(OSObject, 0);
+/* Class global data */
+OSObject::MetaClass OSObject::gMetaClass;
+const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass;
+const OSMetaClass * const OSObject::superClass = NULL;
+
+// Virtual Padding
+OSMetaClassDefineReservedUnused(OSObject, 0);
+OSMetaClassDefineReservedUnused(OSObject, 1);
+OSMetaClassDefineReservedUnused(OSObject, 2);
+OSMetaClassDefineReservedUnused(OSObject, 3);
+OSMetaClassDefineReservedUnused(OSObject, 4);
+OSMetaClassDefineReservedUnused(OSObject, 5);
+OSMetaClassDefineReservedUnused(OSObject, 6);
+OSMetaClassDefineReservedUnused(OSObject, 7);
+OSMetaClassDefineReservedUnused(OSObject, 8);
+OSMetaClassDefineReservedUnused(OSObject, 9);
+OSMetaClassDefineReservedUnused(OSObject, 10);
+OSMetaClassDefineReservedUnused(OSObject, 11);
+OSMetaClassDefineReservedUnused(OSObject, 12);
+OSMetaClassDefineReservedUnused(OSObject, 13);
+OSMetaClassDefineReservedUnused(OSObject, 14);
+OSMetaClassDefineReservedUnused(OSObject, 15);
+
+// struct IORPC { };
+
+// OSMetaClassBase
+OSMetaClassBase::OSMetaClassBase() { }
+OSMetaClassBase::~OSMetaClassBase() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { }
+kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; }
+bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; }
+
+// OSMetaClass
+OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { }
+OSMetaClass::~OSMetaClass() { }
+void OSMetaClass::reservedCalled(int ind) const { }
+void OSMetaClass::retain() const { }
+void OSMetaClass::release() const { }
+void OSMetaClass::release(int freeWhen) const { }
+int OSMetaClass::getRetainCount() const { return 0; }
+const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; }
+void OSMetaClass::taggedRetain(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; }
+void OSMetaClass::instanceConstructed() const { }
+
+// OSMetaClass reserved methods
+void OSMetaClass::_RESERVEDOSMetaClass0() { }
+void OSMetaClass::_RESERVEDOSMetaClass1() { }
+void OSMetaClass::_RESERVEDOSMetaClass2() { }
+void OSMetaClass::_RESERVEDOSMetaClass3() { }
+void OSMetaClass::_RESERVEDOSMetaClass4() { }
+void OSMetaClass::_RESERVEDOSMetaClass5() { }
+void OSMetaClass::_RESERVEDOSMetaClass6() { }
+void OSMetaClass::_RESERVEDOSMetaClass7() { }
+
+// OSObject::MetaClass
+OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { }
+OSObject *OSObject::MetaClass::alloc() const { return NULL; }
+kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; }
+
+// OSObject
+OSObject::OSObject(OSMetaClass const*) { }
+OSObject::~OSObject() { }
+const OSMetaClass* OSObject::getMetaClass() const { return NULL; }
+void OSObject::free() { }
+bool OSObject::init() { return false; }
+void OSObject::retain() const { }
+void OSObject::release() const { }
+void OSObject::release(int freeWhen) const { }
+int OSObject::getRetainCount() const { return 0; }
+void OSObject::taggedRetain(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSObject::serialize(OSSerialize * serializer) const { return false; }
+kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; }
+void* OSObject::operator new(unsigned long) { return (void*)1; }
+void OSObject::operator delete(void*, unsigned long) { return; }
+
+__attribute__((section(("__HIB, __text"))))
+extern "C" int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-vtable-patching/main.kernel b/testing/kernel-cache-tests/kext-vtable-patching/main.kernel
new file mode 100755 (executable)
index 0000000..119060f
Binary files /dev/null and b/testing/kernel-cache-tests/kext-vtable-patching/main.kernel differ
diff --git a/testing/kernel-cache-tests/kext-vtable-patching/test.py b/testing/kernel-cache-tests/kext-vtable-patching/test.py
new file mode 100644 (file)
index 0000000..088713b
--- /dev/null
@@ -0,0 +1,53 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that a kext can bind to another kext
+# foo.kext exports foo and bar.kext uses it
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/kext-vtable-patching/main.kc", "/kext-vtable-patching/main.kernel", "/kext-vtable-patching/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/kext-vtable-patching/main.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/kext-vtable-patching/main.kc", ["-symbols", "-arch", "x86_64"])
+    
+    # From bar, find the vtable and its override of foo()
+    # Bar::foo()
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][3]["name"] == "__ZN3Bar3fooEv"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][3]["vmAddr"] == "0x16F10"
+    
+    # From foo, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable
+    # Foo::foo()
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][6]["name"] == "__ZN3Foo3fooEv"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][6]["vmAddr"] == "0x17ED0"
+    # Foo::fooUsed0()
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][7]["name"] == "__ZN3Foo8fooUsed0Ev"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][7]["vmAddr"] == "0x17EF0"
+
+
+    # Check the fixups
+    kernel_cache.analyze("/kext-vtable-patching/main.kc", ["-fixups", "-arch", "x86_64"])
+    
+    # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup
+    assert kernel_cache.dictionary()["fixups"]["0x1D3E0"] == "kc(0) + 0x17ED0"
+    # Then the following fixup should be to Foo::fooUsed0()
+    assert kernel_cache.dictionary()["fixups"]["0x1D3E8"] == "kc(0) + 0x17EF0"
+
+    # Now in bar, again match the entry for its Bar::foo() symbol
+    assert kernel_cache.dictionary()["fixups"]["0x1D150"] == "kc(0) + 0x16F10"
+    # And if the patching was correct, then following entry should be to Foo::fooUsed0()
+    assert kernel_cache.dictionary()["fixups"]["0x1D158"] == "kc(0) + 0x17EF0"
+
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r--
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.cpp -o extensions/foo.kext/foo -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -DFOO_USED=1
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kext-weak-bind-chained/bar.c b/testing/kernel-cache-tests/kext-weak-bind-chained/bar.c
new file mode 100644 (file)
index 0000000..b8f2ff3
--- /dev/null
@@ -0,0 +1,7 @@
+
+__attribute__((weak))
+int weakValue = 0;
+
+int bar() {
+       return weakValue;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..76d9c80
Binary files /dev/null and b/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..47e2270
Binary files /dev/null and b/testing/kernel-cache-tests/kext-weak-bind-chained/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/kext-weak-bind-chained/foo.c b/testing/kernel-cache-tests/kext-weak-bind-chained/foo.c
new file mode 100644 (file)
index 0000000..b58a506
--- /dev/null
@@ -0,0 +1,7 @@
+
+__attribute__((weak))
+int weakValue = 0;
+
+int foo() {
+       return weakValue;
+}
diff --git a/testing/kernel-cache-tests/kext-weak-bind-chained/main.c b/testing/kernel-cache-tests/kext-weak-bind-chained/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-weak-bind-chained/main.kernel b/testing/kernel-cache-tests/kext-weak-bind-chained/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/kext-weak-bind-chained/main.kernel differ
diff --git a/testing/kernel-cache-tests/kext-weak-bind-chained/test.py b/testing/kernel-cache-tests/kext-weak-bind-chained/test.py
new file mode 100644 (file)
index 0000000..90b5c57
--- /dev/null
@@ -0,0 +1,37 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that weak binds point to the same symbol
+
+# FIXME: This should be re-enabled once we know how to handle classic relocs combined with split seg v2.
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/kext-weak-bind-chained/main.kc", "/kext-weak-bind-chained/main.kernel", "/kext-weak-bind-chained/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/kext-weak-bind-chained/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+
+    # Check the fixups
+    kernel_cache.analyze("/kext-weak-bind-chained/main.kc", ["-fixups", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 2
+    assert kernel_cache.dictionary()["fixups"]["0x1C000"] == "kc(0) + 0x1C018"
+    assert kernel_cache.dictionary()["fixups"]["0x1C010"] == "kc(0) + 0x1C018"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo -Wl,-fixup_chains
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar -Wl,-fixup_chains
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kext-weak-bind/bar.c b/testing/kernel-cache-tests/kext-weak-bind/bar.c
new file mode 100644 (file)
index 0000000..b8f2ff3
--- /dev/null
@@ -0,0 +1,7 @@
+
+__attribute__((weak))
+int weakValue = 0;
+
+int bar() {
+       return weakValue;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-weak-bind/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kext-weak-bind/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-weak-bind/extensions/bar.kext/bar b/testing/kernel-cache-tests/kext-weak-bind/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..58085a5
Binary files /dev/null and b/testing/kernel-cache-tests/kext-weak-bind/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kext-weak-bind/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kext-weak-bind/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kext-weak-bind/extensions/foo.kext/foo b/testing/kernel-cache-tests/kext-weak-bind/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..6d0c0c5
Binary files /dev/null and b/testing/kernel-cache-tests/kext-weak-bind/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/kext-weak-bind/foo.c b/testing/kernel-cache-tests/kext-weak-bind/foo.c
new file mode 100644 (file)
index 0000000..b58a506
--- /dev/null
@@ -0,0 +1,7 @@
+
+__attribute__((weak))
+int weakValue = 0;
+
+int foo() {
+       return weakValue;
+}
diff --git a/testing/kernel-cache-tests/kext-weak-bind/main.c b/testing/kernel-cache-tests/kext-weak-bind/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kext-weak-bind/main.kernel b/testing/kernel-cache-tests/kext-weak-bind/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/kext-weak-bind/main.kernel differ
diff --git a/testing/kernel-cache-tests/kext-weak-bind/test.py b/testing/kernel-cache-tests/kext-weak-bind/test.py
new file mode 100644 (file)
index 0000000..d8f1731
--- /dev/null
@@ -0,0 +1,37 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that weak binds point to the same symbol
+
+# FIXME: This should be re-enabled once we know how to handle classic relocs combined with split seg v2.
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/kext-weak-bind/main.kc", "/kext-weak-bind/main.kernel", "/kext-weak-bind/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/kext-weak-bind/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+
+    # Check the fixups
+    kernel_cache.analyze("/kext-weak-bind/main.kc", ["-fixups", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 2
+    assert kernel_cache.dictionary()["fixups"]["0x1C000"] == "kc(0) + 0x1C018"
+    assert kernel_cache.dictionary()["fixups"]["0x1C010"] == "kc(0) + 0x1C018"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kexts-missing-split-seg-error/bar.c b/testing/kernel-cache-tests/kexts-missing-split-seg-error/bar.c
new file mode 100644 (file)
index 0000000..2ad0d8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/bar.kext/bar b/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..02d2b6f
Binary files /dev/null and b/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/foo.kext/foo b/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..ad1d740
Binary files /dev/null and b/testing/kernel-cache-tests/kexts-missing-split-seg-error/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/kexts-missing-split-seg-error/foo.c b/testing/kernel-cache-tests/kexts-missing-split-seg-error/foo.c
new file mode 100644 (file)
index 0000000..5a6eec2
--- /dev/null
@@ -0,0 +1,4 @@
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/kexts-missing-split-seg-error/main.c b/testing/kernel-cache-tests/kexts-missing-split-seg-error/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/kexts-missing-split-seg-error/main.kernel b/testing/kernel-cache-tests/kexts-missing-split-seg-error/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/kexts-missing-split-seg-error/main.kernel differ
diff --git a/testing/kernel-cache-tests/kexts-missing-split-seg-error/test.py b/testing/kernel-cache-tests/kexts-missing-split-seg-error/test.py
new file mode 100644 (file)
index 0000000..6fcfdd2
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Check errors from canBePlacedInKernelCollection()
+# We use a lack of split seg here as arm64 requires it
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/kexts-missing-split-seg-error/main.kc", "/kexts-missing-split-seg-error/main.kernel", "/kexts-missing-split-seg-error/extensions", ["com.apple.foo", "com.apple.bar"], ["-json-errors"])
+    assert len(kernel_cache.dictionary()) == 2
+    # bar
+    assert kernel_cache.dictionary()[0]["id"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()[0]["errors"]) == 1
+    assert kernel_cache.dictionary()[0]["errors"][0] == "cannot be placed in kernel collection because: Missing split seg v2"
+    # foo
+    assert kernel_cache.dictionary()[1]["id"] == "com.apple.foo"
+    assert len(kernel_cache.dictionary()[1]["errors"]) == 1
+    assert kernel_cache.dictionary()[1]["errors"][0] == "cannot be placed in kernel collection because: Missing split seg v2"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const -mios-version-min=8.0 foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const -mios-version-min=8.0 bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kmod-info-errors/bar.c b/testing/kernel-cache-tests/kmod-info-errors/bar.c
new file mode 100644 (file)
index 0000000..81ad71b
--- /dev/null
@@ -0,0 +1,15 @@
+
+#include "kmod.h"
+
+int startKext() {
+       return 0;
+}
+int endKext() {
+       return 0;
+}
+
+KMOD_EXPLICIT_DECL(com.apple.bar, "1.0.0", startKext, endKext)
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/kmod-info-errors/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kmod-info-errors/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kmod-info-errors/extensions/bar.kext/bar b/testing/kernel-cache-tests/kmod-info-errors/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..513e0bf
Binary files /dev/null and b/testing/kernel-cache-tests/kmod-info-errors/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kmod-info-errors/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kmod-info-errors/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kmod-info-errors/extensions/foo.kext/foo b/testing/kernel-cache-tests/kmod-info-errors/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..06c3cc8
Binary files /dev/null and b/testing/kernel-cache-tests/kmod-info-errors/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/kmod-info-errors/foo.c b/testing/kernel-cache-tests/kmod-info-errors/foo.c
new file mode 100644 (file)
index 0000000..cddf937
--- /dev/null
@@ -0,0 +1,15 @@
+
+#include "kmod.h"
+
+int startKext() {
+       return 0;
+}
+int endKext() {
+       return 0;
+}
+
+KMOD_EXPLICIT_DECL(com.apple.foo, "1.0.0", startKext, endKext)
+
+int bar() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/kmod-info-errors/kmod.h b/testing/kernel-cache-tests/kmod-info-errors/kmod.h
new file mode 100644 (file)
index 0000000..b2bc0d3
--- /dev/null
@@ -0,0 +1,39 @@
+
+#include <stdint.h>
+
+typedef uint64_t  vm_address_t;
+typedef uint64_t  vm_size_t;
+
+// Taken from kmod.h
+#define KMOD_MAX_NAME 64
+
+#pragma pack(push, 4)
+typedef struct kmod_info {
+    struct kmod_info  * next;
+    int32_t             info_version;       // version of this structure
+    uint32_t            id;
+    char                name[KMOD_MAX_NAME];
+    char                version[KMOD_MAX_NAME];
+    int32_t             reference_count;    // # linkage refs to this
+    void  *       reference_list;     // who this refs (links on)
+    vm_address_t        address;            // starting address
+    vm_size_t           size;               // total size
+    vm_size_t           hdr_size;           // unwired hdr size
+    void *        start;
+    void *        stop;
+} kmod_info_t;
+#pragma pack(pop)
+
+#define KMOD_INFO_NAME       kmod_info
+#define KMOD_INFO_VERSION    2
+#define KMOD_DECL(name, version)                                  \
+    static kmod_start_func_t name ## _module_start;               \
+    static kmod_stop_func_t  name ## _module_stop;                \
+    kmod_info_t KMOD_INFO_NAME = { 0, KMOD_INFO_VERSION, -1U,      \
+                   { #name }, { version }, -1, 0, 0, 0, 0,    \
+                       name ## _module_start,                 \
+                       name ## _module_stop };
+#define KMOD_EXPLICIT_DECL(name, version, start, stop)            \
+    kmod_info_t KMOD_INFO_NAME = { 0, KMOD_INFO_VERSION, -1U,      \
+                   { #name }, { version }, -1, 0, 0, 0, 0,    \
+                       start, stop };
diff --git a/testing/kernel-cache-tests/kmod-info-errors/main.c b/testing/kernel-cache-tests/kmod-info-errors/main.c
new file mode 100644 (file)
index 0000000..27b70cb
--- /dev/null
@@ -0,0 +1,5 @@
+
+__attribute__((section(("__HIB, __text"))))
+int _start() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/kmod-info-errors/main.kernel b/testing/kernel-cache-tests/kmod-info-errors/main.kernel
new file mode 100755 (executable)
index 0000000..ab172f1
Binary files /dev/null and b/testing/kernel-cache-tests/kmod-info-errors/main.kernel differ
diff --git a/testing/kernel-cache-tests/kmod-info-errors/test.py b/testing/kernel-cache-tests/kmod-info-errors/test.py
new file mode 100755 (executable)
index 0000000..32767b4
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Error for bad kmod info
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/kmod-info-errors/main.kc", "/kmod-info-errors/main.kernel", "/kmod-info-errors/extensions", ["com.apple.foo", "com.apple.bar"], ["-json-errors"])
+
+    assert len(kernel_cache.dictionary()) == 2
+    # bar
+    assert kernel_cache.dictionary()[0]["id"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()[0]["errors"]) == 1
+    assert kernel_cache.dictionary()[0]["errors"][0] == "unsupported kmod_info version of 2"
+    # foo
+    assert kernel_cache.dictionary()[1]["id"] == "com.apple.foo"
+    assert len(kernel_cache.dictionary()[1]["errors"]) == 1
+    assert kernel_cache.dictionary()[1]["errors"][0] == "unsupported kmod_info version of 2"
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x200000 -Wl,-segaddr,__HIB,0x100000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kmod-info-local-symbols/bar.c b/testing/kernel-cache-tests/kmod-info-local-symbols/bar.c
new file mode 100644 (file)
index 0000000..81ad71b
--- /dev/null
@@ -0,0 +1,15 @@
+
+#include "kmod.h"
+
+int startKext() {
+       return 0;
+}
+int endKext() {
+       return 0;
+}
+
+KMOD_EXPLICIT_DECL(com.apple.bar, "1.0.0", startKext, endKext)
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/bar.kext/bar b/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..d8f704c
Binary files /dev/null and b/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/foo.kext/foo b/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..d809a0a
Binary files /dev/null and b/testing/kernel-cache-tests/kmod-info-local-symbols/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/kmod-info-local-symbols/foo.c b/testing/kernel-cache-tests/kmod-info-local-symbols/foo.c
new file mode 100644 (file)
index 0000000..cddf937
--- /dev/null
@@ -0,0 +1,15 @@
+
+#include "kmod.h"
+
+int startKext() {
+       return 0;
+}
+int endKext() {
+       return 0;
+}
+
+KMOD_EXPLICIT_DECL(com.apple.foo, "1.0.0", startKext, endKext)
+
+int bar() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/kmod-info-local-symbols/kmod.h b/testing/kernel-cache-tests/kmod-info-local-symbols/kmod.h
new file mode 100644 (file)
index 0000000..eb5f782
--- /dev/null
@@ -0,0 +1,39 @@
+
+#include <stdint.h>
+
+typedef uint64_t  vm_address_t;
+typedef uint64_t  vm_size_t;
+
+// Taken from kmod.h
+#define KMOD_MAX_NAME 64
+
+#pragma pack(push, 4)
+typedef struct kmod_info {
+    struct kmod_info  * next;
+    int32_t             info_version;       // version of this structure
+    uint32_t            id;
+    char                name[KMOD_MAX_NAME];
+    char                version[KMOD_MAX_NAME];
+    int32_t             reference_count;    // # linkage refs to this
+    void  *       reference_list;     // who this refs (links on)
+    vm_address_t        address;            // starting address
+    vm_size_t           size;               // total size
+    vm_size_t           hdr_size;           // unwired hdr size
+    void *        start;
+    void *        stop;
+} kmod_info_t;
+#pragma pack(pop)
+
+#define KMOD_INFO_NAME       kmod_info
+#define KMOD_INFO_VERSION    1
+#define KMOD_DECL(name, version)                                  \
+    static kmod_start_func_t name ## _module_start;               \
+    static kmod_stop_func_t  name ## _module_stop;                \
+    kmod_info_t KMOD_INFO_NAME = { 0, KMOD_INFO_VERSION, -1U,      \
+                   { #name }, { version }, -1, 0, 0, 0, 0,    \
+                       name ## _module_start,                 \
+                       name ## _module_stop };
+#define KMOD_EXPLICIT_DECL(name, version, start, stop)            \
+    kmod_info_t KMOD_INFO_NAME = { 0, KMOD_INFO_VERSION, -1U,      \
+                   { #name }, { version }, -1, 0, 0, 0, 0,    \
+                       start, stop };
diff --git a/testing/kernel-cache-tests/kmod-info-local-symbols/main.c b/testing/kernel-cache-tests/kmod-info-local-symbols/main.c
new file mode 100644 (file)
index 0000000..27b70cb
--- /dev/null
@@ -0,0 +1,5 @@
+
+__attribute__((section(("__HIB, __text"))))
+int _start() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/kmod-info-local-symbols/main.kernel b/testing/kernel-cache-tests/kmod-info-local-symbols/main.kernel
new file mode 100755 (executable)
index 0000000..5c6c23a
Binary files /dev/null and b/testing/kernel-cache-tests/kmod-info-local-symbols/main.kernel differ
diff --git a/testing/kernel-cache-tests/kmod-info-local-symbols/test.py b/testing/kernel-cache-tests/kmod-info-local-symbols/test.py
new file mode 100755 (executable)
index 0000000..2d76234
--- /dev/null
@@ -0,0 +1,55 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Update the kmod_info entry for the files here
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/kmod-info-local-symbols/main.kc", "/kmod-info-local-symbols/main.kernel", "/kmod-info-local-symbols/extensions", ["com.apple.foo", "com.apple.bar"], [])
+
+    # Check the layout
+    kernel_cache.analyze("/kmod-info-local-symbols/main.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+
+    # Find the address and size of each kext in the layout and check that these match their kmod info values
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0x205000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmSize"] == "0xFF8"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmAddr"] == "0x206000"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmSize"] == "0xFF8"
+
+    # Check the kmod info
+    kernel_cache.analyze("/kmod-info-local-symbols/main.kc", ["-kmod", "-arch", "x86_64"])
+    assert len(kernel_cache.dictionary()) == 3
+    assert kernel_cache.dictionary()[0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()[0]["kmod_info"] == "none"
+
+    # bar.kext
+    assert kernel_cache.dictionary()[1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()[1]["kmod_info"]["info-version"] == "1"
+    assert kernel_cache.dictionary()[1]["kmod_info"]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()[1]["kmod_info"]["version"] == "1.0.0"
+    assert kernel_cache.dictionary()[1]["kmod_info"]["address"] == "0x205000"
+    assert kernel_cache.dictionary()[1]["kmod_info"]["size"] == "0xFF8"
+
+    # foo.kext
+    assert kernel_cache.dictionary()[2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()[2]["kmod_info"]["info-version"] == "1"
+    assert kernel_cache.dictionary()[2]["kmod_info"]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()[2]["kmod_info"]["version"] == "1.0.0"
+    assert kernel_cache.dictionary()[2]["kmod_info"]["address"] == "0x206000"
+    assert kernel_cache.dictionary()[2]["kmod_info"]["size"] == "0xFF8"
+
+# [~]> xcrun -sdk macosx.internal cc -fvisibility=hidden -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x200000 -Wl,-segaddr,__HIB,0x100000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack
+# [~]> xcrun -sdk macosx.internal cc -fvisibility=hidden -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk macosx.internal cc -fvisibility=hidden -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/kmod-info/bar.c b/testing/kernel-cache-tests/kmod-info/bar.c
new file mode 100644 (file)
index 0000000..81ad71b
--- /dev/null
@@ -0,0 +1,15 @@
+
+#include "kmod.h"
+
+int startKext() {
+       return 0;
+}
+int endKext() {
+       return 0;
+}
+
+KMOD_EXPLICIT_DECL(com.apple.bar, "1.0.0", startKext, endKext)
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/kmod-info/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/kmod-info/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kmod-info/extensions/bar.kext/bar b/testing/kernel-cache-tests/kmod-info/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..69d6139
Binary files /dev/null and b/testing/kernel-cache-tests/kmod-info/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/kmod-info/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/kmod-info/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/kmod-info/extensions/foo.kext/foo b/testing/kernel-cache-tests/kmod-info/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..8a6ecfc
Binary files /dev/null and b/testing/kernel-cache-tests/kmod-info/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/kmod-info/foo.c b/testing/kernel-cache-tests/kmod-info/foo.c
new file mode 100644 (file)
index 0000000..cddf937
--- /dev/null
@@ -0,0 +1,15 @@
+
+#include "kmod.h"
+
+int startKext() {
+       return 0;
+}
+int endKext() {
+       return 0;
+}
+
+KMOD_EXPLICIT_DECL(com.apple.foo, "1.0.0", startKext, endKext)
+
+int bar() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/kmod-info/kmod.h b/testing/kernel-cache-tests/kmod-info/kmod.h
new file mode 100644 (file)
index 0000000..eb5f782
--- /dev/null
@@ -0,0 +1,39 @@
+
+#include <stdint.h>
+
+typedef uint64_t  vm_address_t;
+typedef uint64_t  vm_size_t;
+
+// Taken from kmod.h
+#define KMOD_MAX_NAME 64
+
+#pragma pack(push, 4)
+typedef struct kmod_info {
+    struct kmod_info  * next;
+    int32_t             info_version;       // version of this structure
+    uint32_t            id;
+    char                name[KMOD_MAX_NAME];
+    char                version[KMOD_MAX_NAME];
+    int32_t             reference_count;    // # linkage refs to this
+    void  *       reference_list;     // who this refs (links on)
+    vm_address_t        address;            // starting address
+    vm_size_t           size;               // total size
+    vm_size_t           hdr_size;           // unwired hdr size
+    void *        start;
+    void *        stop;
+} kmod_info_t;
+#pragma pack(pop)
+
+#define KMOD_INFO_NAME       kmod_info
+#define KMOD_INFO_VERSION    1
+#define KMOD_DECL(name, version)                                  \
+    static kmod_start_func_t name ## _module_start;               \
+    static kmod_stop_func_t  name ## _module_stop;                \
+    kmod_info_t KMOD_INFO_NAME = { 0, KMOD_INFO_VERSION, -1U,      \
+                   { #name }, { version }, -1, 0, 0, 0, 0,    \
+                       name ## _module_start,                 \
+                       name ## _module_stop };
+#define KMOD_EXPLICIT_DECL(name, version, start, stop)            \
+    kmod_info_t KMOD_INFO_NAME = { 0, KMOD_INFO_VERSION, -1U,      \
+                   { #name }, { version }, -1, 0, 0, 0, 0,    \
+                       start, stop };
diff --git a/testing/kernel-cache-tests/kmod-info/main.c b/testing/kernel-cache-tests/kmod-info/main.c
new file mode 100644 (file)
index 0000000..27b70cb
--- /dev/null
@@ -0,0 +1,5 @@
+
+__attribute__((section(("__HIB, __text"))))
+int _start() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/kmod-info/main.kernel b/testing/kernel-cache-tests/kmod-info/main.kernel
new file mode 100755 (executable)
index 0000000..ab172f1
Binary files /dev/null and b/testing/kernel-cache-tests/kmod-info/main.kernel differ
diff --git a/testing/kernel-cache-tests/kmod-info/test.py b/testing/kernel-cache-tests/kmod-info/test.py
new file mode 100755 (executable)
index 0000000..22d204d
--- /dev/null
@@ -0,0 +1,55 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Update the kmod_info entry for the files here
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/kmod-info/main.kc", "/kmod-info/main.kernel", "/kmod-info/extensions", ["com.apple.foo", "com.apple.bar"], [])
+
+    # Check the layout
+    kernel_cache.analyze("/kmod-info/main.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+
+    # Find the address and size of each kext in the layout and check that these match their kmod info values
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0x205000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmSize"] == "0xFF8"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmAddr"] == "0x206000"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmSize"] == "0xFF8"
+
+    # Check the kmod info
+    kernel_cache.analyze("/kmod-info/main.kc", ["-kmod", "-arch", "x86_64"])
+    assert len(kernel_cache.dictionary()) == 3
+    assert kernel_cache.dictionary()[0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()[0]["kmod_info"] == "none"
+
+    # bar.kext
+    assert kernel_cache.dictionary()[1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()[1]["kmod_info"]["info-version"] == "1"
+    assert kernel_cache.dictionary()[1]["kmod_info"]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()[1]["kmod_info"]["version"] == "1.0.0"
+    assert kernel_cache.dictionary()[1]["kmod_info"]["address"] == "0x205000"
+    assert kernel_cache.dictionary()[1]["kmod_info"]["size"] == "0xFF8"
+
+    # foo.kext
+    assert kernel_cache.dictionary()[2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()[2]["kmod_info"]["info-version"] == "1"
+    assert kernel_cache.dictionary()[2]["kmod_info"]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()[2]["kmod_info"]["version"] == "1.0.0"
+    assert kernel_cache.dictionary()[2]["kmod_info"]["address"] == "0x206000"
+    assert kernel_cache.dictionary()[2]["kmod_info"]["size"] == "0xFF8"
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x200000 -Wl,-segaddr,__HIB,0x100000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/large-auxkc-errors/bar.c b/testing/kernel-cache-tests/large-auxkc-errors/bar.c
new file mode 100644 (file)
index 0000000..afee5a2
--- /dev/null
@@ -0,0 +1,9 @@
+
+__attribute__((used))
+char largeBuffer[64 * 1024 * 1024];
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/large-auxkc-errors/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/large-auxkc-errors/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/large-auxkc-errors/extensions/bar.kext/bar b/testing/kernel-cache-tests/large-auxkc-errors/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..a3f42db
Binary files /dev/null and b/testing/kernel-cache-tests/large-auxkc-errors/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/large-auxkc-errors/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/large-auxkc-errors/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/large-auxkc-errors/extensions/foo.kext/foo b/testing/kernel-cache-tests/large-auxkc-errors/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..f0115a4
Binary files /dev/null and b/testing/kernel-cache-tests/large-auxkc-errors/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/large-auxkc-errors/foo.c b/testing/kernel-cache-tests/large-auxkc-errors/foo.c
new file mode 100644 (file)
index 0000000..dd18f9a
--- /dev/null
@@ -0,0 +1,7 @@
+
+__attribute__((used))
+char largeBuffer[32 * 1024 * 1024];
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/large-auxkc-errors/main.c b/testing/kernel-cache-tests/large-auxkc-errors/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/large-auxkc-errors/main.kernel b/testing/kernel-cache-tests/large-auxkc-errors/main.kernel
new file mode 100755 (executable)
index 0000000..a46bfb5
Binary files /dev/null and b/testing/kernel-cache-tests/large-auxkc-errors/main.kernel differ
diff --git a/testing/kernel-cache-tests/large-auxkc-errors/test.py b/testing/kernel-cache-tests/large-auxkc-errors/test.py
new file mode 100644 (file)
index 0000000..fbbcef5
--- /dev/null
@@ -0,0 +1,24 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# The arm64e auxKC has a lower memory limit than other KCs.  Verify that we get an error with only 64MB in there.
+
+def check(kernel_cache):
+    # First build a kernel collection
+    kernel_cache.buildKernelCollection("arm64e", "/large-auxkc-errors/main.kc", "/large-auxkc-errors/main.kernel", "/large-auxkc-errors/extensions", [], [])
+    kernel_cache.analyze("/large-auxkc-errors/main.kc", ["-layout", "-arch", "arm64e"])
+
+    # Now build an aux cache using the baseline kernel collection
+    kernel_cache.buildAuxKernelCollection("arm64e", "/large-auxkc-errors/aux.kc", "/large-auxkc-errors/main.kc", "", "/large-auxkc-errors/extensions", ["com.apple.foo", "com.apple.bar"], ["-json-errors"])
+
+    assert len(kernel_cache.dictionary()) == 1
+    assert "kernel collection size exceeds maximum size of 67108864" in kernel_cache.dictionary()[0]
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64e -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/large-auxkc-no-errors/bar.c b/testing/kernel-cache-tests/large-auxkc-no-errors/bar.c
new file mode 100644 (file)
index 0000000..64c7316
--- /dev/null
@@ -0,0 +1,9 @@
+
+__attribute__((used))
+char largeBuffer[512 * 1024 * 1024];
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/bar.kext/bar b/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..a52f0f5
Binary files /dev/null and b/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/foo.kext/foo b/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..f557435
Binary files /dev/null and b/testing/kernel-cache-tests/large-auxkc-no-errors/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/large-auxkc-no-errors/foo.c b/testing/kernel-cache-tests/large-auxkc-no-errors/foo.c
new file mode 100644 (file)
index 0000000..607bfa0
--- /dev/null
@@ -0,0 +1,7 @@
+
+__attribute__((used))
+char largeBuffer[400 * 1024 * 1024];
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/large-auxkc-no-errors/main.c b/testing/kernel-cache-tests/large-auxkc-no-errors/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/large-auxkc-no-errors/main.kernel b/testing/kernel-cache-tests/large-auxkc-no-errors/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/large-auxkc-no-errors/main.kernel differ
diff --git a/testing/kernel-cache-tests/large-auxkc-no-errors/test.py b/testing/kernel-cache-tests/large-auxkc-no-errors/test.py
new file mode 100644 (file)
index 0000000..acca4be
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# The non-auxKC can still be 1GB.
+
+def check(kernel_cache):
+    # First build a kernel collection
+    kernel_cache.buildKernelCollection("arm64", "/large-auxkc-no-errors/main.kc", "/large-auxkc-no-errors/main.kernel", "/large-auxkc-no-errors/extensions", [], [])
+    kernel_cache.analyze("/large-auxkc-no-errors/main.kc", ["-layout", "-arch", "arm64"])
+
+    # Now build an aux cache using the baseline kernel collection
+    kernel_cache.buildAuxKernelCollection("arm64", "/large-auxkc-no-errors/aux.kc", "/large-auxkc-no-errors/main.kc", "", "/large-auxkc-no-errors/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/large-auxkc-no-errors/main.kc", ["-layout", "-arch", "arm64"])
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/large-kcs-errors/bar.c b/testing/kernel-cache-tests/large-kcs-errors/bar.c
new file mode 100644 (file)
index 0000000..93f5249
--- /dev/null
@@ -0,0 +1,7 @@
+
+__attribute__((used))
+char largeBuffer[512 * 1024 * 1024];
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/large-kcs-errors/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/large-kcs-errors/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/large-kcs-errors/extensions/bar.kext/bar b/testing/kernel-cache-tests/large-kcs-errors/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..9e23135
Binary files /dev/null and b/testing/kernel-cache-tests/large-kcs-errors/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/large-kcs-errors/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/large-kcs-errors/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/large-kcs-errors/extensions/foo.kext/foo b/testing/kernel-cache-tests/large-kcs-errors/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..3d568d9
Binary files /dev/null and b/testing/kernel-cache-tests/large-kcs-errors/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/large-kcs-errors/foo.c b/testing/kernel-cache-tests/large-kcs-errors/foo.c
new file mode 100644 (file)
index 0000000..6428460
--- /dev/null
@@ -0,0 +1,7 @@
+
+__attribute__((used))
+char largeBuffer[512 * 1024 * 1024];
+
+int bar() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/large-kcs-errors/main.c b/testing/kernel-cache-tests/large-kcs-errors/main.c
new file mode 100644 (file)
index 0000000..27b70cb
--- /dev/null
@@ -0,0 +1,5 @@
+
+__attribute__((section(("__HIB, __text"))))
+int _start() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/large-kcs-errors/main.kernel b/testing/kernel-cache-tests/large-kcs-errors/main.kernel
new file mode 100755 (executable)
index 0000000..ab172f1
Binary files /dev/null and b/testing/kernel-cache-tests/large-kcs-errors/main.kernel differ
diff --git a/testing/kernel-cache-tests/large-kcs-errors/test.py b/testing/kernel-cache-tests/large-kcs-errors/test.py
new file mode 100755 (executable)
index 0000000..6c0109a
--- /dev/null
@@ -0,0 +1,17 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Error for bad kmod info
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/large-kcs-errors/main.kc", "/large-kcs-errors/main.kernel", "/large-kcs-errors/extensions", ["com.apple.foo", "com.apple.bar"], ["-json-errors"])
+
+    assert len(kernel_cache.dictionary()) == 1
+    assert "kernel collection size exceeds maximum size of 1073741824" in kernel_cache.dictionary()[0]
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.c -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x200000 -Wl,-segaddr,__HIB,0x100000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+
diff --git a/testing/kernel-cache-tests/last-data-const/bar.c b/testing/kernel-cache-tests/last-data-const/bar.c
new file mode 100644 (file)
index 0000000..3f90848
--- /dev/null
@@ -0,0 +1,8 @@
+
+int pack = 0;
+
+extern int foo();
+
+int bar() {
+       return foo() + pack;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/last-data-const/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/last-data-const/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/last-data-const/extensions/bar.kext/bar b/testing/kernel-cache-tests/last-data-const/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..5d8a757
Binary files /dev/null and b/testing/kernel-cache-tests/last-data-const/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/last-data-const/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/last-data-const/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/last-data-const/extensions/foo.kext/foo b/testing/kernel-cache-tests/last-data-const/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..af1710e
Binary files /dev/null and b/testing/kernel-cache-tests/last-data-const/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/last-data-const/foo.c b/testing/kernel-cache-tests/last-data-const/foo.c
new file mode 100644 (file)
index 0000000..acd9563
--- /dev/null
@@ -0,0 +1,6 @@
+
+int pack;
+
+int foo() {
+       return pack;
+}
diff --git a/testing/kernel-cache-tests/last-data-const/main.c b/testing/kernel-cache-tests/last-data-const/main.c
new file mode 100644 (file)
index 0000000..5bdda59
--- /dev/null
@@ -0,0 +1,7 @@
+
+int x = 0;
+int* g = &x;
+
+int _start() {
+       return *g;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/last-data-const/main.kernel b/testing/kernel-cache-tests/last-data-const/main.kernel
new file mode 100755 (executable)
index 0000000..f4ca636
Binary files /dev/null and b/testing/kernel-cache-tests/last-data-const/main.kernel differ
diff --git a/testing/kernel-cache-tests/last-data-const/test.py b/testing/kernel-cache-tests/last-data-const/test.py
new file mode 100644 (file)
index 0000000..9e54ad0
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Last data const should be folded in under data const and allowed to have fixups
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/last-data-const/main.kc", "/last-data-const/main.kernel", "/last-data-const/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/last-data-const/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__DATA_CONST"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0x18000"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmSize"] == "0x8000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LASTDATA_CONST"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0x18000"
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+
+    # Check we have the correct addresses of the symbols being bound to
+    kernel_cache.analyze("/last-data-const/main.kc", ["-symbols", "-arch", "arm64"])
+    # main.kernel
+    # int g = &x;
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["name"] == "_x"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["vmAddr"] == "0x24000"
+    # foo.kext
+    # foo()
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["name"] == "_foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["vmAddr"] == "0x14030"
+
+    kernel_cache.analyze("/last-data-const/main.kc", ["-fixups", "-arch", "arm64"])
+    # main.kernel
+    # int g = &x;
+    assert kernel_cache.dictionary()["fixups"]["0x18000"] == "kc(0) + 0x24000"
+    # foo.kext
+    # foo()
+    assert kernel_cache.dictionary()["fixups"]["0x1C000"] == "kc(0) + 0x14030"
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie -Wl,-rename_section,__DATA,__data,__LASTDATA_CONST,__data -Wl,-segprot,__LASTDATA_CONST,r--,r-- main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/packed-kext-data/bar.c b/testing/kernel-cache-tests/packed-kext-data/bar.c
new file mode 100644 (file)
index 0000000..3f90848
--- /dev/null
@@ -0,0 +1,8 @@
+
+int pack = 0;
+
+extern int foo();
+
+int bar() {
+       return foo() + pack;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/packed-kext-data/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/packed-kext-data/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/packed-kext-data/extensions/bar.kext/bar b/testing/kernel-cache-tests/packed-kext-data/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..5d8a757
Binary files /dev/null and b/testing/kernel-cache-tests/packed-kext-data/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/packed-kext-data/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/packed-kext-data/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/packed-kext-data/extensions/foo.kext/foo b/testing/kernel-cache-tests/packed-kext-data/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..af1710e
Binary files /dev/null and b/testing/kernel-cache-tests/packed-kext-data/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/packed-kext-data/foo.c b/testing/kernel-cache-tests/packed-kext-data/foo.c
new file mode 100644 (file)
index 0000000..acd9563
--- /dev/null
@@ -0,0 +1,6 @@
+
+int pack;
+
+int foo() {
+       return pack;
+}
diff --git a/testing/kernel-cache-tests/packed-kext-data/main.c b/testing/kernel-cache-tests/packed-kext-data/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/packed-kext-data/main.kernel b/testing/kernel-cache-tests/packed-kext-data/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/packed-kext-data/main.kernel differ
diff --git a/testing/kernel-cache-tests/packed-kext-data/test.py b/testing/kernel-cache-tests/packed-kext-data/test.py
new file mode 100644 (file)
index 0000000..8f763af
--- /dev/null
@@ -0,0 +1,36 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# __DATA should be packed at sub-page alignment
+# But only in the kexts.  The kernel pages should not be packed
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/packed-kext-data/main.kc", "/packed-kext-data/main.kernel", "/packed-kext-data/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/packed-kext-data/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["cache-segments"]) == 7
+    assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0x20000"
+    assert kernel_cache.dictionary()["cache-segments"][5]["vmSize"] == "0x4000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x20000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmSize"] == "0x4"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert len(kernel_cache.dictionary()["dylibs"][2]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["vmAddr"] == "0x20004"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["vmSize"] == "0x4"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/packed-kext-text/bar.c b/testing/kernel-cache-tests/packed-kext-text/bar.c
new file mode 100644 (file)
index 0000000..f9cdb9b
--- /dev/null
@@ -0,0 +1,10 @@
+
+// Hack to force a section on __TEXT as otherwise its given the vmSize by forEachSegment
+__attribute__((used, section("__TEXT, __const")))
+int packHack = 0;
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/packed-kext-text/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/packed-kext-text/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/packed-kext-text/extensions/bar.kext/bar b/testing/kernel-cache-tests/packed-kext-text/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..3e06336
Binary files /dev/null and b/testing/kernel-cache-tests/packed-kext-text/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/packed-kext-text/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/packed-kext-text/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/packed-kext-text/extensions/foo.kext/foo b/testing/kernel-cache-tests/packed-kext-text/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..0a125c3
Binary files /dev/null and b/testing/kernel-cache-tests/packed-kext-text/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/packed-kext-text/foo.c b/testing/kernel-cache-tests/packed-kext-text/foo.c
new file mode 100644 (file)
index 0000000..1911bd9
--- /dev/null
@@ -0,0 +1,8 @@
+
+// Hack to force a section on __TEXT as otherwise its given the vmSize by forEachSegment
+__attribute__((used, section("__TEXT, __const")))
+int packHack = 0;
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/packed-kext-text/main.c b/testing/kernel-cache-tests/packed-kext-text/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/packed-kext-text/main.kernel b/testing/kernel-cache-tests/packed-kext-text/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/packed-kext-text/main.kernel differ
diff --git a/testing/kernel-cache-tests/packed-kext-text/test.py b/testing/kernel-cache-tests/packed-kext-text/test.py
new file mode 100644 (file)
index 0000000..22672a3
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# __DATA should be packed at sub-page alignment
+# But only in the kexts.  The kernel pages should not be packed
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/packed-kext-text/main.kc", "/packed-kext-text/main.kernel", "/packed-kext-text/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/packed-kext-text/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0x8000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmSize"] == "0xC000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0xC000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmSize"] == "0x4000"
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0x10000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmSize"] == "0x20"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["vmAddr"] == "0x10020"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["vmSize"] == "0xC"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/pageablekc-bind-to-basekc/bar.c b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/bar.c
new file mode 100644 (file)
index 0000000..2ad0d8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/bar.kext/bar b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..7eafc00
Binary files /dev/null and b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/foo.kext/foo b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..cd1fa31
Binary files /dev/null and b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/pageablekc-bind-to-basekc/foo.c b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/foo.c
new file mode 100644 (file)
index 0000000..5a6eec2
--- /dev/null
@@ -0,0 +1,4 @@
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/pageablekc-bind-to-basekc/main.c b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/pageablekc-bind-to-basekc/main.kernel b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/main.kernel differ
diff --git a/testing/kernel-cache-tests/pageablekc-bind-to-basekc/test.py b/testing/kernel-cache-tests/pageablekc-bind-to-basekc/test.py
new file mode 100644 (file)
index 0000000..baf91e5
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that a kext can bind to another kext
+# foo.kext exports foo and bar.kext uses it
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/pageablekc-bind-to-basekc/main.kc", "/pageablekc-bind-to-basekc/main.kernel", "/pageablekc-bind-to-basekc/extensions", ["com.apple.foo"], [])
+    kernel_cache.analyze("/pageablekc-bind-to-basekc/main.kc", ["-symbols", "-arch", "arm64"])
+
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][0]["name"] == "_foo"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][0]["vmAddr"] == "0x10000"
+
+    # Now build an pageable cache using the baseline kernel collection
+    kernel_cache.buildPageableKernelCollection("arm64", "/pageablekc-bind-to-basekc/pageable.kc", "/pageablekc-bind-to-basekc/main.kc", "/pageablekc-bind-to-basekc/extensions", ["com.apple.bar"], [])
+    kernel_cache.analyze("/pageablekc-bind-to-basekc/pageable.kc", ["-fixups", "-arch", "arm64"])
+
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["fixups"]) == 1
+    # extern int foo()
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0xC000"] == "kc(0) + 0x10000"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/pageablekc-uuid/bar.c b/testing/kernel-cache-tests/pageablekc-uuid/bar.c
new file mode 100644 (file)
index 0000000..2ad0d8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/pageablekc-uuid/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/pageablekc-uuid/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/pageablekc-uuid/extensions/bar.kext/bar b/testing/kernel-cache-tests/pageablekc-uuid/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..6288d91
Binary files /dev/null and b/testing/kernel-cache-tests/pageablekc-uuid/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/pageablekc-uuid/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/pageablekc-uuid/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/pageablekc-uuid/extensions/foo.kext/foo b/testing/kernel-cache-tests/pageablekc-uuid/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..cd1fa31
Binary files /dev/null and b/testing/kernel-cache-tests/pageablekc-uuid/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/pageablekc-uuid/foo.c b/testing/kernel-cache-tests/pageablekc-uuid/foo.c
new file mode 100644 (file)
index 0000000..5a6eec2
--- /dev/null
@@ -0,0 +1,4 @@
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/pageablekc-uuid/main.c b/testing/kernel-cache-tests/pageablekc-uuid/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/pageablekc-uuid/main.kernel b/testing/kernel-cache-tests/pageablekc-uuid/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/pageablekc-uuid/main.kernel differ
diff --git a/testing/kernel-cache-tests/pageablekc-uuid/test.py b/testing/kernel-cache-tests/pageablekc-uuid/test.py
new file mode 100644 (file)
index 0000000..8322d15
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Verify that an pageableKC references the UUID of the base KC
+
+def check(kernel_cache):
+    # First build a kernel collection
+    kernel_cache.buildKernelCollection("arm64", "/pageablekc-uuid/main.kc", "/pageablekc-uuid/main.kernel", "/pageablekc-uuid/extensions", [], [])
+    kernel_cache.analyze("/pageablekc-uuid/main.kc", ["-layout", "-arch", "arm64"])
+
+    # Check the kernel UUID
+    kernel_cache.analyze("/pageablekc-uuid/main.kc", ["-uuid", "-arch", "arm64"])
+    kernelUUID = kernel_cache.dictionary()["uuid"]
+    assert kernelUUID != "00000000-0000-0000-0000-000000000000"
+    assert kernelUUID == kernel_cache.dictionary()["prelink-info-uuid"]
+
+    # Now build an pageable cache using the baseline kernel collection
+    kernel_cache.buildPageableKernelCollection("arm64", "/pageablekc-uuid/pageable.kc", "/pageablekc-uuid/main.kc", "/pageablekc-uuid/extensions", ["com.apple.foo", "com.apple.bar"], [])
+
+    # Check the pageable UUID
+    kernel_cache.analyze("/pageablekc-uuid/pageable.kc", ["-uuid", "-arch", "arm64"])
+    assert kernel_cache.dictionary()["uuid"] != "00000000-0000-0000-0000-000000000000"
+    assert kernel_cache.dictionary()["prelink-info-base-uuid"] == kernelUUID
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/SymbolSets.plist b/testing/kernel-cache-tests/pageablekc-vtable-patching/SymbolSets.plist
new file mode 100644 (file)
index 0000000..57b9a95
--- /dev/null
@@ -0,0 +1,48 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.symbols</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK11OSMetaClass</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK15OSMetaClassBase</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZN8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZNK8OSObject</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>__ZTV8OSObject</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/bar.cpp b/testing/kernel-cache-tests/pageablekc-vtable-patching/bar.cpp
new file mode 100644 (file)
index 0000000..a87b825
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include "foo.h"
+
+class Bar : public Foo
+{
+    OSDeclareDefaultStructors( Bar )
+    
+public:
+    virtual int foo();
+};
+
+OSDefineMetaClassAndStructors( Bar, Foo )
+
+int Bar::foo() {
+       return 1;
+}
+
+int bar() {
+       Bar* bar = new Bar();
+       return bar->foo();
+}
diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..911622b
--- /dev/null
@@ -0,0 +1,21 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/bar.kext/bar b/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..0bdcc52
Binary files /dev/null and b/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..237c06f
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/foo.kext/foo b/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..5fe8196
Binary files /dev/null and b/testing/kernel-cache-tests/pageablekc-vtable-patching/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/foo.cpp b/testing/kernel-cache-tests/pageablekc-vtable-patching/foo.cpp
new file mode 100644 (file)
index 0000000..83e5e67
--- /dev/null
@@ -0,0 +1,23 @@
+
+#include "foo.h"
+
+OSDefineMetaClassAndStructors( Foo, OSObject )
+
+// Index 0 has been replaced with a method
+OSMetaClassDefineReservedUsed(Foo, 0)
+OSMetaClassDefineReservedUnused( Foo, 1 )
+OSMetaClassDefineReservedUnused( Foo, 2 )
+OSMetaClassDefineReservedUnused( Foo, 3 )
+
+int Foo::foo() {
+       return 0;
+}
+
+int Foo::fooUsed0() {
+       return 0;
+}
+
+int foo() {
+       Foo* foo = new Foo();
+       return foo->foo() + foo->fooUsed0();
+}
diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/foo.h b/testing/kernel-cache-tests/pageablekc-vtable-patching/foo.h
new file mode 100644 (file)
index 0000000..31d6216
--- /dev/null
@@ -0,0 +1,21 @@
+
+#include <Kernel/libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+class Foo : public OSObject
+{
+    OSDeclareDefaultStructors( Foo )
+    
+public:
+    virtual int foo();
+    
+#ifdef FOO_USED
+    OSMetaClassDeclareReservedUsed(Foo, 0);
+    virtual int fooUsed0();
+#else
+    OSMetaClassDeclareReservedUnused(Foo, 0);
+#endif
+    OSMetaClassDeclareReservedUnused(Foo, 1);
+    OSMetaClassDeclareReservedUnused(Foo, 2);
+    OSMetaClassDeclareReservedUnused(Foo, 3);
+};
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/main.cpp b/testing/kernel-cache-tests/pageablekc-vtable-patching/main.cpp
new file mode 100644 (file)
index 0000000..b689f8a
--- /dev/null
@@ -0,0 +1,119 @@
+
+#include <libkern/c++/OSMetaClass.h>
+#include <Kernel/libkern/c++/OSObject.h>
+
+int __cxa_pure_virtual = 0;
+
+#if 0
+class OSObject : public OSMetaClassBase
+{
+    OSDeclareAbstractStructors(OSObject);
+
+public:
+       OSMetaClassDeclareReservedUnused(OSObject, 0);
+    OSMetaClassDeclareReservedUnused(OSObject, 1);
+    OSMetaClassDeclareReservedUnused(OSObject, 2);
+    OSMetaClassDeclareReservedUnused(OSObject, 3);
+    OSMetaClassDeclareReservedUnused(OSObject, 4);
+    OSMetaClassDeclareReservedUnused(OSObject, 5);
+    OSMetaClassDeclareReservedUnused(OSObject, 6);
+    OSMetaClassDeclareReservedUnused(OSObject, 7);
+    OSMetaClassDeclareReservedUnused(OSObject, 8);
+    OSMetaClassDeclareReservedUnused(OSObject, 9);
+    OSMetaClassDeclareReservedUnused(OSObject, 10);
+    OSMetaClassDeclareReservedUnused(OSObject, 11);
+    OSMetaClassDeclareReservedUnused(OSObject, 12);
+    OSMetaClassDeclareReservedUnused(OSObject, 13);
+    OSMetaClassDeclareReservedUnused(OSObject, 14);
+    OSMetaClassDeclareReservedUnused(OSObject, 15);
+};
+#endif
+
+// OSDefineMetaClassAndAbstractStructors(OSObject, 0);
+/* Class global data */
+OSObject::MetaClass OSObject::gMetaClass;
+const OSMetaClass * const OSObject::metaClass = &OSObject::gMetaClass;
+const OSMetaClass * const OSObject::superClass = NULL;
+
+// Virtual Padding
+OSMetaClassDefineReservedUnused(OSObject, 0);
+OSMetaClassDefineReservedUnused(OSObject, 1);
+OSMetaClassDefineReservedUnused(OSObject, 2);
+OSMetaClassDefineReservedUnused(OSObject, 3);
+OSMetaClassDefineReservedUnused(OSObject, 4);
+OSMetaClassDefineReservedUnused(OSObject, 5);
+OSMetaClassDefineReservedUnused(OSObject, 6);
+OSMetaClassDefineReservedUnused(OSObject, 7);
+OSMetaClassDefineReservedUnused(OSObject, 8);
+OSMetaClassDefineReservedUnused(OSObject, 9);
+OSMetaClassDefineReservedUnused(OSObject, 10);
+OSMetaClassDefineReservedUnused(OSObject, 11);
+OSMetaClassDefineReservedUnused(OSObject, 12);
+OSMetaClassDefineReservedUnused(OSObject, 13);
+OSMetaClassDefineReservedUnused(OSObject, 14);
+OSMetaClassDefineReservedUnused(OSObject, 15);
+
+// struct IORPC { };
+
+// OSMetaClassBase
+OSMetaClassBase::OSMetaClassBase() { }
+OSMetaClassBase::~OSMetaClassBase() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { }
+void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { }
+kern_return_t OSMetaClassBase::Dispatch(const IORPC rpc) { return 0; }
+bool OSMetaClassBase::isEqualTo(const OSMetaClassBase*) const { return false; }
+
+// OSMetaClass
+OSMetaClass::OSMetaClass(const char* inClassName, const OSMetaClass* inSuperClass, unsigned int inClassSize) { }
+OSMetaClass::~OSMetaClass() { }
+void OSMetaClass::reservedCalled(int ind) const { }
+void OSMetaClass::retain() const { }
+void OSMetaClass::release() const { }
+void OSMetaClass::release(int freeWhen) const { }
+int OSMetaClass::getRetainCount() const { return 0; }
+const OSMetaClass* OSMetaClass::getMetaClass() const { return NULL; }
+void OSMetaClass::taggedRetain(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag) const  { }
+void OSMetaClass::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSMetaClass::serialize(OSSerialize * serializer) const { return false; }
+void OSMetaClass::instanceConstructed() const { }
+
+// OSMetaClass reserved methods
+void OSMetaClass::_RESERVEDOSMetaClass0() { }
+void OSMetaClass::_RESERVEDOSMetaClass1() { }
+void OSMetaClass::_RESERVEDOSMetaClass2() { }
+void OSMetaClass::_RESERVEDOSMetaClass3() { }
+void OSMetaClass::_RESERVEDOSMetaClass4() { }
+void OSMetaClass::_RESERVEDOSMetaClass5() { }
+void OSMetaClass::_RESERVEDOSMetaClass6() { }
+void OSMetaClass::_RESERVEDOSMetaClass7() { }
+
+// OSObject::MetaClass
+OSObject::MetaClass::MetaClass() : OSMetaClass("OSObject", OSObject::superClass, sizeof(OSObject)) { }
+OSObject *OSObject::MetaClass::alloc() const { return NULL; }
+kern_return_t OSObject::MetaClass::Dispatch(const IORPC rpc) { return 0; }
+
+// OSObject
+OSObject::OSObject(OSMetaClass const*) { }
+OSObject::~OSObject() { }
+const OSMetaClass* OSObject::getMetaClass() const { return NULL; }
+void OSObject::free() { }
+bool OSObject::init() { return false; }
+void OSObject::retain() const { }
+void OSObject::release() const { }
+void OSObject::release(int freeWhen) const { }
+int OSObject::getRetainCount() const { return 0; }
+void OSObject::taggedRetain(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag) const  { }
+void OSObject::taggedRelease(const void * tag, const int freeWhen) const { }
+bool OSObject::serialize(OSSerialize * serializer) const { return false; }
+kern_return_t OSObject::Dispatch(const IORPC rpc) { return 0; }
+void* OSObject::operator new(unsigned long) { return (void*)1; }
+void OSObject::operator delete(void*, unsigned long) { return; }
+
+__attribute__((section(("__HIB, __text"))))
+extern "C" int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/main.kernel b/testing/kernel-cache-tests/pageablekc-vtable-patching/main.kernel
new file mode 100755 (executable)
index 0000000..119060f
Binary files /dev/null and b/testing/kernel-cache-tests/pageablekc-vtable-patching/main.kernel differ
diff --git a/testing/kernel-cache-tests/pageablekc-vtable-patching/test.py b/testing/kernel-cache-tests/pageablekc-vtable-patching/test.py
new file mode 100644 (file)
index 0000000..bcf847d
--- /dev/null
@@ -0,0 +1,69 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that a kext can patch vtables against another kext
+# We put foo.kext in the base KC so that the patch slot in bar.kext has to know to use the correct fixup level in the fixup chain
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/pageablekc-vtable-patching/main.kc", "/pageablekc-vtable-patching/main.kernel", "/pageablekc-vtable-patching/extensions", ["com.apple.foo"], [])
+    kernel_cache.analyze("/pageablekc-vtable-patching/main.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/pageablekc-vtable-patching/main.kc", ["-symbols", "-arch", "x86_64"])
+    
+    # From foo, we want to know where the vtable is, and the foo() and fooUsed0() slots in that vtable
+    # Foo::foo()
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][6]["name"] == "__ZN3Foo3fooEv"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][6]["vmAddr"] == "0x16ED0"
+    # Foo::fooUsed0()
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][7]["name"] == "__ZN3Foo8fooUsed0Ev"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][7]["vmAddr"] == "0x16EF0"
+
+
+    # Check the fixups
+    kernel_cache.analyze("/pageablekc-vtable-patching/main.kc", ["-fixups", "-arch", "x86_64"])
+    
+    # In vtable for Foo, we match the entry for Foo::foo() by looking for its value on the RHS of the fixup
+    assert kernel_cache.dictionary()["fixups"]["0x1D150"] == "kc(0) + 0x16ED0"
+    # Then the following fixup should be to Foo::fooUsed0()
+    assert kernel_cache.dictionary()["fixups"]["0x1D158"] == "kc(0) + 0x16EF0"
+
+
+    # -----------------------------------------------------------
+    # Now build an pageable cache using the baseline kernel collection
+    kernel_cache.buildPageableKernelCollection("x86_64", "/pageablekc-vtable-patching/pageable.kc", "/pageablekc-vtable-patching/main.kc", "/pageablekc-vtable-patching/extensions", ["com.apple.bar"], [])
+    kernel_cache.analyze("/pageablekc-vtable-patching/pageable.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar"
+
+
+    # Get the addresses for the symbols we are looking at.  This will make it easier to work out the fixup slots
+    kernel_cache.analyze("/pageablekc-vtable-patching/pageable.kc", ["-symbols", "-arch", "x86_64"])
+    
+    # From bar, find the vtable and its override of foo()
+    # Bar::foo()
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["name"] == "__ZN3Bar3fooEv"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["vmAddr"] == "0x4F10"
+
+
+    # Check the fixups
+    kernel_cache.analyze("/pageablekc-vtable-patching/pageable.kc", ["-fixups", "-arch", "x86_64"])
+
+    # In bar, again match the entry for its Bar::foo() symbol
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x8150"] == "kc(1) + 0x4F10"
+    # And if the patching was correct, then following entry should be to Foo::fooUsed0()
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x8158"] == "kc(0) + 0x12EF0"
+
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0x10000 -Wl,-segaddr,__HIB,0x4000 -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r--
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-no_data_const foo.cpp -o extensions/foo.kext/foo -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers -DFOO_USED=1
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-data_const bar.cpp -o extensions/bar.kext/bar -iwithsysroot /System/Library/Frameworks/Kernel.framework/Headers
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/plugins/bar.c b/testing/kernel-cache-tests/plugins/bar.c
new file mode 100644 (file)
index 0000000..691611e
--- /dev/null
@@ -0,0 +1,12 @@
+
+int g = 0;
+
+int bar() {
+       return g;
+}
+
+__typeof(&bar) barPtr = &bar;
+
+int baz() {
+       return barPtr();
+}
diff --git a/testing/kernel-cache-tests/plugins/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/plugins/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/plugins/extensions/foo.kext/PlugIns/bar.kext/Info.plist b/testing/kernel-cache-tests/plugins/extensions/foo.kext/PlugIns/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..77b3de8
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/plugins/extensions/foo.kext/PlugIns/bar.kext/bar b/testing/kernel-cache-tests/plugins/extensions/foo.kext/PlugIns/bar.kext/bar
new file mode 100755 (executable)
index 0000000..4467cee
Binary files /dev/null and b/testing/kernel-cache-tests/plugins/extensions/foo.kext/PlugIns/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/plugins/extensions/foo.kext/foo b/testing/kernel-cache-tests/plugins/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..1e2e3c3
Binary files /dev/null and b/testing/kernel-cache-tests/plugins/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/plugins/foo.c b/testing/kernel-cache-tests/plugins/foo.c
new file mode 100644 (file)
index 0000000..43ab00e
--- /dev/null
@@ -0,0 +1,7 @@
+
+int g = 0;
+int* gPtr = &g;
+
+int foo() {
+       return *gPtr;
+}
diff --git a/testing/kernel-cache-tests/plugins/main.c b/testing/kernel-cache-tests/plugins/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/plugins/main.kernel b/testing/kernel-cache-tests/plugins/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/plugins/main.kernel differ
diff --git a/testing/kernel-cache-tests/plugins/test.py b/testing/kernel-cache-tests/plugins/test.py
new file mode 100644 (file)
index 0000000..1d788bd
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that we look in the foo.kext Plugins subdirectory for more kexts
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/plugins/main.kc", "/plugins/main.kernel", "/plugins/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/plugins/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/foo.kext/PlugIns/bar.kext/bar
+# [~]> rm -r extensions/foo.kext/*.ld
+# [~]> rm -r extensions/foo.kext/PlugIns/bar.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/ppl-data-const/bar.c b/testing/kernel-cache-tests/ppl-data-const/bar.c
new file mode 100644 (file)
index 0000000..3f90848
--- /dev/null
@@ -0,0 +1,8 @@
+
+int pack = 0;
+
+extern int foo();
+
+int bar() {
+       return foo() + pack;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/ppl-data-const/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/ppl-data-const/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/ppl-data-const/extensions/bar.kext/bar b/testing/kernel-cache-tests/ppl-data-const/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..5d8a757
Binary files /dev/null and b/testing/kernel-cache-tests/ppl-data-const/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/ppl-data-const/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/ppl-data-const/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/ppl-data-const/extensions/foo.kext/foo b/testing/kernel-cache-tests/ppl-data-const/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..af1710e
Binary files /dev/null and b/testing/kernel-cache-tests/ppl-data-const/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/ppl-data-const/foo.c b/testing/kernel-cache-tests/ppl-data-const/foo.c
new file mode 100644 (file)
index 0000000..acd9563
--- /dev/null
@@ -0,0 +1,6 @@
+
+int pack;
+
+int foo() {
+       return pack;
+}
diff --git a/testing/kernel-cache-tests/ppl-data-const/main.c b/testing/kernel-cache-tests/ppl-data-const/main.c
new file mode 100644 (file)
index 0000000..5bdda59
--- /dev/null
@@ -0,0 +1,7 @@
+
+int x = 0;
+int* g = &x;
+
+int _start() {
+       return *g;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/ppl-data-const/main.kernel b/testing/kernel-cache-tests/ppl-data-const/main.kernel
new file mode 100755 (executable)
index 0000000..b6b70b1
Binary files /dev/null and b/testing/kernel-cache-tests/ppl-data-const/main.kernel differ
diff --git a/testing/kernel-cache-tests/ppl-data-const/test.py b/testing/kernel-cache-tests/ppl-data-const/test.py
new file mode 100644 (file)
index 0000000..7a5c775
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# PPL data const should be folded in under data const and allowed to have fixups
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/ppl-data-const/main.kc", "/ppl-data-const/main.kernel", "/ppl-data-const/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/ppl-data-const/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__DATA_CONST"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0x18000"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmSize"] == "0x8000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__PPLDATA_CONST"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0x18000"
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+
+    # Check we have the correct addresses of the symbols being bound to
+    kernel_cache.analyze("/ppl-data-const/main.kc", ["-symbols", "-arch", "arm64"])
+    # main.kernel
+    # int g = &x;
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["name"] == "_x"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["vmAddr"] == "0x24000"
+    # foo.kext
+    # foo()
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["name"] == "_foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["vmAddr"] == "0x14030"
+
+    kernel_cache.analyze("/ppl-data-const/main.kc", ["-fixups", "-arch", "arm64"])
+    # main.kernel
+    # int g = &x;
+    assert kernel_cache.dictionary()["fixups"]["0x18000"] == "kc(0) + 0x24000"
+    # foo.kext
+    # foo()
+    assert kernel_cache.dictionary()["fixups"]["0x1C000"] == "kc(0) + 0x14030"
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie -Wl,-rename_section,__DATA,__data,__PPLDATA_CONST,__data -Wl,-segprot,__PPLDATA_CONST,r--,r-- main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/strip-all-kexts/bar.c b/testing/kernel-cache-tests/strip-all-kexts/bar.c
new file mode 100644 (file)
index 0000000..2ad0d8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/strip-all-kexts/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/strip-all-kexts/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/strip-all-kexts/extensions/bar.kext/bar b/testing/kernel-cache-tests/strip-all-kexts/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..6288d91
Binary files /dev/null and b/testing/kernel-cache-tests/strip-all-kexts/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/strip-all-kexts/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/strip-all-kexts/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/strip-all-kexts/extensions/foo.kext/foo b/testing/kernel-cache-tests/strip-all-kexts/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..cd1fa31
Binary files /dev/null and b/testing/kernel-cache-tests/strip-all-kexts/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/strip-all-kexts/foo.c b/testing/kernel-cache-tests/strip-all-kexts/foo.c
new file mode 100644 (file)
index 0000000..5a6eec2
--- /dev/null
@@ -0,0 +1,4 @@
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/strip-all-kexts/main.c b/testing/kernel-cache-tests/strip-all-kexts/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/strip-all-kexts/main.kernel b/testing/kernel-cache-tests/strip-all-kexts/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/strip-all-kexts/main.kernel differ
diff --git a/testing/kernel-cache-tests/strip-all-kexts/test.py b/testing/kernel-cache-tests/strip-all-kexts/test.py
new file mode 100644 (file)
index 0000000..b9b78f0
--- /dev/null
@@ -0,0 +1,35 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Check that we stripped all the symbols from kexts but the kernel is still not strippped
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/strip-all-kexts/main.kc", "/strip-all-kexts/main.kernel", "/strip-all-kexts/extensions", ["com.apple.foo", "com.apple.bar"], ["-strip-all-kexts"])
+    kernel_cache.analyze("/strip-all-kexts/main.kc", ["-layout", "-arch", "arm64"])
+
+    # Check the symbols
+    kernel_cache.analyze("/strip-all-kexts/main.kc", ["-symbols", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["global-symbols"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][0]["name"] == "__mh_execute_header"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][1]["name"] == "__start"
+    assert kernel_cache.dictionary()["dylibs"][0]["local-symbols"] == "none"
+    # bar
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["local-symbols"] == "none"
+    # foo
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][2]["local-symbols"] == "none"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/strip-all/bar.c b/testing/kernel-cache-tests/strip-all/bar.c
new file mode 100644 (file)
index 0000000..2ad0d8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/strip-all/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/strip-all/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/strip-all/extensions/bar.kext/bar b/testing/kernel-cache-tests/strip-all/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..6288d91
Binary files /dev/null and b/testing/kernel-cache-tests/strip-all/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/strip-all/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/strip-all/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/strip-all/extensions/foo.kext/foo b/testing/kernel-cache-tests/strip-all/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..cd1fa31
Binary files /dev/null and b/testing/kernel-cache-tests/strip-all/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/strip-all/foo.c b/testing/kernel-cache-tests/strip-all/foo.c
new file mode 100644 (file)
index 0000000..5a6eec2
--- /dev/null
@@ -0,0 +1,4 @@
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/strip-all/main.c b/testing/kernel-cache-tests/strip-all/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/strip-all/main.kernel b/testing/kernel-cache-tests/strip-all/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/strip-all/main.kernel differ
diff --git a/testing/kernel-cache-tests/strip-all/test.py b/testing/kernel-cache-tests/strip-all/test.py
new file mode 100644 (file)
index 0000000..da241ec
--- /dev/null
@@ -0,0 +1,33 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Check that we stripped all the symbols
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/strip-all/main.kc", "/strip-all/main.kernel", "/strip-all/extensions", ["com.apple.foo", "com.apple.bar"], ["-strip-all"])
+    kernel_cache.analyze("/strip-all/main.kc", ["-layout", "-arch", "arm64"])
+
+    # Check the symbols
+    kernel_cache.analyze("/strip-all/main.kc", ["-symbols", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][0]["local-symbols"] == "none"
+    # bar
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["local-symbols"] == "none"
+    # foo
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][2]["local-symbols"] == "none"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/strip-kexts-all/bar.c b/testing/kernel-cache-tests/strip-kexts-all/bar.c
new file mode 100644 (file)
index 0000000..d79e410
--- /dev/null
@@ -0,0 +1,7 @@
+
+extern int foo();
+static int y;
+
+int bar() {
+       return foo() + y;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/strip-kexts-all/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/strip-kexts-all/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/strip-kexts-all/extensions/bar.kext/bar b/testing/kernel-cache-tests/strip-kexts-all/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..df109bd
Binary files /dev/null and b/testing/kernel-cache-tests/strip-kexts-all/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/strip-kexts-all/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/strip-kexts-all/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/strip-kexts-all/extensions/foo.kext/foo b/testing/kernel-cache-tests/strip-kexts-all/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..b374232
Binary files /dev/null and b/testing/kernel-cache-tests/strip-kexts-all/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/strip-kexts-all/foo.c b/testing/kernel-cache-tests/strip-kexts-all/foo.c
new file mode 100644 (file)
index 0000000..8470d7f
--- /dev/null
@@ -0,0 +1,6 @@
+
+static int x;
+
+int foo() {
+       return x;
+}
diff --git a/testing/kernel-cache-tests/strip-kexts-all/main.c b/testing/kernel-cache-tests/strip-kexts-all/main.c
new file mode 100644 (file)
index 0000000..b1f7ec6
--- /dev/null
@@ -0,0 +1,6 @@
+
+static int z;
+
+int _start() {
+       return z;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/strip-kexts-all/main.kernel b/testing/kernel-cache-tests/strip-kexts-all/main.kernel
new file mode 100755 (executable)
index 0000000..f70d9d7
Binary files /dev/null and b/testing/kernel-cache-tests/strip-kexts-all/main.kernel differ
diff --git a/testing/kernel-cache-tests/strip-kexts-all/test.py b/testing/kernel-cache-tests/strip-kexts-all/test.py
new file mode 100644 (file)
index 0000000..6f18849
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Check fine grained stripping of locals and exports
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/strip-kexts-all/main.kc", "/strip-kexts-all/main.kernel", "/strip-kexts-all/extensions", ["com.apple.foo:all", "com.apple.bar"], [])
+    kernel_cache.analyze("/strip-kexts-all/main.kc", ["-layout", "-arch", "arm64"])
+
+    # Check the symbols
+    kernel_cache.analyze("/strip-kexts-all/main.kc", ["-symbols", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["global-symbols"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][0]["name"] == "__mh_execute_header"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][1]["name"] == "__start"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["local-symbols"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["local-symbols"][0]["name"] == "_z"
+    # bar
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["global-symbols"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][0]["name"] == "_bar"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["local-symbols"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][1]["local-symbols"][0]["name"] == "_y"
+    # foo
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][2]["local-symbols"] == "none"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/strip-kexts-exports/bar.c b/testing/kernel-cache-tests/strip-kexts-exports/bar.c
new file mode 100644 (file)
index 0000000..d79e410
--- /dev/null
@@ -0,0 +1,7 @@
+
+extern int foo();
+static int y;
+
+int bar() {
+       return foo() + y;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/strip-kexts-exports/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/strip-kexts-exports/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/strip-kexts-exports/extensions/bar.kext/bar b/testing/kernel-cache-tests/strip-kexts-exports/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..df109bd
Binary files /dev/null and b/testing/kernel-cache-tests/strip-kexts-exports/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/strip-kexts-exports/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/strip-kexts-exports/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/strip-kexts-exports/extensions/foo.kext/foo b/testing/kernel-cache-tests/strip-kexts-exports/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..b374232
Binary files /dev/null and b/testing/kernel-cache-tests/strip-kexts-exports/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/strip-kexts-exports/foo.c b/testing/kernel-cache-tests/strip-kexts-exports/foo.c
new file mode 100644 (file)
index 0000000..8470d7f
--- /dev/null
@@ -0,0 +1,6 @@
+
+static int x;
+
+int foo() {
+       return x;
+}
diff --git a/testing/kernel-cache-tests/strip-kexts-exports/main.c b/testing/kernel-cache-tests/strip-kexts-exports/main.c
new file mode 100644 (file)
index 0000000..b1f7ec6
--- /dev/null
@@ -0,0 +1,6 @@
+
+static int z;
+
+int _start() {
+       return z;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/strip-kexts-exports/main.kernel b/testing/kernel-cache-tests/strip-kexts-exports/main.kernel
new file mode 100755 (executable)
index 0000000..f70d9d7
Binary files /dev/null and b/testing/kernel-cache-tests/strip-kexts-exports/main.kernel differ
diff --git a/testing/kernel-cache-tests/strip-kexts-exports/test.py b/testing/kernel-cache-tests/strip-kexts-exports/test.py
new file mode 100644 (file)
index 0000000..fe8998e
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Check fine grained stripping of exports
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/strip-kexts-exports/main.kc", "/strip-kexts-exports/main.kernel", "/strip-kexts-exports/extensions", ["com.apple.foo:exports", "com.apple.bar"], [])
+    kernel_cache.analyze("/strip-kexts-exports/main.kc", ["-layout", "-arch", "arm64"])
+
+    # Check the symbols
+    kernel_cache.analyze("/strip-kexts-exports/main.kc", ["-symbols", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["global-symbols"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][0]["name"] == "__mh_execute_header"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][1]["name"] == "__start"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["local-symbols"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["local-symbols"][0]["name"] == "_z"
+    # bar
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["global-symbols"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][0]["name"] == "_bar"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["local-symbols"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][1]["local-symbols"][0]["name"] == "_y"
+    # foo
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"] == "none"
+    assert len(kernel_cache.dictionary()["dylibs"][2]["local-symbols"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][2]["local-symbols"][0]["name"] == "_x"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/strip-kexts-locals/bar.c b/testing/kernel-cache-tests/strip-kexts-locals/bar.c
new file mode 100644 (file)
index 0000000..d79e410
--- /dev/null
@@ -0,0 +1,7 @@
+
+extern int foo();
+static int y;
+
+int bar() {
+       return foo() + y;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/strip-kexts-locals/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/strip-kexts-locals/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/strip-kexts-locals/extensions/bar.kext/bar b/testing/kernel-cache-tests/strip-kexts-locals/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..df109bd
Binary files /dev/null and b/testing/kernel-cache-tests/strip-kexts-locals/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/strip-kexts-locals/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/strip-kexts-locals/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/strip-kexts-locals/extensions/foo.kext/foo b/testing/kernel-cache-tests/strip-kexts-locals/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..b374232
Binary files /dev/null and b/testing/kernel-cache-tests/strip-kexts-locals/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/strip-kexts-locals/foo.c b/testing/kernel-cache-tests/strip-kexts-locals/foo.c
new file mode 100644 (file)
index 0000000..8470d7f
--- /dev/null
@@ -0,0 +1,6 @@
+
+static int x;
+
+int foo() {
+       return x;
+}
diff --git a/testing/kernel-cache-tests/strip-kexts-locals/main.c b/testing/kernel-cache-tests/strip-kexts-locals/main.c
new file mode 100644 (file)
index 0000000..b1f7ec6
--- /dev/null
@@ -0,0 +1,6 @@
+
+static int z;
+
+int _start() {
+       return z;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/strip-kexts-locals/main.kernel b/testing/kernel-cache-tests/strip-kexts-locals/main.kernel
new file mode 100755 (executable)
index 0000000..f70d9d7
Binary files /dev/null and b/testing/kernel-cache-tests/strip-kexts-locals/main.kernel differ
diff --git a/testing/kernel-cache-tests/strip-kexts-locals/test.py b/testing/kernel-cache-tests/strip-kexts-locals/test.py
new file mode 100644 (file)
index 0000000..bfbe0dd
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Check fine grained stripping of locals
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/strip-kexts-locals/main.kc", "/strip-kexts-locals/main.kernel", "/strip-kexts-locals/extensions", ["com.apple.foo:locals", "com.apple.bar"], [])
+    kernel_cache.analyze("/strip-kexts-locals/main.kc", ["-layout", "-arch", "arm64"])
+
+    # Check the symbols
+    kernel_cache.analyze("/strip-kexts-locals/main.kc", ["-symbols", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["global-symbols"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][0]["name"] == "__mh_execute_header"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][1]["name"] == "__start"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["local-symbols"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["local-symbols"][0]["name"] == "_z"
+    # bar
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["global-symbols"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][0]["name"] == "_bar"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["local-symbols"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][1]["local-symbols"][0]["name"] == "_y"
+    # foo
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert len(kernel_cache.dictionary()["dylibs"][2]["global-symbols"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["name"] == "_foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["local-symbols"] == "none"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/strip-none/bar.c b/testing/kernel-cache-tests/strip-none/bar.c
new file mode 100644 (file)
index 0000000..2ad0d8d
--- /dev/null
@@ -0,0 +1,6 @@
+
+extern int foo();
+
+int bar() {
+       return foo();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/strip-none/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/strip-none/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/strip-none/extensions/bar.kext/bar b/testing/kernel-cache-tests/strip-none/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..6288d91
Binary files /dev/null and b/testing/kernel-cache-tests/strip-none/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/strip-none/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/strip-none/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/strip-none/extensions/foo.kext/foo b/testing/kernel-cache-tests/strip-none/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..cd1fa31
Binary files /dev/null and b/testing/kernel-cache-tests/strip-none/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/strip-none/foo.c b/testing/kernel-cache-tests/strip-none/foo.c
new file mode 100644 (file)
index 0000000..5a6eec2
--- /dev/null
@@ -0,0 +1,4 @@
+
+int foo() {
+       return 0;
+}
diff --git a/testing/kernel-cache-tests/strip-none/main.c b/testing/kernel-cache-tests/strip-none/main.c
new file mode 100644 (file)
index 0000000..7e49911
--- /dev/null
@@ -0,0 +1,4 @@
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/strip-none/main.kernel b/testing/kernel-cache-tests/strip-none/main.kernel
new file mode 100755 (executable)
index 0000000..9f1a437
Binary files /dev/null and b/testing/kernel-cache-tests/strip-none/main.kernel differ
diff --git a/testing/kernel-cache-tests/strip-none/test.py b/testing/kernel-cache-tests/strip-none/test.py
new file mode 100644 (file)
index 0000000..0d09db0
--- /dev/null
@@ -0,0 +1,37 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Check that the kernel and kexts still have all their symbols as we didn't strip them
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/strip-none/main.kc", "/strip-none/main.kernel", "/strip-none/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/strip-none/main.kc", ["-layout", "-arch", "arm64"])
+
+    # Check the symbols
+    kernel_cache.analyze("/strip-none/main.kc", ["-symbols", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["global-symbols"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][0]["name"] == "__mh_execute_header"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][1]["name"] == "__start"
+    assert kernel_cache.dictionary()["dylibs"][0]["local-symbols"] == "none"
+    # bar
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["global-symbols"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][1]["global-symbols"][0]["name"] == "_bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["local-symbols"] == "none"
+    # foo
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert len(kernel_cache.dictionary()["dylibs"][2]["global-symbols"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][2]["global-symbols"][0]["name"] == "_foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["local-symbols"] == "none"
+
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info bar.c -o extensions/bar.kext/bar
+# [~]> rm -r extensions/*.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/SymbolSets.plist b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/SymbolSets.plist
new file mode 100644 (file)
index 0000000..88c91cc
--- /dev/null
@@ -0,0 +1,30 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.bar</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolName</key>
+                                       <string>_symbol_from_bar</string>
+                                       <key>AliasTarget</key>
+                                       <string>_symbol_from_xnu</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolName</key>
+                                       <string>_symbol_from_xnu_no_alias</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..77b3de8
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/baz.kext/Info.plist b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/baz.kext/Info.plist
new file mode 100644 (file)
index 0000000..683493e
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>baz</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.baz</string>
+       <key>CFBundleName</key>
+       <string>baz</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..0865794
--- /dev/null
@@ -0,0 +1,21 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.bar</key>
+               <string>1.0</string>
+               <key>com.apple.baz</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/foo.kext/foo b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..9bbe62d
Binary files /dev/null and b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/foo.c b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/foo.c
new file mode 100644 (file)
index 0000000..50a560e
--- /dev/null
@@ -0,0 +1,7 @@
+
+extern int symbol_from_bar();
+extern int symbol_from_xnu_no_alias();
+
+int foo() {
+       return symbol_from_bar() + symbol_from_xnu_no_alias();
+}
diff --git a/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/main.c b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/main.c
new file mode 100644 (file)
index 0000000..5b3791c
--- /dev/null
@@ -0,0 +1,14 @@
+
+// This will be re-exported from a symbol set in bar with an alias
+int symbol_from_xnu() {
+       return 0;
+}
+
+// This will be re-exported from a symbol set in bar without an alias
+int symbol_from_xnu_no_alias() {
+       return 0;
+}
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/main.kernel b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/main.kernel
new file mode 100755 (executable)
index 0000000..62811b7
Binary files /dev/null and b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/main.kernel differ
diff --git a/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/test.py b/testing/kernel-cache-tests/symbol-sets-and-codeless-kexts/test.py
new file mode 100644 (file)
index 0000000..b8ee22b
--- /dev/null
@@ -0,0 +1,41 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Test that the codeless bar.kext (with bundle id com.apple.bar) doesn't interfere with foo using the symbol set of the same bundle id
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/symbol-sets-and-codeless-kexts/main.kc", "/symbol-sets-and-codeless-kexts/main.kernel", "/symbol-sets-and-codeless-kexts/extensions", ["com.apple.foo", "com.apple.bar", "com.apple.baz"], [])
+    kernel_cache.analyze("/symbol-sets-and-codeless-kexts/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA_CONST"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x14000"
+
+    # Find the address of the symbols to bind to
+    kernel_cache.analyze("/symbol-sets-and-codeless-kexts/main.kc", ["-symbols", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["name"] == "_symbol_from_xnu"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["vmAddr"] == "0xC000"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["name"] == "_symbol_from_xnu_no_alias"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["vmAddr"] == "0xC00C"
+
+    # Check the fixups
+    kernel_cache.analyze("/symbol-sets-and-codeless-kexts/main.kc", ["-fixups", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 2
+    assert kernel_cache.dictionary()["fixups"]["0x14000"] == "kc(0) + 0xC000"
+    assert kernel_cache.dictionary()["fixups"]["0x14008"] == "kc(0) + 0xC00C"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r--
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> rm -r extensions/foo.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/symbol-sets-prefix/SymbolSets.plist b/testing/kernel-cache-tests/symbol-sets-prefix/SymbolSets.plist
new file mode 100644 (file)
index 0000000..e0fe9ad
--- /dev/null
@@ -0,0 +1,24 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.symbols</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolPrefix</key>
+                                       <string>_symbol_from_xnu</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/symbol-sets-prefix/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/symbol-sets-prefix/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..237c06f
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.symbols</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/symbol-sets-prefix/extensions/foo.kext/foo b/testing/kernel-cache-tests/symbol-sets-prefix/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..ca3e060
Binary files /dev/null and b/testing/kernel-cache-tests/symbol-sets-prefix/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/symbol-sets-prefix/foo.c b/testing/kernel-cache-tests/symbol-sets-prefix/foo.c
new file mode 100644 (file)
index 0000000..1ec0ceb
--- /dev/null
@@ -0,0 +1,9 @@
+
+extern int symbol_from_xnu0();
+extern int symbol_from_xnu1();
+extern int symbol_from_xnu2();
+extern int symbol_from_xnu3();
+
+int foo() {
+       return symbol_from_xnu0() + symbol_from_xnu1() + symbol_from_xnu2() + symbol_from_xnu3();
+}
diff --git a/testing/kernel-cache-tests/symbol-sets-prefix/main.c b/testing/kernel-cache-tests/symbol-sets-prefix/main.c
new file mode 100644 (file)
index 0000000..389d828
--- /dev/null
@@ -0,0 +1,21 @@
+
+int symbol_from_xnu0() {
+       return 0;
+}
+
+int symbol_from_xnu1() {
+       return 0;
+}
+
+int symbol_from_xnu2() {
+       return 0;
+}
+
+int symbol_from_xnu3() {
+       return 0;
+}
+
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/symbol-sets-prefix/main.kernel b/testing/kernel-cache-tests/symbol-sets-prefix/main.kernel
new file mode 100755 (executable)
index 0000000..a6f279b
Binary files /dev/null and b/testing/kernel-cache-tests/symbol-sets-prefix/main.kernel differ
diff --git a/testing/kernel-cache-tests/symbol-sets-prefix/test.py b/testing/kernel-cache-tests/symbol-sets-prefix/test.py
new file mode 100644 (file)
index 0000000..26ff1da
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# Symbol sets can have a prefix with an implicit * wildcard on the end which re-exports anything from xnu with that name
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/symbol-sets-prefix/main.kc", "/symbol-sets-prefix/main.kernel", "/symbol-sets-prefix/extensions", ["com.apple.foo"], [])
+    kernel_cache.analyze("/symbol-sets-prefix/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA_CONST"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x14000"
+
+    # Check the symbols
+    kernel_cache.analyze("/symbol-sets-prefix/main.kc", ["-symbols", "-arch", "arm64"])
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["global-symbols"]) == 6
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][0]["name"] == "__mh_execute_header"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][1]["name"] == "__start"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["name"] == "_symbol_from_xnu0"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["vmAddr"] == "0xC000"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["name"] == "_symbol_from_xnu1"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["vmAddr"] == "0xC00C"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][4]["name"] == "_symbol_from_xnu2"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][4]["vmAddr"] == "0xC018"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][5]["name"] == "_symbol_from_xnu3"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][5]["vmAddr"] == "0xC024"
+
+    # Check the fixups
+    kernel_cache.analyze("/symbol-sets-prefix/main.kc", ["-fixups", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 4
+    assert kernel_cache.dictionary()["fixups"]["0x14000"] == "kc(0) + 0xC000"
+    assert kernel_cache.dictionary()["fixups"]["0x14008"] == "kc(0) + 0xC00C"
+    assert kernel_cache.dictionary()["fixups"]["0x14010"] == "kc(0) + 0xC018"
+    assert kernel_cache.dictionary()["fixups"]["0x14018"] == "kc(0) + 0xC024"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r--
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> rm -r extensions/foo.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/symbol-sets/SymbolSets.plist b/testing/kernel-cache-tests/symbol-sets/SymbolSets.plist
new file mode 100644 (file)
index 0000000..88c91cc
--- /dev/null
@@ -0,0 +1,30 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.bar</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolName</key>
+                                       <string>_symbol_from_bar</string>
+                                       <key>AliasTarget</key>
+                                       <string>_symbol_from_xnu</string>
+                               </dict>
+                               <dict>
+                                       <key>SymbolName</key>
+                                       <string>_symbol_from_xnu_no_alias</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/symbol-sets/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/symbol-sets/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..1111344
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.bar</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/symbol-sets/extensions/foo.kext/foo b/testing/kernel-cache-tests/symbol-sets/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..9bbe62d
Binary files /dev/null and b/testing/kernel-cache-tests/symbol-sets/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/symbol-sets/foo.c b/testing/kernel-cache-tests/symbol-sets/foo.c
new file mode 100644 (file)
index 0000000..50a560e
--- /dev/null
@@ -0,0 +1,7 @@
+
+extern int symbol_from_bar();
+extern int symbol_from_xnu_no_alias();
+
+int foo() {
+       return symbol_from_bar() + symbol_from_xnu_no_alias();
+}
diff --git a/testing/kernel-cache-tests/symbol-sets/main.c b/testing/kernel-cache-tests/symbol-sets/main.c
new file mode 100644 (file)
index 0000000..5b3791c
--- /dev/null
@@ -0,0 +1,14 @@
+
+// This will be re-exported from a symbol set in bar with an alias
+int symbol_from_xnu() {
+       return 0;
+}
+
+// This will be re-exported from a symbol set in bar without an alias
+int symbol_from_xnu_no_alias() {
+       return 0;
+}
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/symbol-sets/main.kernel b/testing/kernel-cache-tests/symbol-sets/main.kernel
new file mode 100755 (executable)
index 0000000..62811b7
Binary files /dev/null and b/testing/kernel-cache-tests/symbol-sets/main.kernel differ
diff --git a/testing/kernel-cache-tests/symbol-sets/test.py b/testing/kernel-cache-tests/symbol-sets/test.py
new file mode 100644 (file)
index 0000000..f2cb88c
--- /dev/null
@@ -0,0 +1,42 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# This verifies that we use the symbol set when resolving symbols in to the kernel
+# Note symbol sets are the plist which is embedded in to the kernel
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/symbol-sets/main.kc", "/symbol-sets/main.kernel", "/symbol-sets/extensions", ["com.apple.foo"], [])
+    kernel_cache.analyze("/symbol-sets/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA_CONST"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x14000"
+
+    # Find the address of the symbols to bind to
+    kernel_cache.analyze("/symbol-sets/main.kc", ["-symbols", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["name"] == "_symbol_from_xnu"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][2]["vmAddr"] == "0xC000"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["name"] == "_symbol_from_xnu_no_alias"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][3]["vmAddr"] == "0xC00C"
+
+    # Check the fixups
+    kernel_cache.analyze("/symbol-sets/main.kc", ["-fixups", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 2
+    assert kernel_cache.dictionary()["fixups"]["0x14000"] == "kc(0) + 0xC000"
+    assert kernel_cache.dictionary()["fixups"]["0x14008"] == "kc(0) + 0xC00C"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r--
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> rm -r extensions/foo.kext/*.ld
+
diff --git a/testing/kernel-cache-tests/testall.py b/testing/kernel-cache-tests/testall.py
new file mode 100755 (executable)
index 0000000..fd7cfba
--- /dev/null
@@ -0,0 +1,47 @@
+#!/usr/bin/python2.7
+
+import string
+import os
+import json
+import sys
+import imp
+import os.path
+import traceback
+
+sys.dont_write_bytecode = True
+
+import KernelCollection
+
+
+if __name__ == "__main__":
+    test_dir = os.path.realpath(os.path.dirname(__file__))
+    sys.path.append(test_dir)
+    all_tests = os.listdir(test_dir)
+    all_tests.sort()
+    test_to_run = ""
+    if len(sys.argv) == 2:
+        test_to_run = sys.argv[1]
+        all_tests = [ test_to_run ]
+    for f in all_tests:
+        test_case = test_dir + "/" + f + "/test.py"
+        if os.path.isfile(test_case):
+            py_mod = imp.load_source(f, test_case)
+            check_func = getattr(py_mod, "check", 0)
+            if check_func == 0:
+                print "FAIL: " + f + ", missing check() function";
+            else:
+                try:
+                    kernelCollection = KernelCollection.KernelCollection(test_to_run != "")
+                    check_func(kernelCollection)
+                    print "PASS: " + f
+                except AssertionError, e:
+                    _, _, tb = sys.exc_info()
+                    tb_info = traceback.extract_tb(tb)
+                    filename, line, func, text = tb_info[-1]
+                    print "FAIL: " + f + ", " + text
+                except KeyError, e:
+                    _, _, tb = sys.exc_info()
+                    tb_info = traceback.extract_tb(tb)
+                    filename, line, func, text = tb_info[-1]
+                    print "FAIL: " + f + ", " + text
+
diff --git a/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/bar.c b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/bar.c
new file mode 100644 (file)
index 0000000..2cf9969
--- /dev/null
@@ -0,0 +1,13 @@
+
+int g = 0;
+
+int bar() {
+       return g;
+}
+
+__attribute__((section(("__TEXT, __text"))))
+__typeof(&bar) barPtr = &bar;
+
+int baz() {
+       return barPtr();
+}
diff --git a/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/bar.kext/bar b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..7b04223
Binary files /dev/null and b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/foo.kext/foo b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..e333729
Binary files /dev/null and b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/foo.c b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/foo.c
new file mode 100644 (file)
index 0000000..67edd8a
--- /dev/null
@@ -0,0 +1,9 @@
+
+int g = 0;
+
+__attribute__((section(("__TEXT, __text"))))
+int* gPtr = &g;
+
+int foo() {
+       return *gPtr;
+}
diff --git a/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/main.cpp b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/main.cpp
new file mode 100644 (file)
index 0000000..b3811a2
--- /dev/null
@@ -0,0 +1,36 @@
+
+int g = 0;
+
+static int func() {
+       return g;
+}
+
+struct S {
+       __typeof(&func) funcPtr;
+       __typeof(&func) funcPtr2;
+       int *p1;
+       __attribute__((aligned((16384)))) __typeof(&func) funcPtr3;
+       int *p2;
+};
+
+S s = { &func, &func, &g, &func, &g };
+
+struct __attribute__((packed)) PackedS {
+       int i;
+       __typeof(&func) funcPtr;        // aligned to 4
+       __typeof(&func) funcPtr2;       // aligned to 4
+       int j;
+       int *p1;                                        // aligned to 8
+       char k;
+       int *p2;                                        // aligned to 1
+};
+
+__attribute__((aligned((16384))))
+PackedS ps = { 0, &func, &func, 0, &g, 0, &g };
+
+__asm(".code32; .text; .section __HIB, __text; .globl _foo; _foo: movl _foo, %esp; ret");
+
+__attribute__((section(("__HIB, __text"))))
+extern "C" int _start() {
+       return s.funcPtr() + s.funcPtr2() + s.funcPtr3() + ps.funcPtr();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/main.kernel b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/main.kernel
new file mode 100755 (executable)
index 0000000..f2ebee2
Binary files /dev/null and b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/main.kernel differ
diff --git a/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/test.py b/testing/kernel-cache-tests/text-fixups-x86_64-auxkc/test.py
new file mode 100644 (file)
index 0000000..07f44b8
--- /dev/null
@@ -0,0 +1,66 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+
+def findGlobalSymbolVMAddr(kernel_cache, dylib_index, symbol_name):
+    for symbol_and_addr in kernel_cache.dictionary()["dylibs"][dylib_index]["global-symbols"]:
+        if symbol_and_addr["name"] == symbol_name:
+            return symbol_and_addr["vmAddr"]
+    return None
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/text-fixups-x86_64-auxkc/main.kc", "/text-fixups-x86_64-auxkc/main.kernel", "/text-fixups-x86_64-auxkc/extensions", [], [])
+    kernel_cache.analyze("/text-fixups-x86_64-auxkc/main.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+
+    # Now build an aux cache using the baseline kernel collection
+    kernel_cache.buildAuxKernelCollection("x86_64", "/text-fixups-x86_64-auxkc/aux.kc", "/text-fixups-x86_64-auxkc/main.kc", "", "/text-fixups-x86_64-auxkc/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/text-fixups-x86_64-auxkc/aux.kc", ["-layout", "-arch", "x86_64"])
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0x8000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0x9000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0xC000"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0xA000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0xB000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0xC000"
+
+    # Find the sybmols the fixups point to
+    kernel_cache.analyze("/text-fixups-x86_64-auxkc/aux.kc", ["-symbols", "-arch", "x86_64"])
+    barAddress = findGlobalSymbolVMAddr(kernel_cache, 0, "_bar")
+    gAddress = findGlobalSymbolVMAddr(kernel_cache, 1, "_g")
+
+    # Check the fixups
+    kernel_cache.analyze("/text-fixups-x86_64-auxkc/aux.kc", ["-fixups", "-arch", "x86_64"])
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["fixups"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"]["0x4FF0"] == "kc(3) + " + barAddress
+
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    
+    assert len(kernel_cache.dictionary()["dylibs"][1]["fixups"]) == 1
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"]["0x4FF0"] == "kc(3) + " + gAddress
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-kernel -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0xffffff8000200000 -Wl,-segaddr,__HIB,0xffffff8000100000 -Wl,-add_split_seg_info  -Wl,-read_only_relocs,suppress -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib bar.c -o extensions/bar.kext/bar
+
diff --git a/testing/kernel-cache-tests/text-fixups-x86_64/bar.c b/testing/kernel-cache-tests/text-fixups-x86_64/bar.c
new file mode 100644 (file)
index 0000000..2cf9969
--- /dev/null
@@ -0,0 +1,13 @@
+
+int g = 0;
+
+int bar() {
+       return g;
+}
+
+__attribute__((section(("__TEXT, __text"))))
+__typeof(&bar) barPtr = &bar;
+
+int baz() {
+       return barPtr();
+}
diff --git a/testing/kernel-cache-tests/text-fixups-x86_64/extensions/bar.kext/Info.plist b/testing/kernel-cache-tests/text-fixups-x86_64/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..2eee003
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.foo</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/text-fixups-x86_64/extensions/bar.kext/bar b/testing/kernel-cache-tests/text-fixups-x86_64/extensions/bar.kext/bar
new file mode 100755 (executable)
index 0000000..7b04223
Binary files /dev/null and b/testing/kernel-cache-tests/text-fixups-x86_64/extensions/bar.kext/bar differ
diff --git a/testing/kernel-cache-tests/text-fixups-x86_64/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/text-fixups-x86_64/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..f379e8b
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/text-fixups-x86_64/extensions/foo.kext/foo b/testing/kernel-cache-tests/text-fixups-x86_64/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..e333729
Binary files /dev/null and b/testing/kernel-cache-tests/text-fixups-x86_64/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/text-fixups-x86_64/foo.c b/testing/kernel-cache-tests/text-fixups-x86_64/foo.c
new file mode 100644 (file)
index 0000000..67edd8a
--- /dev/null
@@ -0,0 +1,9 @@
+
+int g = 0;
+
+__attribute__((section(("__TEXT, __text"))))
+int* gPtr = &g;
+
+int foo() {
+       return *gPtr;
+}
diff --git a/testing/kernel-cache-tests/text-fixups-x86_64/main.cpp b/testing/kernel-cache-tests/text-fixups-x86_64/main.cpp
new file mode 100644 (file)
index 0000000..b3811a2
--- /dev/null
@@ -0,0 +1,36 @@
+
+int g = 0;
+
+static int func() {
+       return g;
+}
+
+struct S {
+       __typeof(&func) funcPtr;
+       __typeof(&func) funcPtr2;
+       int *p1;
+       __attribute__((aligned((16384)))) __typeof(&func) funcPtr3;
+       int *p2;
+};
+
+S s = { &func, &func, &g, &func, &g };
+
+struct __attribute__((packed)) PackedS {
+       int i;
+       __typeof(&func) funcPtr;        // aligned to 4
+       __typeof(&func) funcPtr2;       // aligned to 4
+       int j;
+       int *p1;                                        // aligned to 8
+       char k;
+       int *p2;                                        // aligned to 1
+};
+
+__attribute__((aligned((16384))))
+PackedS ps = { 0, &func, &func, 0, &g, 0, &g };
+
+__asm(".code32; .text; .section __HIB, __text; .globl _foo; _foo: movl _foo, %esp; ret");
+
+__attribute__((section(("__HIB, __text"))))
+extern "C" int _start() {
+       return s.funcPtr() + s.funcPtr2() + s.funcPtr3() + ps.funcPtr();
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/text-fixups-x86_64/main.kernel b/testing/kernel-cache-tests/text-fixups-x86_64/main.kernel
new file mode 100755 (executable)
index 0000000..f2ebee2
Binary files /dev/null and b/testing/kernel-cache-tests/text-fixups-x86_64/main.kernel differ
diff --git a/testing/kernel-cache-tests/text-fixups-x86_64/test.py b/testing/kernel-cache-tests/text-fixups-x86_64/test.py
new file mode 100644 (file)
index 0000000..412f8a2
--- /dev/null
@@ -0,0 +1,102 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("x86_64", "/text-fixups-x86_64/main.kc", "/text-fixups-x86_64/main.kernel", "/text-fixups-x86_64/extensions", ["com.apple.foo", "com.apple.bar"], [])
+    kernel_cache.analyze("/text-fixups-x86_64/main.kc", ["-layout", "-arch", "x86_64"])
+
+    assert len(kernel_cache.dictionary()["cache-segments"]) == 11
+    assert kernel_cache.dictionary()["cache-segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][0]["vmAddr"] == "0xFFFFFF8000200000"
+    assert kernel_cache.dictionary()["cache-segments"][1]["name"] == "__PRELINK_TEXT"
+    assert kernel_cache.dictionary()["cache-segments"][1]["vmAddr"] == "0xFFFFFF8000204000"
+    assert kernel_cache.dictionary()["cache-segments"][2]["name"] == "__TEXT_EXEC"
+    assert kernel_cache.dictionary()["cache-segments"][2]["vmAddr"] == "0xFFFFFF8000204000"
+    assert kernel_cache.dictionary()["cache-segments"][3]["name"] == "__PRELINK_INFO"
+    assert kernel_cache.dictionary()["cache-segments"][3]["vmAddr"] == "0xFFFFFF8000208000"
+    assert kernel_cache.dictionary()["cache-segments"][4]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["cache-segments"][4]["vmAddr"] == "0xFFFFFF800020C000"
+    assert kernel_cache.dictionary()["cache-segments"][5]["name"] == "__HIB"
+    assert kernel_cache.dictionary()["cache-segments"][5]["vmAddr"] == "0xFFFFFF8000100000"
+    assert kernel_cache.dictionary()["cache-segments"][6]["name"] == "__REGION0"
+    assert kernel_cache.dictionary()["cache-segments"][6]["vmAddr"] == "0xFFFFFF8000218000"
+    assert kernel_cache.dictionary()["cache-segments"][7]["name"] == "__REGION1"
+    assert kernel_cache.dictionary()["cache-segments"][7]["vmAddr"] == "0xFFFFFF8000219000"
+    assert kernel_cache.dictionary()["cache-segments"][8]["name"] == "__REGION2"
+    assert kernel_cache.dictionary()["cache-segments"][8]["vmAddr"] == "0xFFFFFF800021A000"
+    assert kernel_cache.dictionary()["cache-segments"][9]["name"] == "__REGION3"
+    assert kernel_cache.dictionary()["cache-segments"][9]["vmAddr"] == "0xFFFFFF800021B000"
+    assert kernel_cache.dictionary()["cache-segments"][10]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["cache-segments"][10]["vmAddr"] == "0xFFFFFF800021C000"
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    # main.kernel
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert len(kernel_cache.dictionary()["dylibs"][0]["segments"]) == 4
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][0]["vmAddr"] == "0xFFFFFF8000204000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][1]["vmAddr"] == "0xFFFFFF800020C000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["name"] == "__HIB"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][2]["vmAddr"] == "0xFFFFFF8000100000"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][0]["segments"][3]["vmAddr"] == "0xFFFFFF800021C000"
+    # bar.kext
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert len(kernel_cache.dictionary()["dylibs"][1]["segments"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][0]["vmAddr"] == "0xFFFFFF8000218000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][1]["vmAddr"] == "0xFFFFFF8000219000"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0xFFFFFF800021C000"
+    # foo.kext
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert len(kernel_cache.dictionary()["dylibs"][2]["segments"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["name"] == "__TEXT"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][0]["vmAddr"] == "0xFFFFFF800021A000"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["name"] == "__DATA"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][1]["vmAddr"] == "0xFFFFFF800021B000"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["name"] == "__LINKEDIT"
+    assert kernel_cache.dictionary()["dylibs"][2]["segments"][2]["vmAddr"] == "0xFFFFFF800021C000"
+
+    # Check the fixups
+    kernel_cache.analyze("/text-fixups-x86_64/main.kc", ["-fixups", "-arch", "x86_64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 12
+    # main.kernel: S s = { &func, &func, &g, &func, &g };
+    # _s is at 0xFFFFFF8000208000 which is offset 0x108000 from __HIB
+    assert kernel_cache.dictionary()["fixups"]["0x10C000"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x10C008"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x10C010"] == "kc(0) + 0xFFFFFF800021402C : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x110000"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x110008"] == "kc(0) + 0xFFFFFF800021402C : pointer64"
+    # main.kernel: PackedS ps = { 0, &func, &func, 0, &g, 0, &g };
+    # _ps is at 0xFFFFFF8000210000 which is offset 0x110000 from __HIB
+    assert kernel_cache.dictionary()["fixups"]["0x114004"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x11400C"] == "kc(0) + 0xFFFFFF8000204FF0 : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x114018"] == "kc(0) + 0xFFFFFF800021402C : pointer64"
+    assert kernel_cache.dictionary()["fixups"]["0x114021"] == "kc(0) + 0xFFFFFF800021402C : pointer64"
+    # bar.kext: __typeof(&bar) barPtr = &bar;
+    # _barPtr is at 0xFFFFFF8000210030 which is offset 0x110030 from __HIB
+    assert kernel_cache.dictionary()["fixups"]["0x118FF0"] == "kc(0) + 0xFFFFFF8000218FD0"
+    # foo.kext: int* gPtr = &g;
+    # _gPtr is at 0xFFFFFF8000210040 which is offset 0x110040 from __HIB
+    assert kernel_cache.dictionary()["fixups"]["0x11AFF0"] == "kc(0) + 0xFFFFFF800021B000"
+    # main.kernel: movl _foo, %esp
+    # The _foo reloc is at 0xFFFFFF8000100002 which is offset 0x2 from __HIB
+    assert kernel_cache.dictionary()["fixups"]["0x2"] == "kc(0) + 0x100000 : pointer32"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 3
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.bar"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][2]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][2]["fixups"] == "none"
+
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-static -mkernel -nostdlib -Wl,-kernel -Wl,-e,__start -Wl,-pie main.cpp -Wl,-pagezero_size,0x0 -o main.kernel -Wl,-image_base,0xffffff8000200000 -Wl,-segaddr,__HIB,0xffffff8000100000 -Wl,-add_split_seg_info  -Wl,-read_only_relocs,suppress -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib foo.c -o extensions/foo.kext/foo
+# [~]> xcrun -sdk macosx.internal cc -arch x86_64 -Wl,-kext -mkernel -nostdlib bar.c -o extensions/bar.kext/bar
+
diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/SymbolSets.plist b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/SymbolSets.plist
new file mode 100644 (file)
index 0000000..e8397f4
--- /dev/null
@@ -0,0 +1,24 @@
+<?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>SymbolsSets</key>
+       <array>
+               <dict>
+                       <key>CFBundleIdentifier</key>
+                       <string>com.apple.bar</string>
+                       <key>CFBundleVersion</key>
+                       <string>###KERNEL_VERSION_LONG###</string>
+                       <key>OSBundleCompatibleVersion</key>
+                       <string>8.0.0b1</string>
+                       <key>Symbols</key>
+                       <array>
+                               <dict>
+                                       <key>SymbolName</key>
+                                       <string>__ZN15OSMetaClassBase8DispatchE5IORPC</string>
+                               </dict>
+                       </array>
+               </dict>
+       </array>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/Info.plist b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..1111344
--- /dev/null
@@ -0,0 +1,19 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>foo</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.bar</key>
+               <string>1.0</string>
+       </dict>
+</dict>
+</plist>
diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo
new file mode 100755 (executable)
index 0000000..5bed34f
Binary files /dev/null and b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo differ
diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/filelist b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/filelist
new file mode 100644 (file)
index 0000000..29f0ea3
--- /dev/null
@@ -0,0 +1 @@
+files/foo-f6e015.o
diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/files/foo-f6e015.o b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/files/foo-f6e015.o
new file mode 100644 (file)
index 0000000..6f02100
Binary files /dev/null and b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/files/foo-f6e015.o differ
diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/link_command b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/link_command
new file mode 100644 (file)
index 0000000..2f98bd6
--- /dev/null
@@ -0,0 +1,10 @@
+/Applications/Xcode.app/Contents/Developer/Toolchains/iOS14.0.xctoolchain/usr/bin/ld
+-Z
+-demangle
+-lto_library /Applications/Xcode.app/Contents/Developer/Toolchains/iOS14.0.xctoolchain/usr/lib/libLTO.dylib
+-no_deduplicate
+-dynamic
+-arch arm64
+-iphoneos_version_min 14.0.0
+-kext
+-add_split_seg_info
diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/orig_command_line b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/extensions/foo.kext/foo.arm64.ld/orig_command_line
new file mode 100644 (file)
index 0000000..1643a33
--- /dev/null
@@ -0,0 +1,2 @@
+cd /Users/pete/dyld/testing/kernel-cache-tests/vtable-patching-metaclass-hack
+/Applications/Xcode.app/Contents/Developer/Toolchains/iOS14.0.xctoolchain/usr/bin/ld -demangle -lto_library /Applications/Xcode.app/Contents/Developer/Toolchains/iOS14.0.xctoolchain/usr/lib/libLTO.dylib -no_deduplicate -dynamic -arch arm64 -iphoneos_version_min 14.0.0 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.Internal.sdk -o extensions/foo.kext/foo -kext -add_split_seg_info /var/folders/m5/2d3gpq6x53bfdgfshsfxqxv40000gn/T/foo-f6e015.o
diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/foo.c b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/foo.c
new file mode 100644 (file)
index 0000000..5b7b9be
--- /dev/null
@@ -0,0 +1,10 @@
+
+// This is the symbol xnu now exports
+extern int symbol_from_xnu() __asm("__ZN15OSMetaClassBase8DispatchE5IORPC");
+
+// And this is the old symbol it needs to implicitly alias to the above symbol
+extern int symbol_from_xnu_implicit_alias() __asm("__ZN15OSMetaClassBase25_RESERVEDOSMetaClassBase3Ev");
+
+int foo() {
+       return symbol_from_xnu() + symbol_from_xnu_implicit_alias();
+}
diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/main.c b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/main.c
new file mode 100644 (file)
index 0000000..e1e3eb5
--- /dev/null
@@ -0,0 +1,10 @@
+
+// This will be re-exported from a symbol set in bar with an alias
+int symbol_from_xnu() __asm("__ZN15OSMetaClassBase8DispatchE5IORPC");
+int symbol_from_xnu() {
+       return 0;
+}
+
+int _start() {
+       return 0;
+}
\ No newline at end of file
diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/main.kernel b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/main.kernel
new file mode 100755 (executable)
index 0000000..a99ed2a
Binary files /dev/null and b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/main.kernel differ
diff --git a/testing/kernel-cache-tests/vtable-patching-metaclass-alias/test.py b/testing/kernel-cache-tests/vtable-patching-metaclass-alias/test.py
new file mode 100644 (file)
index 0000000..78d4645
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/python2.7
+
+import os
+import KernelCollection
+
+# kxld has an implicit alias for a metaclass vtable entry.  Test that we also rewrite that alias
+
+def check(kernel_cache):
+    kernel_cache.buildKernelCollection("arm64", "/vtable-patching-metaclass-alias/main.kc", "/vtable-patching-metaclass-alias/main.kernel", "/vtable-patching-metaclass-alias/extensions", ["com.apple.foo"], [])
+    kernel_cache.analyze("/vtable-patching-metaclass-alias/main.kc", ["-layout", "-arch", "arm64"])
+
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["name"] == "__DATA_CONST"
+    assert kernel_cache.dictionary()["dylibs"][1]["segments"][2]["vmAddr"] == "0x14000"
+
+    # Find the address of the symbols to bind to
+    kernel_cache.analyze("/vtable-patching-metaclass-alias/main.kc", ["-symbols", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][0]["name"] == "__ZN15OSMetaClassBase8DispatchE5IORPC"
+    assert kernel_cache.dictionary()["dylibs"][0]["global-symbols"][0]["vmAddr"] == "0xC000"
+
+    # Check the fixups
+    kernel_cache.analyze("/vtable-patching-metaclass-alias/main.kc", ["-fixups", "-arch", "arm64"])
+    assert len(kernel_cache.dictionary()["fixups"]) == 2
+    assert kernel_cache.dictionary()["fixups"]["0x14000"] == "kc(0) + 0xC000"
+    assert kernel_cache.dictionary()["fixups"]["0x14008"] == "kc(0) + 0xC000"
+    assert len(kernel_cache.dictionary()["dylibs"]) == 2
+    assert kernel_cache.dictionary()["dylibs"][0]["name"] == "com.apple.kernel"
+    assert kernel_cache.dictionary()["dylibs"][0]["fixups"] == "none"
+    assert kernel_cache.dictionary()["dylibs"][1]["name"] == "com.apple.foo"
+    assert kernel_cache.dictionary()["dylibs"][1]["fixups"] == "none"
+
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-rename_section,__TEXT,__text,__TEXT_EXEC,__text -Wl,-e,__start -Wl,-pagezero_size,0x0 -Wl,-pie main.c -o main.kernel -Wl,-sectcreate,__LINKINFO,__symbolsets,SymbolSets.plist -Wl,-segprot,__LINKINFO,r--,r--
+# [~]> xcrun -sdk iphoneos.internal cc -arch arm64 -Wl,-kext -mkernel -nostdlib -Wl,-add_split_seg_info foo.c -o extensions/foo.kext/foo
+# [~]> rm -r extensions/foo.kext/*.ld
+
index a98dc22215fe51cfbad6c734ab24d6a583f1d7e9..42207bf28cd8813f30b8bc6f3ea67c72c4f085fa 100644 (file)
@@ -140,6 +140,7 @@ private:
     };
     void runLeaks();
     void dumpLogs();
+    void getLogsString(char** buffer);
     static uint8_t hexCharToUInt(const char hexByte, uint8_t* value);
     static uint64_t hexToUInt64(const char* startHexByte, const char** endHexByte);
     
@@ -154,9 +155,9 @@ private:
 };
 
 // Okay, this is tricky. We need something with roughly he semantics of a weak def, but without using weak defs as their presence
-// m ay impact certain tests. Instead we do the following:
+// may impact certain tests. Instead we do the following:
 //
-// 1. Embed a stuct containing a lock and a pointer to our global state object in eahc binary
+// 1. Embed a stuct containing a lock and a pointer to our global state object in each binary
 // 2. Once per binary we walk the entire image list looking for the first entry that also has state data
 // 3. If it has state we lock its initializaion lock, and if it is not initialized we initialize it
 // 4. We then copy the initalized pointer into our own state, and unlock the initializer lock
@@ -455,6 +456,29 @@ uint64_t TestState::hexToUInt64(const char* startHexByte, const char** endHexByt
     return retval;
 }
 
+void TestState::getLogsString(char** buffer)
+{
+    char *logBuf = NULL;
+    if ( logs.count() ) {
+        size_t idx = 0;
+        size_t bufSize = 0;
+        for (const auto& log : logs) {
+            size_t logSize = strlen(log);
+            bufSize += logSize + 2;  // \t and \n
+            logBuf = (char*)realloc(logBuf, bufSize);
+            strncpy(logBuf+idx, "\t", 1);
+            idx++;
+            strncpy(logBuf+idx, log, logSize);
+            idx += logSize;
+            strncpy(logBuf+idx, "\n", 1);
+            idx++;
+        }
+        logBuf = (char*)realloc(logBuf, bufSize + 1);
+        logBuf[bufSize] = '\0';
+        *buffer = logBuf;
+    }
+}
+
 TestState::TestState() : testName(__progname), logImmediate(false), logOnSuccess(false),  checkForLeaks(false), output(Console) {
     forEachEnvVar(environ, [this](const char* env, const char* val) {
         if (strcmp(env, "TEST_LOG_IMMEDIATE") == 0) {
@@ -492,46 +516,40 @@ TestState::TestState() : testName(__progname), logImmediate(false), logOnSuccess
     }
 }
 
-static std::atomic<TestState*>& getExecutableImageState() {
-    uint32_t imageCnt = _dyld_image_count();
-    for (uint32_t i = 0; i < imageCnt; ++i) {
-        #if __LP64__
-            const struct mach_header_64* mh = (const struct mach_header_64*)_dyld_get_image_header(i);
-        #else
-            const struct mach_header* mh = _dyld_get_image_header(i);
-        #endif
-        if (mh->filetype != MH_EXECUTE) {
-            continue;
-        }
-        size_t size = 0;
-        auto state = (std::atomic<TestState*>*)getsectiondata(mh, "__DATA", "__dyld_test", &size);
-        if (!state) {
-            fprintf(stderr, "Could not find test state in main executable TestState\n");
-            exit(0);
-        }
-        return *state;
-    }
-    fprintf(stderr, "Could not find test state in main executable\n");
-    exit(0);
-}
-
 GrowableArray<std::pair<mach_port_t, _dyld_test_crash_handler_t>>& TestState::getCrashHandlers() {
     return crashHandlers;
 }
 
 TestState* TestState::getState() {
     if (!sState) {
-        auto& state = getExecutableImageState();
-        if (state == nullptr) {
-            void *temp = malloc(sizeof(TestState));
-            auto newState = new (temp) TestState();
-            TestState* expected = nullptr;
-            if(!state.compare_exchange_strong(expected, newState)) {
-                newState->~TestState();
-                free(temp);
+        uint32_t imageCnt = _dyld_image_count();
+        for (uint32_t i = 0; i < imageCnt; ++i) {
+            #if __LP64__
+                const struct mach_header_64* mh = (const struct mach_header_64*)_dyld_get_image_header(i);
+            #else
+                const struct mach_header* mh = _dyld_get_image_header(i);
+            #endif
+            if (mh->filetype != MH_EXECUTE) {
+                continue;
             }
+            size_t size = 0;
+            auto state = (std::atomic<TestState*>*)getsectiondata(mh, "__DATA", "__dyld_test", &size);
+//            fprintf(stderr, "__dyld_test -> 0x%llx\n", state);
+            if (!state) {
+                fprintf(stderr, "Could not find test state in main executable TestState\n");
+                exit(0);
+            }
+            if (*state == nullptr) {
+                void *temp = malloc(sizeof(TestState));
+                auto newState = new (temp) TestState();
+                TestState* expected = nullptr;
+                if(!state->compare_exchange_strong(expected, newState)) {
+                    newState->~TestState();
+                    free(temp);
+                }
+            }
+            sState.store(*state);
         }
-        sState.store(state);
     }
     assert(sState != nullptr);
     return sState;
@@ -623,12 +641,21 @@ void TestState::_PASSV(const char* file, unsigned line, const char* format, va_l
                 printf("<plist version=\"1.0\">");
                 printf("<dict>");
                 printf("<key>PASS</key><true />");
+                if (logOnSuccess) {
+                    char *logBuffer = NULL;
+                    getLogsString(&logBuffer);
+                    if ( logBuffer != NULL ) {
+                        printf("<key>LOGS</key><string>%s</string>", logBuffer);
+                        free(logBuffer);
+                    }
+                }
                 printf("</dict>");
                 printf("</plist>");
             }
+            exit(0);
         });
-        exit(0);
     }
+    __builtin_unreachable();
 }
 
 void _PASS(const char* file, unsigned line, const char* format, ...)  {
@@ -664,7 +691,6 @@ void TestState::_FAILV(const char* file, unsigned line, const char* format, va_l
                 }
             }
         } else if (output == XCTest) {
-            char *buffer;
             printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
             printf("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">");
             printf("<plist version=\"1.0\">");
@@ -672,14 +698,22 @@ void TestState::_FAILV(const char* file, unsigned line, const char* format, va_l
             printf("<key>PASS</key><false />");
             printf("<key>FILE</key><string>%s</string>", file);
             printf("<key>LINE</key><integer>%u</integer>", line);
+            char *buffer;
             vasprintf(&buffer, format, args);
             printf("<key>INFO</key><string>%s</string>", buffer);
             free(buffer);
+            char *logBuffer = NULL;
+            getLogsString(&logBuffer);
+            if ( logBuffer != NULL ) {
+                printf("<key>LOGS</key><string>%s</string>", logBuffer);
+                free(logBuffer);
+            }
             printf("</dict>");
             printf("</plist>");
         }
+        exit(0);
     });
-    exit(0);
+    __builtin_unreachable();
 }
 
 void _FAIL(const char* file, unsigned line, const char* format, ...)  {
diff --git a/testing/run-static/jit_entitlement.plist b/testing/run-static/jit_entitlement.plist
new file mode 100644 (file)
index 0000000..9a1d0fb
--- /dev/null
@@ -0,0 +1,8 @@
+<?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>dynamic-codesigning</key>
+       <true/>
+</dict>
+</plist>
diff --git a/testing/run-static/run-static.cpp b/testing/run-static/run-static.cpp
new file mode 100644 (file)
index 0000000..a06ea63
--- /dev/null
@@ -0,0 +1,230 @@
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <mach-o/loader.h>
+#include <signal.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include <TargetConditionals.h>
+
+#include "ClosureFileSystemPhysical.h"
+#include "MachOAnalyzer.h"
+#include "MachOFile.h"
+
+#include "../testing/test-cases/kernel-test-runner.h"
+
+const bool isLoggingEnabled = false;
+
+int entryFunc(const TestRunnerFunctions* funcs);
+typedef __typeof(&entryFunc) EntryFuncTy;
+
+TestRunnerFunctions testFuncs = {
+    .version        = 1,
+    .mhs            = { nullptr, nullptr, nullptr, nullptr },
+    .basePointers   = { nullptr, nullptr, nullptr, nullptr },
+    .printf         = &::printf,
+    .exit           = &::exit,
+    .testPass       = &_PASS,
+    .testFail       = &_FAIL,
+    .testLog        = &_LOG,
+    .testTimeout    = &_TIMEOUT,
+};
+
+struct LoadedMachO {
+    const dyld3::MachOAnalyzer* ma              = nullptr;
+    // base pointer is the same as 'ma' when the binary has __TEXT first,
+    // but will point at where we mapped __DATA if building a reverse auxKC.
+    const void*                 basePointer     = nullptr;
+};
+
+LoadedMachO loadPath(const char* binaryPath) {
+    __block Diagnostics diag;
+    dyld3::closure::FileSystemPhysical fileSystem;
+    dyld3::closure::LoadedFileInfo info;
+    char realerPath[MAXPATHLEN];
+    __block bool printedError = false;
+    if (!fileSystem.loadFile(binaryPath, info, realerPath, ^(const char* format, ...) {
+        fprintf(stderr, "run-static: ");
+        va_list list;
+        va_start(list, format);
+        vfprintf(stderr, format, list);
+        va_end(list);
+        printedError = true;
+    })) {
+        if (!printedError )
+            fprintf(stderr, "run-static: %s: file not found\n", binaryPath);
+        exit(1);
+    }
+
+    const char* currentArchName = dyld3::MachOFile::currentArchName();
+    const dyld3::GradedArchs& currentArchs = dyld3::GradedArchs::forName(currentArchName);
+    __block const dyld3::MachOFile* mf = nullptr;
+    __block uint64_t sliceOffset = 0;
+    if ( dyld3::FatFile::isFatFile(info.fileContent) ) {
+        const dyld3::FatFile* ff = (dyld3::FatFile*)info.fileContent;
+        ff->forEachSlice(diag, info.fileContentLen, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType,
+                                                      const void* sliceStart, uint64_t sliceSize, bool& stop) {
+            const dyld3::MachOFile* sliceMF = (dyld3::MachOFile*)sliceStart;
+            if ( currentArchs.grade(sliceMF->cputype, sliceMF->cpusubtype, false) != 0 ) {
+                mf = sliceMF;
+                sliceOffset = (uint64_t)mf - (uint64_t)ff;
+                stop = true;
+                return;
+            }
+        });
+
+        if ( diag.hasError() ) {
+            fprintf(stderr, "Error: %s\n", diag.errorMessage());
+            return { nullptr, nullptr };
+        }
+
+        if ( mf == nullptr ) {
+            fprintf(stderr, "Could not use binary '%s' because it does not contain a slice compatible with host '%s'\n",
+                    binaryPath, currentArchName);
+            return { nullptr, nullptr };
+        }
+    } else {
+        mf = (dyld3::MachOFile*)info.fileContent;
+        if ( !mf->isMachO(diag, info.sliceLen) ) {
+            fprintf(stderr, "Could not use binary '%s' because '%s'\n", binaryPath, diag.errorMessage());
+            return { nullptr, nullptr };
+        }
+
+        if ( currentArchs.grade(mf->cputype, mf->cpusubtype, false) == 0 ) {
+            fprintf(stderr, "Could not use binary '%s' because 'incompatible arch'\n", binaryPath);
+            return { nullptr, nullptr };
+        }
+    }
+
+    if ( !mf->isFileSet() ) {
+        fprintf(stderr, "Could not use binary '%s' because 'it is not a static executable'\n", binaryPath);
+        return { nullptr, nullptr };
+    }
+
+    uint64_t mappedSize = ((dyld3::MachOAnalyzer*)mf)->mappedSize();
+    vm_address_t mappedAddr;
+    if ( ::vm_allocate(mach_task_self(), &mappedAddr, (size_t)mappedSize, VM_FLAGS_ANYWHERE) != 0 ) {
+        fprintf(stderr, "Could not use binary '%s' because 'vm allocation failure'\n", binaryPath);
+        return { nullptr, nullptr };
+    }
+
+    int fd = open(binaryPath, O_RDONLY);
+    if ( fd == 0 ) {
+        fprintf(stderr, "Could not open binary '%s' because '%s'\n", binaryPath, strerror(errno));
+        return { nullptr, nullptr };
+    }
+
+    __block uint64_t baseAddress = ~0ULL;
+    __block uint64_t textSegVMAddr = ~0ULL;
+    mf->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo& info, bool& stop) {
+        baseAddress = std::min(baseAddress, info.vmAddr);
+        if ( strcmp(info.segName, "__TEXT") == 0 ) {
+            textSegVMAddr = info.vmAddr;
+        }
+    });
+
+    uint64_t loadAddress = (uint64_t)mappedAddr;
+    if ( isLoggingEnabled ) {
+        fprintf(stderr, "Mapping binary built at 0x%llx to 0x%llx\n", baseAddress, loadAddress);
+    }
+    mf->forEachSegment(^(const dyld3::MachOFile::SegmentInfo &info, bool &stop) {
+        uint64_t requestedLoadAddress = info.vmAddr - baseAddress + loadAddress;
+        if ( isLoggingEnabled )
+            fprintf(stderr, "Mapping %p: %s with perms %d\n", (void*)requestedLoadAddress, info.segName, info.protections);
+        if ( info.vmSize == 0 )
+            return;
+        size_t readBytes = pread(fd, (void*)requestedLoadAddress, (uintptr_t)info.fileSize, sliceOffset + info.fileOffset);
+        if ( readBytes != info.fileSize ) {
+            fprintf(stderr, "Didn't read enough bytes\n");
+            exit(1);
+        }
+        // __DATA_CONST is read-only when we actually run live, but this test runner fixes up __DATA_CONST after this vm_protect
+        // For now just don't make __DATA_CONST read only
+        uint32_t protections = info.protections;
+        if ( !strcmp(info.segName, "__DATA_CONST") )
+            protections = VM_PROT_READ | VM_PROT_WRITE;
+        const bool setCurrentPermissions = false;
+        kern_return_t r = vm_protect(mach_task_self(), (vm_address_t)requestedLoadAddress, (uintptr_t)info.vmSize, setCurrentPermissions, protections);
+        if ( r != KERN_SUCCESS ) {
+            diag.error("vm_protect didn't work because %d", r);
+            stop = true;
+            return;
+        }
+    });
+
+    if ( diag.hasError() ) {
+        fprintf(stderr, "Error: %s\n", diag.errorMessage());
+        return { nullptr, nullptr };
+    }
+
+    if ( textSegVMAddr != baseAddress ) {
+        // __DATA is first.  ma should still point to __TEXT
+        const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)(mappedAddr + textSegVMAddr - baseAddress);
+        if ( !ma->validMachOForArchAndPlatform(diag, (size_t)mappedSize, binaryPath, currentArchs, dyld3::Platform::unknown, false) ) {
+            fprintf(stderr, "Error: %s\n", diag.errorMessage());
+            exit(1);
+        }
+        return { ma, (const void*)mappedAddr };
+    }
+
+    // __TEXT is first, so ma and base address are the same
+    const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)mappedAddr;
+    if ( !ma->validMachOForArchAndPlatform(diag, (size_t)mappedSize, binaryPath, currentArchs, dyld3::Platform::unknown, false) ) {
+        fprintf(stderr, "Error: %s\n", diag.errorMessage());
+        exit(1);
+    }
+    return { ma, (const void*)mappedAddr };
+}
+
+int main(int argc, const char * argv[]) {
+    bool unsupported = false;
+#if TARGET_OS_WATCH
+    // HACK: Watch archs are not supported right now, so just return
+    unsupported = true;
+#endif
+    if ( unsupported ) {
+        funcs = &testFuncs;
+        PASS("Success");
+    }
+
+    if ( (argc < 2) || (argc > 5) ) {
+        fprintf(stderr, "Usage: run-static *path to static binary* [- - *path to auc kc*]\n");
+        return 1;
+    }
+
+    for (unsigned i = 1; i != argc; ++i) {
+        if ( !strcmp(argv[i], "-") )
+            continue;
+        LoadedMachO macho = loadPath(argv[i]);
+        if ( macho.ma == nullptr )
+            return 1;
+        testFuncs.mhs[i - 1] = macho.ma;
+        testFuncs.basePointers[i - 1] = macho.basePointer;
+    }
+
+    uint64_t entryOffset    = 0;
+    bool usesCRT            = false;
+    const dyld3::MachOAnalyzer* ma = (const dyld3::MachOAnalyzer*)testFuncs.mhs[0];
+    if ( !ma->getEntry(entryOffset, usesCRT) ) {
+        fprintf(stderr, "Could not use binary '%s' because 'no entry defined'\n", argv[1]);
+        return 1;
+    }
+
+    EntryFuncTy entryFunc = (EntryFuncTy)((uint8_t*)testFuncs.mhs[0] + entryOffset);
+#if __has_feature(ptrauth_calls)
+    entryFunc = (EntryFuncTy)__builtin_ptrauth_sign_unauthenticated((void*)entryFunc, 0, 0);
+#endif
+    fprintf(stderr, "Entering static binary at %p\n", entryFunc);
+    //kill(getpid(), SIGSTOP);
+    int returnCode = entryFunc(&testFuncs);
+    if ( returnCode != 0 ) {
+        fprintf(stderr, "Binary '%s' returned non-zero value %d\n", argv[1], returnCode);
+        return returnCode;
+    }
+    return 0;
+}
index f2597215c8eeca6db405ca7c74741fff01d73feb..771cb46f856b8629e1aa08dc54c9f83c23c1b910 100644 (file)
@@ -1,7 +1,7 @@
-// BUILD_ONLY: MacOSX
+// BUILD(macos):  $CC zzz.c -dynamiclib -o $BUILD_DIR/libzzz.dylib -install_name $RUN_DIR/libzzz.dylib
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/NSAddImage-basic.exe -Wno-deprecated-declarations
 
-// BUILD:  $CC zzz.c -dynamiclib -o $BUILD_DIR/libzzz.dylib -install_name $RUN_DIR/libzzz.dylib
-// BUILD:  $CC main.c            -o $BUILD_DIR/NSAddImage-basic.exe -Wno-deprecated-declarations
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  ./NSAddImage-basic.exe $RUN_DIR/libzzz.dylib
 // RUN:  ./NSAddImage-basic.exe libzzz.dylib
index 0dada7d4258c3165701640e63030404a4f69332d..d970b06c149f6846abca40df7913af143f56078f 100644 (file)
@@ -1,6 +1,8 @@
-// BUILD_ONLY: MacOSX
+// BUILD(macos):  $CXX main.cpp          -o $BUILD_DIR/NSAddImage-fail.exe -Wno-deprecated-declarations -DRUN_DIR="$RUN_DIR"
 
-// BUILD:  $CXX main.cpp          -o $BUILD_DIR/NSAddImage-fail.exe -Wno-deprecated-declarations -DRUN_DIR="$RUN_DIR"
+// BUILD(ios,tvos,watchos,bridgeos):
+
+// NO_CRASH_LOG: NSAddImage-fail.exe
 
 // RUN:  ./NSAddImage-fail.exe return
 // RUN:  ./NSAddImage-fail.exe abort
@@ -42,9 +44,9 @@ int main(int argc, const char* argv[], const char* envp[], const char* apple[])
         process.set_env(env);
         process.set_crash_handler(^(task_t task) {
             LOG("Crash for task=%u", task);
-            vm_address_t corpse_data;
-            uint32_t corpse_size;
-            if (task_map_corpse_info(mach_task_self(), task, &corpse_data, &corpse_size) != KERN_SUCCESS) {
+            mach_vm_address_t corpse_data;
+            mach_vm_size_t corpse_size;
+            if (task_map_corpse_info_64(mach_task_self(), task, &corpse_data, &corpse_size) != KERN_SUCCESS) {
                 FAIL("Could not read corpse data");
             }
             kcdata_iter_t autopsyData = kcdata_iter((void*)corpse_data, corpse_size);
index 0a24cb854b1e7f7961f640abf363691906cbd3a2..d9aff16df43b396d63e07b494ec98ea516dc6eb9 100644 (file)
@@ -1,6 +1,6 @@
-// BUILD_ONLY: MacOSX
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/NSAddImage-loaded.exe -Wno-deprecated-declarations
 
-// BUILD:  $CC main.c            -o $BUILD_DIR/NSAddImage-loaded.exe -Wno-deprecated-declarations
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  ./NSAddImage-loaded.exe return
 
index 70fa3f024b88e7440b243c71056e4863383f62a6..2943360032c2ecc049c15d9fe09b8eeef78396ee 100644 (file)
@@ -1,6 +1,6 @@
-// BUILD_ONLY: MacOSX
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/NSAddressOfSymbol-basic.exe -Wno-deprecated-declarations
 
-// BUILD:  $CC main.c            -o $BUILD_DIR/NSAddressOfSymbol-basic.exe -Wno-deprecated-declarations
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  ./NSAddressOfSymbol-basic.exe
 
index b6eb2e043f6005833abe1c33902a788a677659d1..aced13bc384f2d2769b1db2b019e242ff8ee332f 100644 (file)
@@ -1,7 +1,7 @@
-// BUILD_ONLY: MacOSX
+// BUILD(macos):  $CC main.c  -o $BUILD_DIR/NSCreateObjectFileImageFromFile-basic.exe -Wno-deprecated-declarations
+// BUILD(macos):  $CC foo.c   -o $BUILD_DIR/foo.bundle -bundle
 
-// BUILD:  $CC main.c  -o $BUILD_DIR/NSCreateObjectFileImageFromFile-basic.exe -Wno-deprecated-declarations
-// BUILD:  $CC foo.c   -o $BUILD_DIR/foo.bundle -bundle
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  ./NSCreateObjectFileImageFromFile-basic.exe $RUN_DIR/foo.bundle
 
index d7e6f9822804ef86ea65fda0379ecca707749471..91fd66c2b58f71197d91f2e5d914af1025311eb6 100644 (file)
@@ -1,7 +1,7 @@
-// BUILD_ONLY: MacOSX
+// BUILD(macos):  $CXX main.cpp  -o $BUILD_DIR/NSCreateObjectFileImageFromFile-stress.exe -Wno-deprecated-declarations
+// BUILD(macos):  $CC  foo.c     -o $BUILD_DIR/foo.bundle -bundle
 
-// BUILD:  $CXX main.cpp  -o $BUILD_DIR/NSCreateObjectFileImageFromFile-stress.exe -Wno-deprecated-declarations
-// BUILD:  $CC  foo.c     -o $BUILD_DIR/foo.bundle -bundle
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  ./NSCreateObjectFileImageFromFile-stress.exe $RUN_DIR/foo.bundle
 
index b9944ffff414404fa827c9d26b970753fe6f5216..bbc97174a0cc08c82c1d953cc4431c70e9c7ed8a 100644 (file)
@@ -1,7 +1,7 @@
-// BUILD_ONLY: MacOSX
+// BUILD(macos):  $CC main.c  -o $BUILD_DIR/NSCreateObjectFileImageFromMemory-basic.exe -Wno-deprecated-declarations
+// BUILD(macos):  $CC foo.c   -o $BUILD_DIR/foo.bundle -bundle
 
-// BUILD:  $CC main.c  -o $BUILD_DIR/NSCreateObjectFileImageFromMemory-basic.exe -Wno-deprecated-declarations
-// BUILD:  $CC foo.c   -o $BUILD_DIR/foo.bundle -bundle
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  ./NSCreateObjectFileImageFromMemory-basic.exe $RUN_DIR/foo.bundle
 
index 7cc1d46deddd2845129029d11aa95666ac451cc3..4ef6ce329c161a92e6406fd50ec6c528a2370f07 100644 (file)
@@ -1,6 +1,6 @@
-// BUILD_ONLY: MacOSX
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/NSLookupSymbolInImage-basic.exe -Wno-deprecated-declarations
 
-// BUILD:  $CC main.c            -o $BUILD_DIR/NSLookupSymbolInImage-basic.exe -Wno-deprecated-declarations
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  ./NSLookupSymbolInImage-basic.exe
 
index d9ebb7317a2a94c365c9ab7c8d40b94a2221272f..3d74b42f77848e7c8104efb78eb48b017c64df0c 100644 (file)
 
 #include "test_support.h"
 
+
+static bool objcOptimizedByDyld() {
+    extern const uint32_t objcInfo[]  __asm("section$start$__DATA_CONST$__objc_imageinfo");
+    return (objcInfo[1] & 0x80);
+}
+
+static bool haveDyldCache() {
+    size_t unusedCacheLen;
+    return (_dyld_get_shared_cache_range(&unusedCacheLen) != NULL);
+}
+
 // All the libraries have a copy of NSString
 @interface NSString : NSObject
 @end
@@ -133,23 +144,17 @@ void testDuplicate(const char* className, Class nonCacheClass) {
 }
 
 int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
-  // This API is only available with dyld3 and shared caches.  If we have dyld2 then don't do anything
-  const char* testDyldMode = getenv("TEST_DYLD_MODE");
-  assert(testDyldMode);
-
-  size_t sharedCacheLen = 0;
-  const void* sharedCacheStart = 0;
-  sharedCacheStart = _dyld_get_shared_cache_range(&sharedCacheLen);
-  bool haveSharedCache = sharedCacheStart != NULL;
-  if (!strcmp(testDyldMode, "2") || !haveSharedCache) {
+    size_t sharedCacheLen = 0;
+    const void* sharedCacheStart = _dyld_get_shared_cache_range(&sharedCacheLen);
+    if (!objcOptimizedByDyld() || (sharedCacheStart==NULL)) {
     __block bool sawClass = false;
-    _dyld_for_each_objc_class("NSString", ^(void* classPtr, bool isLoaded, bool* stop) {
+    _dyld_for_each_objc_class("DyldClass", ^(void* classPtr, bool isLoaded, bool* stop) {
       sawClass = true;
     });
     if (sawClass) {
       FAIL("dyld2 shouldn't see any classes");
     }
-    PASS("dyld2 or no shared cache)");
+    PASS("no shared cache or no dyld optimized objc");
   }
 
   // Check that NSString comes from Foundation as the shared cache should win here.
index cd029cb4d716f8b9648841024417342aaf5e9bc4..02aca79b6ec104f0eca2355f366ec67eee11d31a 100644 (file)
@@ -1,11 +1,11 @@
-// BUILD_ONLY: MacOSX
+// BUILD(macos):  $CC missing.m -dynamiclib -o $BUILD_DIR/libmissing.dylib -install_name $RUN_DIR/libmissing.dylib -lobjc -Wl,-fixup_chains
+// BUILD(macos):  $CC lib1.m -dynamiclib -o $BUILD_DIR/liblinked1.dylib -install_name $RUN_DIR/liblinked1.dylib -lobjc -Wl,-fixup_chains
+// BUILD(macos):  $CC lib2.m -dynamiclib -o $BUILD_DIR/liblinked2.dylib -install_name $RUN_DIR/liblinked2.dylib -lobjc $BUILD_DIR/libmissing.dylib -Wl,-fixup_chains
+// BUILD(macos):  $CXX main.mm -o $BUILD_DIR/_dyld_for_each_objc_class-missing-weak-chained.exe -lobjc $BUILD_DIR/liblinked1.dylib $BUILD_DIR/liblinked2.dylib $BUILD_DIR/libmissing.dylib -Wl,-fixup_chains -lc++
 
-// BUILD:  $CC missing.m -dynamiclib -o $BUILD_DIR/libmissing.dylib -install_name $BUILD_DIR/libmissing.dylib -lobjc -Wl,-fixup_chains
-// BUILD:  $CC lib1.m -dynamiclib -o $BUILD_DIR/liblinked1.dylib -install_name $RUN_DIR/liblinked1.dylib -lobjc -Wl,-fixup_chains
-// BUILD:  $CC lib2.m -dynamiclib -o $BUILD_DIR/liblinked2.dylib -install_name $RUN_DIR/liblinked2.dylib -lobjc $BUILD_DIR/libmissing.dylib -Wl,-fixup_chains
-// BUILD:  $CXX main.mm -o $BUILD_DIR/_dyld_for_each_objc_class-missing-weak-chained.exe -lobjc $BUILD_DIR/liblinked1.dylib $BUILD_DIR/liblinked2.dylib $BUILD_DIR/libmissing.dylib -Wl,-fixup_chains -lc++
+// BUILD(macos): $SKIP_INSTALL $BUILD_DIR/libmissing.dylib
 
-// BUILD: $SKIP_INSTALL $BUILD_DIR/libmissing.dylib
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  ./_dyld_for_each_objc_class-missing-weak-chained.exe
 
 
 #include "test_support.h"
 
+static bool objcOptimizedByDyld() {
+    extern const uint32_t objcInfo[]  __asm("section$start$__DATA_CONST$__objc_imageinfo");
+    return (objcInfo[1] & 0x80);
+}
+
+static bool haveDyldCache() {
+    size_t unusedCacheLen;
+    return (_dyld_get_shared_cache_range(&unusedCacheLen) != NULL);
+}
+
 // All the libraries have a copy of DyldClass
 @interface DyldClass : NSObject
 @end
@@ -67,13 +77,7 @@ static bool gotDyldClassMain = false;
 static bool gotDyldClassLinked1 = false;
 
 int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
-  // This API is only available with dyld3 and shared caches.  If we have dyld2 then don't do anything
-  const char* testDyldMode = getenv("TEST_DYLD_MODE");
-  assert(testDyldMode);
-
-  size_t unusedCacheLen;
-  bool haveSharedCache = _dyld_get_shared_cache_range(&unusedCacheLen) != 0;
-  if (!strcmp(testDyldMode, "2") || !haveSharedCache) {
+   if (!objcOptimizedByDyld() || !haveDyldCache()) {
     __block bool sawClass = false;
     _dyld_for_each_objc_class("DyldClass", ^(void* classPtr, bool isLoaded, bool* stop) {
       sawClass = true;
@@ -81,7 +85,7 @@ int main(int argc, const char* argv[], const char* envp[], const char* apple[])
     if (sawClass) {
       FAIL("dyld2 shouldn't see any classes");
     }
-    PASS("Success");
+    PASS("no shared cache or no dyld optimized objc");
   }
 
   // Make sure libmissing.dylib is actually missing
index 4752f008e96b36cd728a59c247618a93e337b303..161212953de075bb83ac6f586e27d7a9a66d3c5e 100644 (file)
@@ -1,5 +1,5 @@
 
-// BUILD:  $CC missing.m -dynamiclib -o $BUILD_DIR/libmissing.dylib -install_name $BUILD_DIR/libmissing.dylib -lobjc
+// BUILD:  $CC missing.m -dynamiclib -o $BUILD_DIR/libmissing.dylib -install_name $RUN_DIR/libmissing.dylib -lobjc
 // BUILD:  $CC lib1.m -dynamiclib -o $BUILD_DIR/liblinked1.dylib -install_name $RUN_DIR/liblinked1.dylib -lobjc
 // BUILD:  $CC lib2.m -dynamiclib -o $BUILD_DIR/liblinked2.dylib -install_name $RUN_DIR/liblinked2.dylib -lobjc $BUILD_DIR/libmissing.dylib
 // BUILD:  $CXX main.mm -o $BUILD_DIR/_dyld_for_each_objc_class-missing-weak.exe -lobjc $BUILD_DIR/liblinked1.dylib $BUILD_DIR/liblinked2.dylib $BUILD_DIR/libmissing.dylib -lc++
 
 #include "test_support.h"
 
+static bool objcOptimizedByDyld() {
+    extern const uint32_t objcInfo[]  __asm("section$start$__DATA_CONST$__objc_imageinfo");
+    return (objcInfo[1] & 0x80);
+}
+
+static bool haveDyldCache() {
+    size_t unusedCacheLen;
+    return (_dyld_get_shared_cache_range(&unusedCacheLen) != NULL);
+}
+
 // All the libraries have a copy of DyldClass
 @interface DyldClass : NSObject
 @end
@@ -66,13 +76,7 @@ static bool gotDyldClassMain = false;
 static bool gotDyldClassLinked1 = false;
 
 int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
-  // This API is only available with dyld3 and shared caches.  If we have dyld2 then don't do anything
-  const char* testDyldMode = getenv("TEST_DYLD_MODE");
-  assert(testDyldMode);
-
-  size_t unusedCacheLen;
-  bool haveSharedCache = _dyld_get_shared_cache_range(&unusedCacheLen) != 0;
-  if (!strcmp(testDyldMode, "2") || !haveSharedCache) {
+   if (!objcOptimizedByDyld() || !haveDyldCache()) {
     __block bool sawClass = false;
     _dyld_for_each_objc_class("DyldClass", ^(void* classPtr, bool isLoaded, bool* stop) {
       sawClass = true;
@@ -80,7 +84,7 @@ int main(int argc, const char* argv[], const char* envp[], const char* apple[])
     if (sawClass) {
       FAIL("dyld2 shouldn't see any classes");
     }
-    PASS("dyld2 or no shared cache");
+    PASS("no shared cache or no dyld optimized objc");
   }
 
   // Make sure libmissing.dylib is actually missing
index 083e94d440da2cd90bd598c26dbea7449dbdb6cd..6aa6c18d71e6a603a3021abc5ec5788a31255e33 100644 (file)
@@ -56,14 +56,19 @@ static bool gotDyldClassMain = false;
 static bool gotDyldClassLinked = false;
 static bool gotDyldClassLinked2 = false;
 
-int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
-  // This API is only available with dyld3 and shared caches.  If we have dyld2 then don't do anything
-  const char* testDyldMode = getenv("TEST_DYLD_MODE");
-  assert(testDyldMode);
 
-  size_t unusedCacheLen;
-  bool haveSharedCache = _dyld_get_shared_cache_range(&unusedCacheLen) != 0;
-  if (!strcmp(testDyldMode, "2") || !haveSharedCache) {
+static bool objcOptimizedByDyld() {
+    extern const uint32_t objcInfo[]  __asm("section$start$__DATA_CONST$__objc_imageinfo");
+    return (objcInfo[1] & 0x80);
+}
+
+static bool haveDyldCache() {
+    size_t unusedCacheLen;
+    return (_dyld_get_shared_cache_range(&unusedCacheLen) != NULL);
+}
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+   if (!objcOptimizedByDyld() || !haveDyldCache()) {
     __block bool sawClass = false;
     _dyld_for_each_objc_class("DyldClass", ^(void* classPtr, bool isLoaded, bool* stop) {
       sawClass = true;
@@ -71,7 +76,7 @@ int main(int argc, const char* argv[], const char* envp[], const char* apple[])
     if (sawClass) {
       FAIL("dyld2 shouldn't see any classes");
     }
-    PASS("dyld2 or no shared cache)");
+    PASS("no shared cache or no dyld optimized objc");
   }
 
   // Check that DyldClass comes from liblinked2 as it is last in load order
index d80aea26d32471d9309a100d77f066abfe630f81..02f2ad67df42e46e4c9a475ee84a02d0693318a8 100644 (file)
 
 #include "test_support.h"
 
+static bool objcOptimizedByDyld() {
+    extern const uint32_t objcInfo[]  __asm("section$start$__DATA_CONST$__objc_imageinfo");
+    return (objcInfo[1] & 0x80);
+}
+
+static bool haveDyldCache() {
+    size_t unusedCacheLen;
+    return (_dyld_get_shared_cache_range(&unusedCacheLen) != NULL);
+}
+
 // All the libraries have a copy of DyldProtocol
 @protocol DyldProtocol
 @end
@@ -53,21 +63,15 @@ static bool isInImage(void* ptr, const char* name) {
 }
 
 int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
-  // This API is only available with dyld3 and shared caches.  If we have dyld2 then don't do anything
-  const char* testDyldMode = getenv("TEST_DYLD_MODE");
-  assert(testDyldMode);
-
-  size_t unusedCacheLen;
-  bool haveSharedCache = _dyld_get_shared_cache_range(&unusedCacheLen) != 0;
-  if (!strcmp(testDyldMode, "2") || !haveSharedCache) {
-    __block bool sawProtocol = false;
-    _dyld_for_each_objc_protocol("DyldProtocol", ^(void* protocolPtr, bool isLoaded, bool* stop) {
-      sawProtocol = true;
+   if (!objcOptimizedByDyld() || !haveDyldCache()) {
+    __block bool sawClass = false;
+    _dyld_for_each_objc_class("DyldClass", ^(void* classPtr, bool isLoaded, bool* stop) {
+      sawClass = true;
     });
-    if (sawProtocol) {
-      FAIL("dyld2 shouldn't see any protocols");
+    if (sawClass) {
+      FAIL("dyld2 shouldn't see any classes");
     }
-    PASS("dyld2 or no shared cache");
+    PASS("no shared cache or no dyld optimized objc");
   }
 
   // Check that DyldProtocol comes from liblinked2 as it is last in load order
@@ -139,5 +143,18 @@ int main(int argc, const char* argv[], const char* envp[], const char* apple[])
     FAIL("_dyld_for_each_objc_protocol should have returned DyldMainProtocol from main.exe");
   }
 
+#if __has_feature(ptrauth_calls)
+  // Check the ISA was signed correctly on arm64e
+  id dyldMainProtocol = @protocol(DyldMainProtocol);
+  void* originalISA = *(void **)dyldMainProtocol;
+  void* strippedISA = __builtin_ptrauth_strip(originalISA, ptrauth_key_asda);
+  uint64_t discriminator = __builtin_ptrauth_blend_discriminator((void*)dyldMainProtocol, 27361);
+  void* signedISA = __builtin_ptrauth_sign_unauthenticated((void*)strippedISA, 2, discriminator);
+  if ( originalISA != signedISA ) {
+    FAIL("_dyld_for_each_objc_protocol DyldMainProtocol ISA is not signed correctly: %p vs %p",
+         originalISA, signedISA);
+  }
+#endif
+
   PASS("Success");
 }
diff --git a/testing/test-cases/_dyld_get_image_name-cache-symlink.dtest/foo.c b/testing/test-cases/_dyld_get_image_name-cache-symlink.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_get_image_name-cache-symlink.dtest/main.c b/testing/test-cases/_dyld_get_image_name-cache-symlink.dtest/main.c
new file mode 100644 (file)
index 0000000..4d065e6
--- /dev/null
@@ -0,0 +1,41 @@
+
+// BUILD:  $CC foo.c -dynamiclib -install_name /System/Library/Frameworks/IOKit.framework/IOKit -o $BUILD_DIR/libfoo.dylib
+// BUILD:  $CC main.c -o $BUILD_DIR/_dyld_get_image_name-cache-symlink.exe $BUILD_DIR/libfoo.dylib
+
+// RUN:  DYLD_LIBRARY_PATH=/usr/lib/system/introspection ./_dyld_get_image_name-cache-symlink.exe
+
+
+#include <stdio.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld_priv.h>
+
+#include "test_support.h"
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    // walk images to see if path was converted to real path
+    const char* foundPath = NULL;
+    int count = _dyld_image_count();
+    for (int i=0; i < count; ++i) {
+        const char* path = _dyld_get_image_name(i);
+        LOG("path[%2d]=%s", i, path);
+        if ( strstr(path, "/IOKit") != NULL ) {
+            if ( foundPath == NULL ) {
+                foundPath = path;
+            }
+            else {
+                FAIL("More than one libfoo found");
+            }
+        }
+    }
+    if ( foundPath == NULL ) {
+        FAIL("No IOKit found");
+    }
+    if ( strcmp(foundPath, "/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit") != 0 ) {
+        FAIL("Path is symlink not real path: %s", foundPath);
+    }
+
+    PASS("Success");
+}
+
index 5a7a735586d7f2c024e29d9f487330d1f0202770..2a0872e6cb0c9236dd43a51f11faf2a763e087b5 100644 (file)
@@ -1,6 +1,6 @@
-// BUILD_ONLY: MacOSX
+// BUILD(macos):  $CC main.m -o $BUILD_DIR/_dyld_get_objc_selector-chained.exe -lobjc -Wl,-fixup_chains
 
-// BUILD:  $CC main.m -o $BUILD_DIR/_dyld_get_objc_selector-chained.exe -lobjc -Wl,-fixup_chains
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  ./_dyld_get_objc_selector-chained.exe
 
diff --git a/testing/test-cases/_dyld_get_prog_image_header.dtest/foo.c b/testing/test-cases/_dyld_get_prog_image_header.dtest/foo.c
new file mode 100644 (file)
index 0000000..0f6c13b
--- /dev/null
@@ -0,0 +1,5 @@
+int foo()
+{
+    return 0;
+}
+
diff --git a/testing/test-cases/_dyld_get_prog_image_header.dtest/main.c b/testing/test-cases/_dyld_get_prog_image_header.dtest/main.c
new file mode 100644 (file)
index 0000000..f372a89
--- /dev/null
@@ -0,0 +1,26 @@
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib
+// BUILD:  $CC main.c            -o $BUILD_DIR/_dyld_get_prog_image_header.exe
+// BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/_dyld_get_prog_image_header.exe
+
+// RUN:  ./_dyld_get_prog_image_header.exe
+// RUN:  DYLD_INSERT_LIBRARIES=libfoo.dylib  ./_dyld_get_prog_image_header.exe
+
+
+#include <mach-o/dyld_priv.h>
+
+#include "test_support.h"
+
+int main(int argc, const char* argv[]) {
+    uint32_t i = 0;
+    const struct mach_header* mhA = NULL;
+    do {
+        mhA = _dyld_get_image_header(i++);
+    }
+    while (mhA->filetype != MH_EXECUTE);
+    
+    const struct mach_header* mhB = _dyld_get_prog_image_header();
+    if ( mhA != mhB )
+        FAIL("Incorrect mach header address (%p)", mhA);
+
+    PASS("Success");
+}
diff --git a/testing/test-cases/_dyld_launch_mode.dtest/main.c b/testing/test-cases/_dyld_launch_mode.dtest/main.c
new file mode 100644 (file)
index 0000000..097ae7c
--- /dev/null
@@ -0,0 +1,46 @@
+
+// BUILD:  $CC main.c  -o $BUILD_DIR/dyld_launch_mode.exe
+
+// RUN:  ./dyld_launch_mode.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h> 
+#include <mach-o/dyld_priv.h>
+#include <dlfcn.h>
+
+#include "test_support.h"
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[])
+{
+    const char* modeStr = getenv("DYLD_USE_CLOSURES");
+    if ( modeStr == NULL ) {
+        FAIL("dyld_launch_mode: DYLD_USE_CLOSURES env var not set");
+    }
+
+    uint32_t expectedFlags;
+    uint32_t launchFlags = _dyld_launch_mode();
+    fprintf(stderr, "launchFlags=0x%08x\n", launchFlags);
+
+    if ( strcmp(modeStr, "0") == 0 ) {
+        expectedFlags = 0;
+    }
+    else if ( strcmp(modeStr, "1") == 0 ) {
+        expectedFlags = DYLD_LAUNCH_MODE_USING_CLOSURE | DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH;
+    }
+    else if ( strcmp(modeStr, "2") == 0 ) {
+        expectedFlags = DYLD_LAUNCH_MODE_USING_CLOSURE | DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH | DYLD_LAUNCH_MODE_MINIMAL_CLOSURE;
+    }
+    else {
+        FAIL("dyld_launch_mode: DYLD_USE_CLOSURES value unknown");
+    }
+
+
+    if ( launchFlags == expectedFlags )
+        PASS("dyld_launch_mode");
+    else
+        FAIL("dyld_launch_mode: expected flags to be 0x%08X but were 0x%08X", expectedFlags, launchFlags);
+
+    return 0;
+}
+
diff --git a/testing/test-cases/_dyld_shared_cache_contains_path.dtest/main.c b/testing/test-cases/_dyld_shared_cache_contains_path.dtest/main.c
new file mode 100644 (file)
index 0000000..36c3e17
--- /dev/null
@@ -0,0 +1,45 @@
+
+// BUILD:  $CC main.c            -o $BUILD_DIR/_dyld_shared_cache_contains_path.exe
+
+// RUN:  ./_dyld_shared_cache_contains_path.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h> 
+#include <mach-o/dyld_priv.h>
+#include <dlfcn.h>
+
+#include "test_support.h"
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[])
+{
+    size_t length;
+    bool hasCache = ( _dyld_get_shared_cache_range(&length) != NULL );
+
+    if ( _dyld_shared_cache_contains_path("/usr/lib/libSystem.B.dylib") != hasCache ) {
+        if ( hasCache )
+            FAIL("libSystem.B.dylib is not in dyld cache");
+        else
+            FAIL("no cache, but libSystem.B.dylib is in dyld cache");
+    }
+    
+    if ( _dyld_shared_cache_contains_path("/System/Library/Frameworks/Foundation.framework/Foundation") != hasCache ) {
+         if ( hasCache )
+             FAIL("Foundation.framework is not in dyld cache");
+         else
+             FAIL("no cache, but Foundation.framework is in dyld cache");
+     }
+
+#if TARGET_OS_OSX
+    if ( _dyld_shared_cache_contains_path("/System/Library/Frameworks/Foundation.framework/Versions/Current/Foundation") != hasCache ) {
+        if ( hasCache )
+            FAIL("Current/Foundation.framework is not in dyld cache");
+        else
+            FAIL("no cache, but Current/Foundation.framework is in dyld cache");
+     }
+#endif
+
+
+    PASS("SUCCESS");
+}
+
diff --git a/testing/test-cases/_dyld_shared_cache_real_path.dtest/main.c b/testing/test-cases/_dyld_shared_cache_real_path.dtest/main.c
new file mode 100644 (file)
index 0000000..55ec8d2
--- /dev/null
@@ -0,0 +1,81 @@
+
+// BUILD:  $CC main.c            -o $BUILD_DIR/_dyld_shared_cache_real_path.exe
+
+// RUN:  ./_dyld_shared_cache_real_path.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h> 
+#include <mach-o/dyld_priv.h>
+#include <dlfcn.h>
+
+#include "test_support.h"
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[])
+{
+    size_t length;
+    bool hasCache = ( _dyld_get_shared_cache_range(&length) != NULL );
+    if ( hasCache ) {
+        const char* path = _dyld_shared_cache_real_path("/usr/lib/libSystem.dylib");
+        if ( path == NULL )
+            FAIL("libSystem.dylib is not in dyld cache");
+        else if ( strcmp(path, "/usr/lib/libSystem.B.dylib") != 0 )
+            FAIL("libSystem.B.dylib != %s", path);
+
+#if TARGET_OS_OSX
+        // actual path
+        path = _dyld_shared_cache_real_path("/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation");
+        if ( path == NULL )
+            FAIL("Foundation is not in dyld cache");
+        else if ( strcmp(path, "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation") != 0 )
+            FAIL("Foundation != %s", path);
+
+        // symlink inside the shared cache
+        path = _dyld_shared_cache_real_path("/System/Library/Frameworks/Foundation.framework/Foundation");
+        if ( path == NULL )
+            FAIL("Foundation is not in dyld cache");
+        else if ( strcmp(path, "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation") != 0 )
+            FAIL("Foundation != %s", path);
+
+        // symlink not in the shared cache (as we don't handle directory symlinks today)
+        path = _dyld_shared_cache_real_path("/System/Library/Frameworks/Foundation.framework/Versions/Current/Foundation");
+        if ( path == NULL )
+            FAIL("Foundation is not in dyld cache");
+        else if ( strcmp(path, "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation") != 0 )
+            FAIL("Foundation != %s", path);
+#endif
+    } else {
+        const char* path = _dyld_shared_cache_real_path("/usr/lib/libSystem.B.dylib");
+        if ( path != NULL )
+            FAIL("no cache, but libSystem.B.dylib is in dyld cache");
+    }
+
+#if 0
+    if (  != hasCache ) {
+        if ( hasCache )
+            FAIL("libSystem.B.dylib is not in dyld cache");
+        else
+            FAIL("no cache, but libSystem.B.dylib is in dyld cache");
+    }
+    
+    if ( _dyld_shared_cache_real_path("/System/Library/Frameworks/Foundation.framework/Foundation") != hasCache ) {
+         if ( hasCache )
+             FAIL("Foundation.framework is not in dyld cache");
+         else
+             FAIL("no cache, but Foundation.framework is in dyld cache");
+     }
+
+#if TARGET_OS_OSX
+    if ( _dyld_shared_cache_real_path("/System/Library/Frameworks/Foundation.framework/Versions/Current/Foundation") != hasCache ) {
+        if ( hasCache )
+            FAIL("Current/Foundation.framework is not in dyld cache");
+        else
+            FAIL("no cache, but Current/Foundation.framework is in dyld cache");
+     }
+#endif
+
+#endif
+
+    PASS("SUCCESS");
+}
+
index 35d77cf72fcdf3c1da4c7b0c50840fea8ac5d179..d069f1d7e5946e264a638078165e74ec53eec3fa 100644 (file)
@@ -1,10 +1,10 @@
-// BUILD_ONLY: MacOSX
-
 // BOOT_ARGS: dyld_flags=2
 
-// BUILD:  $CC my.c -dynamiclib -o $BUILD_DIR/libmy.dylib -install_name $RUN_DIR/libmy.dylib
-// BUILD:  $CC main.c -o $BUILD_DIR/amfi-hardened-dlopen-leaf.exe -DHARDENED=1
-// BUILD:  $CC main.c -o $BUILD_DIR/amfi-not-hardened-dlopen-leaf.exe
+// BUILD(macos):  $CC my.c -dynamiclib -o $BUILD_DIR/libmy.dylib -install_name $RUN_DIR/libmy.dylib
+// BUILD(macos):  $CC main.c -o $BUILD_DIR/amfi-hardened-dlopen-leaf.exe -DHARDENED=1
+// BUILD(macos):  $CC main.c -o $BUILD_DIR/amfi-not-hardened-dlopen-leaf.exe
+
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  DYLD_AMFI_FAKE=0x14 ./amfi-hardened-dlopen-leaf.exe
 // RUN:  DYLD_AMFI_FAKE=0x3F ./amfi-not-hardened-dlopen-leaf.exe
index d35a65e5fb40f3371fb02495e2c8b321fdab2d4b..6baea87f76a581ecea076490a9d9ba5693f2a718 100644 (file)
 
 #include <libamfi.h>
 
+#include "test_support.h"
+
 int main()
 {
-    printf("[BEGIN] amfi-interpose\n");
-
     // interposed malloc() doubles alloction size and prefills allocation with '#'
     char* p1 = malloc(10);
     bool interposed = (strncmp(p1, "####################", 20) == 0);
 
     const char* amfiBits = getenv("DYLD_AMFI_FAKE");
     if ( amfiBits == NULL ) {
-        printf("[FAIL] amfi-interpose: DYLD_AMFI_FAKE not setn\n");
-        return 0;
+        FAIL("amfi-interpose: DYLD_AMFI_FAKE not set");
     }
 #ifdef AMFI_RETURNS_INTERPOSING_FLAG
     bool allowInterposing = (strcmp(amfiBits, "0x7F") == 0);
@@ -38,11 +37,9 @@ int main()
 #endif
 
     if ( interposed == allowInterposing )
-        printf("[PASS] amfi-interpose\n");
+        PASS("Success");
     else if ( interposed )
-        printf("[FAIL] amfi-interpose: malloc interposed, but amfi said to block it\n");
+        FAIL("amfi-interpose: malloc interposed, but amfi said to block it");
     else
-        printf("[FAIL] amfi-interpose: malloc not interposed, but amfi said to allow it\n");
-
-       return 0;
+        FAIL("amfi-interpose: malloc not interposed, but amfi said to allow it");
 }
index f89fe343d7c1e80d6ba01e3ae9030729b398c7c9..f5a917fe56695d21834536ce818482e65639e47a 100644 (file)
@@ -14,7 +14,11 @@ __attribute__((weak))
 void* p = 0;
 
 // Choose a large enough negative offset to be before the shared cache or the image
+#if __LP64__
 const uintptr_t offset = 1ULL << 36;
+#else
+const uintptr_t offset = 1ULL << 28;
+#endif
 void* pMinus = (void*)((uintptr_t)&p - offset);
 
 // Get a pointer to something we assume is in the shared cache
index 78c83ceb17c7d1a7dfb13056b0feeae50869718f..b5498c8dad5cceeca49a00f1f4b25b4fe944e4ae 100644 (file)
 extern char tzname[];  // a char array in libSystem.dylib
 
 
-#define VERIFY(a,b) assert(a==b)
+#define VERIFY(a,b) assert(a==(b))
 
 
 static uint8_t a;
 uint8_t* const rebasedPtrs[] = { NULL, NULL, &a, &a+1, &a+16, &a+1023, NULL, &a-1 };
 
+#if __LP64__
+uint8_t* const tbiPointers[] = { &a+0x8000000000000000, &a, &a+0x9000000000000000 };
+#endif
+
 void verifyRebases()
 {
     VERIFY(rebasedPtrs[0], NULL);
@@ -30,6 +34,10 @@ void verifyRebases()
     VERIFY(rebasedPtrs[5], &a+1023);
     VERIFY(rebasedPtrs[6], NULL);
     VERIFY(rebasedPtrs[7], &a-1);
+#if __LP64__
+    VERIFY(tbiPointers[0], &a + 0x8000000000000000);
+    VERIFY(tbiPointers[2], &a + 0x9000000000000000);
+#endif
 }
 
 
index ea16a47944fdf49d706a71817f23ade1f2c07386..4100bde9e3c860f1d0ba7cdd56f2841a2cb4846c 100644 (file)
@@ -64998,3 +64998,5003 @@ void foo64997() { }
 void foo64998() { }
 void foo64999() { }
 void foo65000() { }
+void foo65001() { }
+void foo65002() { }
+void foo65003() { }
+void foo65004() { }
+void foo65005() { }
+void foo65006() { }
+void foo65007() { }
+void foo65008() { }
+void foo65009() { }
+void foo65010() { }
+void foo65011() { }
+void foo65012() { }
+void foo65013() { }
+void foo65014() { }
+void foo65015() { }
+void foo65016() { }
+void foo65017() { }
+void foo65018() { }
+void foo65019() { }
+void foo65020() { }
+void foo65021() { }
+void foo65022() { }
+void foo65023() { }
+void foo65024() { }
+void foo65025() { }
+void foo65026() { }
+void foo65027() { }
+void foo65028() { }
+void foo65029() { }
+void foo65030() { }
+void foo65031() { }
+void foo65032() { }
+void foo65033() { }
+void foo65034() { }
+void foo65035() { }
+void foo65036() { }
+void foo65037() { }
+void foo65038() { }
+void foo65039() { }
+void foo65040() { }
+void foo65041() { }
+void foo65042() { }
+void foo65043() { }
+void foo65044() { }
+void foo65045() { }
+void foo65046() { }
+void foo65047() { }
+void foo65048() { }
+void foo65049() { }
+void foo65050() { }
+void foo65051() { }
+void foo65052() { }
+void foo65053() { }
+void foo65054() { }
+void foo65055() { }
+void foo65056() { }
+void foo65057() { }
+void foo65058() { }
+void foo65059() { }
+void foo65060() { }
+void foo65061() { }
+void foo65062() { }
+void foo65063() { }
+void foo65064() { }
+void foo65065() { }
+void foo65066() { }
+void foo65067() { }
+void foo65068() { }
+void foo65069() { }
+void foo65070() { }
+void foo65071() { }
+void foo65072() { }
+void foo65073() { }
+void foo65074() { }
+void foo65075() { }
+void foo65076() { }
+void foo65077() { }
+void foo65078() { }
+void foo65079() { }
+void foo65080() { }
+void foo65081() { }
+void foo65082() { }
+void foo65083() { }
+void foo65084() { }
+void foo65085() { }
+void foo65086() { }
+void foo65087() { }
+void foo65088() { }
+void foo65089() { }
+void foo65090() { }
+void foo65091() { }
+void foo65092() { }
+void foo65093() { }
+void foo65094() { }
+void foo65095() { }
+void foo65096() { }
+void foo65097() { }
+void foo65098() { }
+void foo65099() { }
+void foo65100() { }
+void foo65101() { }
+void foo65102() { }
+void foo65103() { }
+void foo65104() { }
+void foo65105() { }
+void foo65106() { }
+void foo65107() { }
+void foo65108() { }
+void foo65109() { }
+void foo65110() { }
+void foo65111() { }
+void foo65112() { }
+void foo65113() { }
+void foo65114() { }
+void foo65115() { }
+void foo65116() { }
+void foo65117() { }
+void foo65118() { }
+void foo65119() { }
+void foo65120() { }
+void foo65121() { }
+void foo65122() { }
+void foo65123() { }
+void foo65124() { }
+void foo65125() { }
+void foo65126() { }
+void foo65127() { }
+void foo65128() { }
+void foo65129() { }
+void foo65130() { }
+void foo65131() { }
+void foo65132() { }
+void foo65133() { }
+void foo65134() { }
+void foo65135() { }
+void foo65136() { }
+void foo65137() { }
+void foo65138() { }
+void foo65139() { }
+void foo65140() { }
+void foo65141() { }
+void foo65142() { }
+void foo65143() { }
+void foo65144() { }
+void foo65145() { }
+void foo65146() { }
+void foo65147() { }
+void foo65148() { }
+void foo65149() { }
+void foo65150() { }
+void foo65151() { }
+void foo65152() { }
+void foo65153() { }
+void foo65154() { }
+void foo65155() { }
+void foo65156() { }
+void foo65157() { }
+void foo65158() { }
+void foo65159() { }
+void foo65160() { }
+void foo65161() { }
+void foo65162() { }
+void foo65163() { }
+void foo65164() { }
+void foo65165() { }
+void foo65166() { }
+void foo65167() { }
+void foo65168() { }
+void foo65169() { }
+void foo65170() { }
+void foo65171() { }
+void foo65172() { }
+void foo65173() { }
+void foo65174() { }
+void foo65175() { }
+void foo65176() { }
+void foo65177() { }
+void foo65178() { }
+void foo65179() { }
+void foo65180() { }
+void foo65181() { }
+void foo65182() { }
+void foo65183() { }
+void foo65184() { }
+void foo65185() { }
+void foo65186() { }
+void foo65187() { }
+void foo65188() { }
+void foo65189() { }
+void foo65190() { }
+void foo65191() { }
+void foo65192() { }
+void foo65193() { }
+void foo65194() { }
+void foo65195() { }
+void foo65196() { }
+void foo65197() { }
+void foo65198() { }
+void foo65199() { }
+void foo65200() { }
+void foo65201() { }
+void foo65202() { }
+void foo65203() { }
+void foo65204() { }
+void foo65205() { }
+void foo65206() { }
+void foo65207() { }
+void foo65208() { }
+void foo65209() { }
+void foo65210() { }
+void foo65211() { }
+void foo65212() { }
+void foo65213() { }
+void foo65214() { }
+void foo65215() { }
+void foo65216() { }
+void foo65217() { }
+void foo65218() { }
+void foo65219() { }
+void foo65220() { }
+void foo65221() { }
+void foo65222() { }
+void foo65223() { }
+void foo65224() { }
+void foo65225() { }
+void foo65226() { }
+void foo65227() { }
+void foo65228() { }
+void foo65229() { }
+void foo65230() { }
+void foo65231() { }
+void foo65232() { }
+void foo65233() { }
+void foo65234() { }
+void foo65235() { }
+void foo65236() { }
+void foo65237() { }
+void foo65238() { }
+void foo65239() { }
+void foo65240() { }
+void foo65241() { }
+void foo65242() { }
+void foo65243() { }
+void foo65244() { }
+void foo65245() { }
+void foo65246() { }
+void foo65247() { }
+void foo65248() { }
+void foo65249() { }
+void foo65250() { }
+void foo65251() { }
+void foo65252() { }
+void foo65253() { }
+void foo65254() { }
+void foo65255() { }
+void foo65256() { }
+void foo65257() { }
+void foo65258() { }
+void foo65259() { }
+void foo65260() { }
+void foo65261() { }
+void foo65262() { }
+void foo65263() { }
+void foo65264() { }
+void foo65265() { }
+void foo65266() { }
+void foo65267() { }
+void foo65268() { }
+void foo65269() { }
+void foo65270() { }
+void foo65271() { }
+void foo65272() { }
+void foo65273() { }
+void foo65274() { }
+void foo65275() { }
+void foo65276() { }
+void foo65277() { }
+void foo65278() { }
+void foo65279() { }
+void foo65280() { }
+void foo65281() { }
+void foo65282() { }
+void foo65283() { }
+void foo65284() { }
+void foo65285() { }
+void foo65286() { }
+void foo65287() { }
+void foo65288() { }
+void foo65289() { }
+void foo65290() { }
+void foo65291() { }
+void foo65292() { }
+void foo65293() { }
+void foo65294() { }
+void foo65295() { }
+void foo65296() { }
+void foo65297() { }
+void foo65298() { }
+void foo65299() { }
+void foo65300() { }
+void foo65301() { }
+void foo65302() { }
+void foo65303() { }
+void foo65304() { }
+void foo65305() { }
+void foo65306() { }
+void foo65307() { }
+void foo65308() { }
+void foo65309() { }
+void foo65310() { }
+void foo65311() { }
+void foo65312() { }
+void foo65313() { }
+void foo65314() { }
+void foo65315() { }
+void foo65316() { }
+void foo65317() { }
+void foo65318() { }
+void foo65319() { }
+void foo65320() { }
+void foo65321() { }
+void foo65322() { }
+void foo65323() { }
+void foo65324() { }
+void foo65325() { }
+void foo65326() { }
+void foo65327() { }
+void foo65328() { }
+void foo65329() { }
+void foo65330() { }
+void foo65331() { }
+void foo65332() { }
+void foo65333() { }
+void foo65334() { }
+void foo65335() { }
+void foo65336() { }
+void foo65337() { }
+void foo65338() { }
+void foo65339() { }
+void foo65340() { }
+void foo65341() { }
+void foo65342() { }
+void foo65343() { }
+void foo65344() { }
+void foo65345() { }
+void foo65346() { }
+void foo65347() { }
+void foo65348() { }
+void foo65349() { }
+void foo65350() { }
+void foo65351() { }
+void foo65352() { }
+void foo65353() { }
+void foo65354() { }
+void foo65355() { }
+void foo65356() { }
+void foo65357() { }
+void foo65358() { }
+void foo65359() { }
+void foo65360() { }
+void foo65361() { }
+void foo65362() { }
+void foo65363() { }
+void foo65364() { }
+void foo65365() { }
+void foo65366() { }
+void foo65367() { }
+void foo65368() { }
+void foo65369() { }
+void foo65370() { }
+void foo65371() { }
+void foo65372() { }
+void foo65373() { }
+void foo65374() { }
+void foo65375() { }
+void foo65376() { }
+void foo65377() { }
+void foo65378() { }
+void foo65379() { }
+void foo65380() { }
+void foo65381() { }
+void foo65382() { }
+void foo65383() { }
+void foo65384() { }
+void foo65385() { }
+void foo65386() { }
+void foo65387() { }
+void foo65388() { }
+void foo65389() { }
+void foo65390() { }
+void foo65391() { }
+void foo65392() { }
+void foo65393() { }
+void foo65394() { }
+void foo65395() { }
+void foo65396() { }
+void foo65397() { }
+void foo65398() { }
+void foo65399() { }
+void foo65400() { }
+void foo65401() { }
+void foo65402() { }
+void foo65403() { }
+void foo65404() { }
+void foo65405() { }
+void foo65406() { }
+void foo65407() { }
+void foo65408() { }
+void foo65409() { }
+void foo65410() { }
+void foo65411() { }
+void foo65412() { }
+void foo65413() { }
+void foo65414() { }
+void foo65415() { }
+void foo65416() { }
+void foo65417() { }
+void foo65418() { }
+void foo65419() { }
+void foo65420() { }
+void foo65421() { }
+void foo65422() { }
+void foo65423() { }
+void foo65424() { }
+void foo65425() { }
+void foo65426() { }
+void foo65427() { }
+void foo65428() { }
+void foo65429() { }
+void foo65430() { }
+void foo65431() { }
+void foo65432() { }
+void foo65433() { }
+void foo65434() { }
+void foo65435() { }
+void foo65436() { }
+void foo65437() { }
+void foo65438() { }
+void foo65439() { }
+void foo65440() { }
+void foo65441() { }
+void foo65442() { }
+void foo65443() { }
+void foo65444() { }
+void foo65445() { }
+void foo65446() { }
+void foo65447() { }
+void foo65448() { }
+void foo65449() { }
+void foo65450() { }
+void foo65451() { }
+void foo65452() { }
+void foo65453() { }
+void foo65454() { }
+void foo65455() { }
+void foo65456() { }
+void foo65457() { }
+void foo65458() { }
+void foo65459() { }
+void foo65460() { }
+void foo65461() { }
+void foo65462() { }
+void foo65463() { }
+void foo65464() { }
+void foo65465() { }
+void foo65466() { }
+void foo65467() { }
+void foo65468() { }
+void foo65469() { }
+void foo65470() { }
+void foo65471() { }
+void foo65472() { }
+void foo65473() { }
+void foo65474() { }
+void foo65475() { }
+void foo65476() { }
+void foo65477() { }
+void foo65478() { }
+void foo65479() { }
+void foo65480() { }
+void foo65481() { }
+void foo65482() { }
+void foo65483() { }
+void foo65484() { }
+void foo65485() { }
+void foo65486() { }
+void foo65487() { }
+void foo65488() { }
+void foo65489() { }
+void foo65490() { }
+void foo65491() { }
+void foo65492() { }
+void foo65493() { }
+void foo65494() { }
+void foo65495() { }
+void foo65496() { }
+void foo65497() { }
+void foo65498() { }
+void foo65499() { }
+void foo65500() { }
+void foo65501() { }
+void foo65502() { }
+void foo65503() { }
+void foo65504() { }
+void foo65505() { }
+void foo65506() { }
+void foo65507() { }
+void foo65508() { }
+void foo65509() { }
+void foo65510() { }
+void foo65511() { }
+void foo65512() { }
+void foo65513() { }
+void foo65514() { }
+void foo65515() { }
+void foo65516() { }
+void foo65517() { }
+void foo65518() { }
+void foo65519() { }
+void foo65520() { }
+void foo65521() { }
+void foo65522() { }
+void foo65523() { }
+void foo65524() { }
+void foo65525() { }
+void foo65526() { }
+void foo65527() { }
+void foo65528() { }
+void foo65529() { }
+void foo65530() { }
+void foo65531() { }
+void foo65532() { }
+void foo65533() { }
+void foo65534() { }
+void foo65535() { }
+void foo65536() { }
+void foo65537() { }
+void foo65538() { }
+void foo65539() { }
+void foo65540() { }
+void foo65541() { }
+void foo65542() { }
+void foo65543() { }
+void foo65544() { }
+void foo65545() { }
+void foo65546() { }
+void foo65547() { }
+void foo65548() { }
+void foo65549() { }
+void foo65550() { }
+void foo65551() { }
+void foo65552() { }
+void foo65553() { }
+void foo65554() { }
+void foo65555() { }
+void foo65556() { }
+void foo65557() { }
+void foo65558() { }
+void foo65559() { }
+void foo65560() { }
+void foo65561() { }
+void foo65562() { }
+void foo65563() { }
+void foo65564() { }
+void foo65565() { }
+void foo65566() { }
+void foo65567() { }
+void foo65568() { }
+void foo65569() { }
+void foo65570() { }
+void foo65571() { }
+void foo65572() { }
+void foo65573() { }
+void foo65574() { }
+void foo65575() { }
+void foo65576() { }
+void foo65577() { }
+void foo65578() { }
+void foo65579() { }
+void foo65580() { }
+void foo65581() { }
+void foo65582() { }
+void foo65583() { }
+void foo65584() { }
+void foo65585() { }
+void foo65586() { }
+void foo65587() { }
+void foo65588() { }
+void foo65589() { }
+void foo65590() { }
+void foo65591() { }
+void foo65592() { }
+void foo65593() { }
+void foo65594() { }
+void foo65595() { }
+void foo65596() { }
+void foo65597() { }
+void foo65598() { }
+void foo65599() { }
+void foo65600() { }
+void foo65601() { }
+void foo65602() { }
+void foo65603() { }
+void foo65604() { }
+void foo65605() { }
+void foo65606() { }
+void foo65607() { }
+void foo65608() { }
+void foo65609() { }
+void foo65610() { }
+void foo65611() { }
+void foo65612() { }
+void foo65613() { }
+void foo65614() { }
+void foo65615() { }
+void foo65616() { }
+void foo65617() { }
+void foo65618() { }
+void foo65619() { }
+void foo65620() { }
+void foo65621() { }
+void foo65622() { }
+void foo65623() { }
+void foo65624() { }
+void foo65625() { }
+void foo65626() { }
+void foo65627() { }
+void foo65628() { }
+void foo65629() { }
+void foo65630() { }
+void foo65631() { }
+void foo65632() { }
+void foo65633() { }
+void foo65634() { }
+void foo65635() { }
+void foo65636() { }
+void foo65637() { }
+void foo65638() { }
+void foo65639() { }
+void foo65640() { }
+void foo65641() { }
+void foo65642() { }
+void foo65643() { }
+void foo65644() { }
+void foo65645() { }
+void foo65646() { }
+void foo65647() { }
+void foo65648() { }
+void foo65649() { }
+void foo65650() { }
+void foo65651() { }
+void foo65652() { }
+void foo65653() { }
+void foo65654() { }
+void foo65655() { }
+void foo65656() { }
+void foo65657() { }
+void foo65658() { }
+void foo65659() { }
+void foo65660() { }
+void foo65661() { }
+void foo65662() { }
+void foo65663() { }
+void foo65664() { }
+void foo65665() { }
+void foo65666() { }
+void foo65667() { }
+void foo65668() { }
+void foo65669() { }
+void foo65670() { }
+void foo65671() { }
+void foo65672() { }
+void foo65673() { }
+void foo65674() { }
+void foo65675() { }
+void foo65676() { }
+void foo65677() { }
+void foo65678() { }
+void foo65679() { }
+void foo65680() { }
+void foo65681() { }
+void foo65682() { }
+void foo65683() { }
+void foo65684() { }
+void foo65685() { }
+void foo65686() { }
+void foo65687() { }
+void foo65688() { }
+void foo65689() { }
+void foo65690() { }
+void foo65691() { }
+void foo65692() { }
+void foo65693() { }
+void foo65694() { }
+void foo65695() { }
+void foo65696() { }
+void foo65697() { }
+void foo65698() { }
+void foo65699() { }
+void foo65700() { }
+void foo65701() { }
+void foo65702() { }
+void foo65703() { }
+void foo65704() { }
+void foo65705() { }
+void foo65706() { }
+void foo65707() { }
+void foo65708() { }
+void foo65709() { }
+void foo65710() { }
+void foo65711() { }
+void foo65712() { }
+void foo65713() { }
+void foo65714() { }
+void foo65715() { }
+void foo65716() { }
+void foo65717() { }
+void foo65718() { }
+void foo65719() { }
+void foo65720() { }
+void foo65721() { }
+void foo65722() { }
+void foo65723() { }
+void foo65724() { }
+void foo65725() { }
+void foo65726() { }
+void foo65727() { }
+void foo65728() { }
+void foo65729() { }
+void foo65730() { }
+void foo65731() { }
+void foo65732() { }
+void foo65733() { }
+void foo65734() { }
+void foo65735() { }
+void foo65736() { }
+void foo65737() { }
+void foo65738() { }
+void foo65739() { }
+void foo65740() { }
+void foo65741() { }
+void foo65742() { }
+void foo65743() { }
+void foo65744() { }
+void foo65745() { }
+void foo65746() { }
+void foo65747() { }
+void foo65748() { }
+void foo65749() { }
+void foo65750() { }
+void foo65751() { }
+void foo65752() { }
+void foo65753() { }
+void foo65754() { }
+void foo65755() { }
+void foo65756() { }
+void foo65757() { }
+void foo65758() { }
+void foo65759() { }
+void foo65760() { }
+void foo65761() { }
+void foo65762() { }
+void foo65763() { }
+void foo65764() { }
+void foo65765() { }
+void foo65766() { }
+void foo65767() { }
+void foo65768() { }
+void foo65769() { }
+void foo65770() { }
+void foo65771() { }
+void foo65772() { }
+void foo65773() { }
+void foo65774() { }
+void foo65775() { }
+void foo65776() { }
+void foo65777() { }
+void foo65778() { }
+void foo65779() { }
+void foo65780() { }
+void foo65781() { }
+void foo65782() { }
+void foo65783() { }
+void foo65784() { }
+void foo65785() { }
+void foo65786() { }
+void foo65787() { }
+void foo65788() { }
+void foo65789() { }
+void foo65790() { }
+void foo65791() { }
+void foo65792() { }
+void foo65793() { }
+void foo65794() { }
+void foo65795() { }
+void foo65796() { }
+void foo65797() { }
+void foo65798() { }
+void foo65799() { }
+void foo65800() { }
+void foo65801() { }
+void foo65802() { }
+void foo65803() { }
+void foo65804() { }
+void foo65805() { }
+void foo65806() { }
+void foo65807() { }
+void foo65808() { }
+void foo65809() { }
+void foo65810() { }
+void foo65811() { }
+void foo65812() { }
+void foo65813() { }
+void foo65814() { }
+void foo65815() { }
+void foo65816() { }
+void foo65817() { }
+void foo65818() { }
+void foo65819() { }
+void foo65820() { }
+void foo65821() { }
+void foo65822() { }
+void foo65823() { }
+void foo65824() { }
+void foo65825() { }
+void foo65826() { }
+void foo65827() { }
+void foo65828() { }
+void foo65829() { }
+void foo65830() { }
+void foo65831() { }
+void foo65832() { }
+void foo65833() { }
+void foo65834() { }
+void foo65835() { }
+void foo65836() { }
+void foo65837() { }
+void foo65838() { }
+void foo65839() { }
+void foo65840() { }
+void foo65841() { }
+void foo65842() { }
+void foo65843() { }
+void foo65844() { }
+void foo65845() { }
+void foo65846() { }
+void foo65847() { }
+void foo65848() { }
+void foo65849() { }
+void foo65850() { }
+void foo65851() { }
+void foo65852() { }
+void foo65853() { }
+void foo65854() { }
+void foo65855() { }
+void foo65856() { }
+void foo65857() { }
+void foo65858() { }
+void foo65859() { }
+void foo65860() { }
+void foo65861() { }
+void foo65862() { }
+void foo65863() { }
+void foo65864() { }
+void foo65865() { }
+void foo65866() { }
+void foo65867() { }
+void foo65868() { }
+void foo65869() { }
+void foo65870() { }
+void foo65871() { }
+void foo65872() { }
+void foo65873() { }
+void foo65874() { }
+void foo65875() { }
+void foo65876() { }
+void foo65877() { }
+void foo65878() { }
+void foo65879() { }
+void foo65880() { }
+void foo65881() { }
+void foo65882() { }
+void foo65883() { }
+void foo65884() { }
+void foo65885() { }
+void foo65886() { }
+void foo65887() { }
+void foo65888() { }
+void foo65889() { }
+void foo65890() { }
+void foo65891() { }
+void foo65892() { }
+void foo65893() { }
+void foo65894() { }
+void foo65895() { }
+void foo65896() { }
+void foo65897() { }
+void foo65898() { }
+void foo65899() { }
+void foo65900() { }
+void foo65901() { }
+void foo65902() { }
+void foo65903() { }
+void foo65904() { }
+void foo65905() { }
+void foo65906() { }
+void foo65907() { }
+void foo65908() { }
+void foo65909() { }
+void foo65910() { }
+void foo65911() { }
+void foo65912() { }
+void foo65913() { }
+void foo65914() { }
+void foo65915() { }
+void foo65916() { }
+void foo65917() { }
+void foo65918() { }
+void foo65919() { }
+void foo65920() { }
+void foo65921() { }
+void foo65922() { }
+void foo65923() { }
+void foo65924() { }
+void foo65925() { }
+void foo65926() { }
+void foo65927() { }
+void foo65928() { }
+void foo65929() { }
+void foo65930() { }
+void foo65931() { }
+void foo65932() { }
+void foo65933() { }
+void foo65934() { }
+void foo65935() { }
+void foo65936() { }
+void foo65937() { }
+void foo65938() { }
+void foo65939() { }
+void foo65940() { }
+void foo65941() { }
+void foo65942() { }
+void foo65943() { }
+void foo65944() { }
+void foo65945() { }
+void foo65946() { }
+void foo65947() { }
+void foo65948() { }
+void foo65949() { }
+void foo65950() { }
+void foo65951() { }
+void foo65952() { }
+void foo65953() { }
+void foo65954() { }
+void foo65955() { }
+void foo65956() { }
+void foo65957() { }
+void foo65958() { }
+void foo65959() { }
+void foo65960() { }
+void foo65961() { }
+void foo65962() { }
+void foo65963() { }
+void foo65964() { }
+void foo65965() { }
+void foo65966() { }
+void foo65967() { }
+void foo65968() { }
+void foo65969() { }
+void foo65970() { }
+void foo65971() { }
+void foo65972() { }
+void foo65973() { }
+void foo65974() { }
+void foo65975() { }
+void foo65976() { }
+void foo65977() { }
+void foo65978() { }
+void foo65979() { }
+void foo65980() { }
+void foo65981() { }
+void foo65982() { }
+void foo65983() { }
+void foo65984() { }
+void foo65985() { }
+void foo65986() { }
+void foo65987() { }
+void foo65988() { }
+void foo65989() { }
+void foo65990() { }
+void foo65991() { }
+void foo65992() { }
+void foo65993() { }
+void foo65994() { }
+void foo65995() { }
+void foo65996() { }
+void foo65997() { }
+void foo65998() { }
+void foo65999() { }
+void foo66000() { }
+void foo66001() { }
+void foo66002() { }
+void foo66003() { }
+void foo66004() { }
+void foo66005() { }
+void foo66006() { }
+void foo66007() { }
+void foo66008() { }
+void foo66009() { }
+void foo66010() { }
+void foo66011() { }
+void foo66012() { }
+void foo66013() { }
+void foo66014() { }
+void foo66015() { }
+void foo66016() { }
+void foo66017() { }
+void foo66018() { }
+void foo66019() { }
+void foo66020() { }
+void foo66021() { }
+void foo66022() { }
+void foo66023() { }
+void foo66024() { }
+void foo66025() { }
+void foo66026() { }
+void foo66027() { }
+void foo66028() { }
+void foo66029() { }
+void foo66030() { }
+void foo66031() { }
+void foo66032() { }
+void foo66033() { }
+void foo66034() { }
+void foo66035() { }
+void foo66036() { }
+void foo66037() { }
+void foo66038() { }
+void foo66039() { }
+void foo66040() { }
+void foo66041() { }
+void foo66042() { }
+void foo66043() { }
+void foo66044() { }
+void foo66045() { }
+void foo66046() { }
+void foo66047() { }
+void foo66048() { }
+void foo66049() { }
+void foo66050() { }
+void foo66051() { }
+void foo66052() { }
+void foo66053() { }
+void foo66054() { }
+void foo66055() { }
+void foo66056() { }
+void foo66057() { }
+void foo66058() { }
+void foo66059() { }
+void foo66060() { }
+void foo66061() { }
+void foo66062() { }
+void foo66063() { }
+void foo66064() { }
+void foo66065() { }
+void foo66066() { }
+void foo66067() { }
+void foo66068() { }
+void foo66069() { }
+void foo66070() { }
+void foo66071() { }
+void foo66072() { }
+void foo66073() { }
+void foo66074() { }
+void foo66075() { }
+void foo66076() { }
+void foo66077() { }
+void foo66078() { }
+void foo66079() { }
+void foo66080() { }
+void foo66081() { }
+void foo66082() { }
+void foo66083() { }
+void foo66084() { }
+void foo66085() { }
+void foo66086() { }
+void foo66087() { }
+void foo66088() { }
+void foo66089() { }
+void foo66090() { }
+void foo66091() { }
+void foo66092() { }
+void foo66093() { }
+void foo66094() { }
+void foo66095() { }
+void foo66096() { }
+void foo66097() { }
+void foo66098() { }
+void foo66099() { }
+void foo66100() { }
+void foo66101() { }
+void foo66102() { }
+void foo66103() { }
+void foo66104() { }
+void foo66105() { }
+void foo66106() { }
+void foo66107() { }
+void foo66108() { }
+void foo66109() { }
+void foo66110() { }
+void foo66111() { }
+void foo66112() { }
+void foo66113() { }
+void foo66114() { }
+void foo66115() { }
+void foo66116() { }
+void foo66117() { }
+void foo66118() { }
+void foo66119() { }
+void foo66120() { }
+void foo66121() { }
+void foo66122() { }
+void foo66123() { }
+void foo66124() { }
+void foo66125() { }
+void foo66126() { }
+void foo66127() { }
+void foo66128() { }
+void foo66129() { }
+void foo66130() { }
+void foo66131() { }
+void foo66132() { }
+void foo66133() { }
+void foo66134() { }
+void foo66135() { }
+void foo66136() { }
+void foo66137() { }
+void foo66138() { }
+void foo66139() { }
+void foo66140() { }
+void foo66141() { }
+void foo66142() { }
+void foo66143() { }
+void foo66144() { }
+void foo66145() { }
+void foo66146() { }
+void foo66147() { }
+void foo66148() { }
+void foo66149() { }
+void foo66150() { }
+void foo66151() { }
+void foo66152() { }
+void foo66153() { }
+void foo66154() { }
+void foo66155() { }
+void foo66156() { }
+void foo66157() { }
+void foo66158() { }
+void foo66159() { }
+void foo66160() { }
+void foo66161() { }
+void foo66162() { }
+void foo66163() { }
+void foo66164() { }
+void foo66165() { }
+void foo66166() { }
+void foo66167() { }
+void foo66168() { }
+void foo66169() { }
+void foo66170() { }
+void foo66171() { }
+void foo66172() { }
+void foo66173() { }
+void foo66174() { }
+void foo66175() { }
+void foo66176() { }
+void foo66177() { }
+void foo66178() { }
+void foo66179() { }
+void foo66180() { }
+void foo66181() { }
+void foo66182() { }
+void foo66183() { }
+void foo66184() { }
+void foo66185() { }
+void foo66186() { }
+void foo66187() { }
+void foo66188() { }
+void foo66189() { }
+void foo66190() { }
+void foo66191() { }
+void foo66192() { }
+void foo66193() { }
+void foo66194() { }
+void foo66195() { }
+void foo66196() { }
+void foo66197() { }
+void foo66198() { }
+void foo66199() { }
+void foo66200() { }
+void foo66201() { }
+void foo66202() { }
+void foo66203() { }
+void foo66204() { }
+void foo66205() { }
+void foo66206() { }
+void foo66207() { }
+void foo66208() { }
+void foo66209() { }
+void foo66210() { }
+void foo66211() { }
+void foo66212() { }
+void foo66213() { }
+void foo66214() { }
+void foo66215() { }
+void foo66216() { }
+void foo66217() { }
+void foo66218() { }
+void foo66219() { }
+void foo66220() { }
+void foo66221() { }
+void foo66222() { }
+void foo66223() { }
+void foo66224() { }
+void foo66225() { }
+void foo66226() { }
+void foo66227() { }
+void foo66228() { }
+void foo66229() { }
+void foo66230() { }
+void foo66231() { }
+void foo66232() { }
+void foo66233() { }
+void foo66234() { }
+void foo66235() { }
+void foo66236() { }
+void foo66237() { }
+void foo66238() { }
+void foo66239() { }
+void foo66240() { }
+void foo66241() { }
+void foo66242() { }
+void foo66243() { }
+void foo66244() { }
+void foo66245() { }
+void foo66246() { }
+void foo66247() { }
+void foo66248() { }
+void foo66249() { }
+void foo66250() { }
+void foo66251() { }
+void foo66252() { }
+void foo66253() { }
+void foo66254() { }
+void foo66255() { }
+void foo66256() { }
+void foo66257() { }
+void foo66258() { }
+void foo66259() { }
+void foo66260() { }
+void foo66261() { }
+void foo66262() { }
+void foo66263() { }
+void foo66264() { }
+void foo66265() { }
+void foo66266() { }
+void foo66267() { }
+void foo66268() { }
+void foo66269() { }
+void foo66270() { }
+void foo66271() { }
+void foo66272() { }
+void foo66273() { }
+void foo66274() { }
+void foo66275() { }
+void foo66276() { }
+void foo66277() { }
+void foo66278() { }
+void foo66279() { }
+void foo66280() { }
+void foo66281() { }
+void foo66282() { }
+void foo66283() { }
+void foo66284() { }
+void foo66285() { }
+void foo66286() { }
+void foo66287() { }
+void foo66288() { }
+void foo66289() { }
+void foo66290() { }
+void foo66291() { }
+void foo66292() { }
+void foo66293() { }
+void foo66294() { }
+void foo66295() { }
+void foo66296() { }
+void foo66297() { }
+void foo66298() { }
+void foo66299() { }
+void foo66300() { }
+void foo66301() { }
+void foo66302() { }
+void foo66303() { }
+void foo66304() { }
+void foo66305() { }
+void foo66306() { }
+void foo66307() { }
+void foo66308() { }
+void foo66309() { }
+void foo66310() { }
+void foo66311() { }
+void foo66312() { }
+void foo66313() { }
+void foo66314() { }
+void foo66315() { }
+void foo66316() { }
+void foo66317() { }
+void foo66318() { }
+void foo66319() { }
+void foo66320() { }
+void foo66321() { }
+void foo66322() { }
+void foo66323() { }
+void foo66324() { }
+void foo66325() { }
+void foo66326() { }
+void foo66327() { }
+void foo66328() { }
+void foo66329() { }
+void foo66330() { }
+void foo66331() { }
+void foo66332() { }
+void foo66333() { }
+void foo66334() { }
+void foo66335() { }
+void foo66336() { }
+void foo66337() { }
+void foo66338() { }
+void foo66339() { }
+void foo66340() { }
+void foo66341() { }
+void foo66342() { }
+void foo66343() { }
+void foo66344() { }
+void foo66345() { }
+void foo66346() { }
+void foo66347() { }
+void foo66348() { }
+void foo66349() { }
+void foo66350() { }
+void foo66351() { }
+void foo66352() { }
+void foo66353() { }
+void foo66354() { }
+void foo66355() { }
+void foo66356() { }
+void foo66357() { }
+void foo66358() { }
+void foo66359() { }
+void foo66360() { }
+void foo66361() { }
+void foo66362() { }
+void foo66363() { }
+void foo66364() { }
+void foo66365() { }
+void foo66366() { }
+void foo66367() { }
+void foo66368() { }
+void foo66369() { }
+void foo66370() { }
+void foo66371() { }
+void foo66372() { }
+void foo66373() { }
+void foo66374() { }
+void foo66375() { }
+void foo66376() { }
+void foo66377() { }
+void foo66378() { }
+void foo66379() { }
+void foo66380() { }
+void foo66381() { }
+void foo66382() { }
+void foo66383() { }
+void foo66384() { }
+void foo66385() { }
+void foo66386() { }
+void foo66387() { }
+void foo66388() { }
+void foo66389() { }
+void foo66390() { }
+void foo66391() { }
+void foo66392() { }
+void foo66393() { }
+void foo66394() { }
+void foo66395() { }
+void foo66396() { }
+void foo66397() { }
+void foo66398() { }
+void foo66399() { }
+void foo66400() { }
+void foo66401() { }
+void foo66402() { }
+void foo66403() { }
+void foo66404() { }
+void foo66405() { }
+void foo66406() { }
+void foo66407() { }
+void foo66408() { }
+void foo66409() { }
+void foo66410() { }
+void foo66411() { }
+void foo66412() { }
+void foo66413() { }
+void foo66414() { }
+void foo66415() { }
+void foo66416() { }
+void foo66417() { }
+void foo66418() { }
+void foo66419() { }
+void foo66420() { }
+void foo66421() { }
+void foo66422() { }
+void foo66423() { }
+void foo66424() { }
+void foo66425() { }
+void foo66426() { }
+void foo66427() { }
+void foo66428() { }
+void foo66429() { }
+void foo66430() { }
+void foo66431() { }
+void foo66432() { }
+void foo66433() { }
+void foo66434() { }
+void foo66435() { }
+void foo66436() { }
+void foo66437() { }
+void foo66438() { }
+void foo66439() { }
+void foo66440() { }
+void foo66441() { }
+void foo66442() { }
+void foo66443() { }
+void foo66444() { }
+void foo66445() { }
+void foo66446() { }
+void foo66447() { }
+void foo66448() { }
+void foo66449() { }
+void foo66450() { }
+void foo66451() { }
+void foo66452() { }
+void foo66453() { }
+void foo66454() { }
+void foo66455() { }
+void foo66456() { }
+void foo66457() { }
+void foo66458() { }
+void foo66459() { }
+void foo66460() { }
+void foo66461() { }
+void foo66462() { }
+void foo66463() { }
+void foo66464() { }
+void foo66465() { }
+void foo66466() { }
+void foo66467() { }
+void foo66468() { }
+void foo66469() { }
+void foo66470() { }
+void foo66471() { }
+void foo66472() { }
+void foo66473() { }
+void foo66474() { }
+void foo66475() { }
+void foo66476() { }
+void foo66477() { }
+void foo66478() { }
+void foo66479() { }
+void foo66480() { }
+void foo66481() { }
+void foo66482() { }
+void foo66483() { }
+void foo66484() { }
+void foo66485() { }
+void foo66486() { }
+void foo66487() { }
+void foo66488() { }
+void foo66489() { }
+void foo66490() { }
+void foo66491() { }
+void foo66492() { }
+void foo66493() { }
+void foo66494() { }
+void foo66495() { }
+void foo66496() { }
+void foo66497() { }
+void foo66498() { }
+void foo66499() { }
+void foo66500() { }
+void foo66501() { }
+void foo66502() { }
+void foo66503() { }
+void foo66504() { }
+void foo66505() { }
+void foo66506() { }
+void foo66507() { }
+void foo66508() { }
+void foo66509() { }
+void foo66510() { }
+void foo66511() { }
+void foo66512() { }
+void foo66513() { }
+void foo66514() { }
+void foo66515() { }
+void foo66516() { }
+void foo66517() { }
+void foo66518() { }
+void foo66519() { }
+void foo66520() { }
+void foo66521() { }
+void foo66522() { }
+void foo66523() { }
+void foo66524() { }
+void foo66525() { }
+void foo66526() { }
+void foo66527() { }
+void foo66528() { }
+void foo66529() { }
+void foo66530() { }
+void foo66531() { }
+void foo66532() { }
+void foo66533() { }
+void foo66534() { }
+void foo66535() { }
+void foo66536() { }
+void foo66537() { }
+void foo66538() { }
+void foo66539() { }
+void foo66540() { }
+void foo66541() { }
+void foo66542() { }
+void foo66543() { }
+void foo66544() { }
+void foo66545() { }
+void foo66546() { }
+void foo66547() { }
+void foo66548() { }
+void foo66549() { }
+void foo66550() { }
+void foo66551() { }
+void foo66552() { }
+void foo66553() { }
+void foo66554() { }
+void foo66555() { }
+void foo66556() { }
+void foo66557() { }
+void foo66558() { }
+void foo66559() { }
+void foo66560() { }
+void foo66561() { }
+void foo66562() { }
+void foo66563() { }
+void foo66564() { }
+void foo66565() { }
+void foo66566() { }
+void foo66567() { }
+void foo66568() { }
+void foo66569() { }
+void foo66570() { }
+void foo66571() { }
+void foo66572() { }
+void foo66573() { }
+void foo66574() { }
+void foo66575() { }
+void foo66576() { }
+void foo66577() { }
+void foo66578() { }
+void foo66579() { }
+void foo66580() { }
+void foo66581() { }
+void foo66582() { }
+void foo66583() { }
+void foo66584() { }
+void foo66585() { }
+void foo66586() { }
+void foo66587() { }
+void foo66588() { }
+void foo66589() { }
+void foo66590() { }
+void foo66591() { }
+void foo66592() { }
+void foo66593() { }
+void foo66594() { }
+void foo66595() { }
+void foo66596() { }
+void foo66597() { }
+void foo66598() { }
+void foo66599() { }
+void foo66600() { }
+void foo66601() { }
+void foo66602() { }
+void foo66603() { }
+void foo66604() { }
+void foo66605() { }
+void foo66606() { }
+void foo66607() { }
+void foo66608() { }
+void foo66609() { }
+void foo66610() { }
+void foo66611() { }
+void foo66612() { }
+void foo66613() { }
+void foo66614() { }
+void foo66615() { }
+void foo66616() { }
+void foo66617() { }
+void foo66618() { }
+void foo66619() { }
+void foo66620() { }
+void foo66621() { }
+void foo66622() { }
+void foo66623() { }
+void foo66624() { }
+void foo66625() { }
+void foo66626() { }
+void foo66627() { }
+void foo66628() { }
+void foo66629() { }
+void foo66630() { }
+void foo66631() { }
+void foo66632() { }
+void foo66633() { }
+void foo66634() { }
+void foo66635() { }
+void foo66636() { }
+void foo66637() { }
+void foo66638() { }
+void foo66639() { }
+void foo66640() { }
+void foo66641() { }
+void foo66642() { }
+void foo66643() { }
+void foo66644() { }
+void foo66645() { }
+void foo66646() { }
+void foo66647() { }
+void foo66648() { }
+void foo66649() { }
+void foo66650() { }
+void foo66651() { }
+void foo66652() { }
+void foo66653() { }
+void foo66654() { }
+void foo66655() { }
+void foo66656() { }
+void foo66657() { }
+void foo66658() { }
+void foo66659() { }
+void foo66660() { }
+void foo66661() { }
+void foo66662() { }
+void foo66663() { }
+void foo66664() { }
+void foo66665() { }
+void foo66666() { }
+void foo66667() { }
+void foo66668() { }
+void foo66669() { }
+void foo66670() { }
+void foo66671() { }
+void foo66672() { }
+void foo66673() { }
+void foo66674() { }
+void foo66675() { }
+void foo66676() { }
+void foo66677() { }
+void foo66678() { }
+void foo66679() { }
+void foo66680() { }
+void foo66681() { }
+void foo66682() { }
+void foo66683() { }
+void foo66684() { }
+void foo66685() { }
+void foo66686() { }
+void foo66687() { }
+void foo66688() { }
+void foo66689() { }
+void foo66690() { }
+void foo66691() { }
+void foo66692() { }
+void foo66693() { }
+void foo66694() { }
+void foo66695() { }
+void foo66696() { }
+void foo66697() { }
+void foo66698() { }
+void foo66699() { }
+void foo66700() { }
+void foo66701() { }
+void foo66702() { }
+void foo66703() { }
+void foo66704() { }
+void foo66705() { }
+void foo66706() { }
+void foo66707() { }
+void foo66708() { }
+void foo66709() { }
+void foo66710() { }
+void foo66711() { }
+void foo66712() { }
+void foo66713() { }
+void foo66714() { }
+void foo66715() { }
+void foo66716() { }
+void foo66717() { }
+void foo66718() { }
+void foo66719() { }
+void foo66720() { }
+void foo66721() { }
+void foo66722() { }
+void foo66723() { }
+void foo66724() { }
+void foo66725() { }
+void foo66726() { }
+void foo66727() { }
+void foo66728() { }
+void foo66729() { }
+void foo66730() { }
+void foo66731() { }
+void foo66732() { }
+void foo66733() { }
+void foo66734() { }
+void foo66735() { }
+void foo66736() { }
+void foo66737() { }
+void foo66738() { }
+void foo66739() { }
+void foo66740() { }
+void foo66741() { }
+void foo66742() { }
+void foo66743() { }
+void foo66744() { }
+void foo66745() { }
+void foo66746() { }
+void foo66747() { }
+void foo66748() { }
+void foo66749() { }
+void foo66750() { }
+void foo66751() { }
+void foo66752() { }
+void foo66753() { }
+void foo66754() { }
+void foo66755() { }
+void foo66756() { }
+void foo66757() { }
+void foo66758() { }
+void foo66759() { }
+void foo66760() { }
+void foo66761() { }
+void foo66762() { }
+void foo66763() { }
+void foo66764() { }
+void foo66765() { }
+void foo66766() { }
+void foo66767() { }
+void foo66768() { }
+void foo66769() { }
+void foo66770() { }
+void foo66771() { }
+void foo66772() { }
+void foo66773() { }
+void foo66774() { }
+void foo66775() { }
+void foo66776() { }
+void foo66777() { }
+void foo66778() { }
+void foo66779() { }
+void foo66780() { }
+void foo66781() { }
+void foo66782() { }
+void foo66783() { }
+void foo66784() { }
+void foo66785() { }
+void foo66786() { }
+void foo66787() { }
+void foo66788() { }
+void foo66789() { }
+void foo66790() { }
+void foo66791() { }
+void foo66792() { }
+void foo66793() { }
+void foo66794() { }
+void foo66795() { }
+void foo66796() { }
+void foo66797() { }
+void foo66798() { }
+void foo66799() { }
+void foo66800() { }
+void foo66801() { }
+void foo66802() { }
+void foo66803() { }
+void foo66804() { }
+void foo66805() { }
+void foo66806() { }
+void foo66807() { }
+void foo66808() { }
+void foo66809() { }
+void foo66810() { }
+void foo66811() { }
+void foo66812() { }
+void foo66813() { }
+void foo66814() { }
+void foo66815() { }
+void foo66816() { }
+void foo66817() { }
+void foo66818() { }
+void foo66819() { }
+void foo66820() { }
+void foo66821() { }
+void foo66822() { }
+void foo66823() { }
+void foo66824() { }
+void foo66825() { }
+void foo66826() { }
+void foo66827() { }
+void foo66828() { }
+void foo66829() { }
+void foo66830() { }
+void foo66831() { }
+void foo66832() { }
+void foo66833() { }
+void foo66834() { }
+void foo66835() { }
+void foo66836() { }
+void foo66837() { }
+void foo66838() { }
+void foo66839() { }
+void foo66840() { }
+void foo66841() { }
+void foo66842() { }
+void foo66843() { }
+void foo66844() { }
+void foo66845() { }
+void foo66846() { }
+void foo66847() { }
+void foo66848() { }
+void foo66849() { }
+void foo66850() { }
+void foo66851() { }
+void foo66852() { }
+void foo66853() { }
+void foo66854() { }
+void foo66855() { }
+void foo66856() { }
+void foo66857() { }
+void foo66858() { }
+void foo66859() { }
+void foo66860() { }
+void foo66861() { }
+void foo66862() { }
+void foo66863() { }
+void foo66864() { }
+void foo66865() { }
+void foo66866() { }
+void foo66867() { }
+void foo66868() { }
+void foo66869() { }
+void foo66870() { }
+void foo66871() { }
+void foo66872() { }
+void foo66873() { }
+void foo66874() { }
+void foo66875() { }
+void foo66876() { }
+void foo66877() { }
+void foo66878() { }
+void foo66879() { }
+void foo66880() { }
+void foo66881() { }
+void foo66882() { }
+void foo66883() { }
+void foo66884() { }
+void foo66885() { }
+void foo66886() { }
+void foo66887() { }
+void foo66888() { }
+void foo66889() { }
+void foo66890() { }
+void foo66891() { }
+void foo66892() { }
+void foo66893() { }
+void foo66894() { }
+void foo66895() { }
+void foo66896() { }
+void foo66897() { }
+void foo66898() { }
+void foo66899() { }
+void foo66900() { }
+void foo66901() { }
+void foo66902() { }
+void foo66903() { }
+void foo66904() { }
+void foo66905() { }
+void foo66906() { }
+void foo66907() { }
+void foo66908() { }
+void foo66909() { }
+void foo66910() { }
+void foo66911() { }
+void foo66912() { }
+void foo66913() { }
+void foo66914() { }
+void foo66915() { }
+void foo66916() { }
+void foo66917() { }
+void foo66918() { }
+void foo66919() { }
+void foo66920() { }
+void foo66921() { }
+void foo66922() { }
+void foo66923() { }
+void foo66924() { }
+void foo66925() { }
+void foo66926() { }
+void foo66927() { }
+void foo66928() { }
+void foo66929() { }
+void foo66930() { }
+void foo66931() { }
+void foo66932() { }
+void foo66933() { }
+void foo66934() { }
+void foo66935() { }
+void foo66936() { }
+void foo66937() { }
+void foo66938() { }
+void foo66939() { }
+void foo66940() { }
+void foo66941() { }
+void foo66942() { }
+void foo66943() { }
+void foo66944() { }
+void foo66945() { }
+void foo66946() { }
+void foo66947() { }
+void foo66948() { }
+void foo66949() { }
+void foo66950() { }
+void foo66951() { }
+void foo66952() { }
+void foo66953() { }
+void foo66954() { }
+void foo66955() { }
+void foo66956() { }
+void foo66957() { }
+void foo66958() { }
+void foo66959() { }
+void foo66960() { }
+void foo66961() { }
+void foo66962() { }
+void foo66963() { }
+void foo66964() { }
+void foo66965() { }
+void foo66966() { }
+void foo66967() { }
+void foo66968() { }
+void foo66969() { }
+void foo66970() { }
+void foo66971() { }
+void foo66972() { }
+void foo66973() { }
+void foo66974() { }
+void foo66975() { }
+void foo66976() { }
+void foo66977() { }
+void foo66978() { }
+void foo66979() { }
+void foo66980() { }
+void foo66981() { }
+void foo66982() { }
+void foo66983() { }
+void foo66984() { }
+void foo66985() { }
+void foo66986() { }
+void foo66987() { }
+void foo66988() { }
+void foo66989() { }
+void foo66990() { }
+void foo66991() { }
+void foo66992() { }
+void foo66993() { }
+void foo66994() { }
+void foo66995() { }
+void foo66996() { }
+void foo66997() { }
+void foo66998() { }
+void foo66999() { }
+void foo67000() { }
+void foo67001() { }
+void foo67002() { }
+void foo67003() { }
+void foo67004() { }
+void foo67005() { }
+void foo67006() { }
+void foo67007() { }
+void foo67008() { }
+void foo67009() { }
+void foo67010() { }
+void foo67011() { }
+void foo67012() { }
+void foo67013() { }
+void foo67014() { }
+void foo67015() { }
+void foo67016() { }
+void foo67017() { }
+void foo67018() { }
+void foo67019() { }
+void foo67020() { }
+void foo67021() { }
+void foo67022() { }
+void foo67023() { }
+void foo67024() { }
+void foo67025() { }
+void foo67026() { }
+void foo67027() { }
+void foo67028() { }
+void foo67029() { }
+void foo67030() { }
+void foo67031() { }
+void foo67032() { }
+void foo67033() { }
+void foo67034() { }
+void foo67035() { }
+void foo67036() { }
+void foo67037() { }
+void foo67038() { }
+void foo67039() { }
+void foo67040() { }
+void foo67041() { }
+void foo67042() { }
+void foo67043() { }
+void foo67044() { }
+void foo67045() { }
+void foo67046() { }
+void foo67047() { }
+void foo67048() { }
+void foo67049() { }
+void foo67050() { }
+void foo67051() { }
+void foo67052() { }
+void foo67053() { }
+void foo67054() { }
+void foo67055() { }
+void foo67056() { }
+void foo67057() { }
+void foo67058() { }
+void foo67059() { }
+void foo67060() { }
+void foo67061() { }
+void foo67062() { }
+void foo67063() { }
+void foo67064() { }
+void foo67065() { }
+void foo67066() { }
+void foo67067() { }
+void foo67068() { }
+void foo67069() { }
+void foo67070() { }
+void foo67071() { }
+void foo67072() { }
+void foo67073() { }
+void foo67074() { }
+void foo67075() { }
+void foo67076() { }
+void foo67077() { }
+void foo67078() { }
+void foo67079() { }
+void foo67080() { }
+void foo67081() { }
+void foo67082() { }
+void foo67083() { }
+void foo67084() { }
+void foo67085() { }
+void foo67086() { }
+void foo67087() { }
+void foo67088() { }
+void foo67089() { }
+void foo67090() { }
+void foo67091() { }
+void foo67092() { }
+void foo67093() { }
+void foo67094() { }
+void foo67095() { }
+void foo67096() { }
+void foo67097() { }
+void foo67098() { }
+void foo67099() { }
+void foo67100() { }
+void foo67101() { }
+void foo67102() { }
+void foo67103() { }
+void foo67104() { }
+void foo67105() { }
+void foo67106() { }
+void foo67107() { }
+void foo67108() { }
+void foo67109() { }
+void foo67110() { }
+void foo67111() { }
+void foo67112() { }
+void foo67113() { }
+void foo67114() { }
+void foo67115() { }
+void foo67116() { }
+void foo67117() { }
+void foo67118() { }
+void foo67119() { }
+void foo67120() { }
+void foo67121() { }
+void foo67122() { }
+void foo67123() { }
+void foo67124() { }
+void foo67125() { }
+void foo67126() { }
+void foo67127() { }
+void foo67128() { }
+void foo67129() { }
+void foo67130() { }
+void foo67131() { }
+void foo67132() { }
+void foo67133() { }
+void foo67134() { }
+void foo67135() { }
+void foo67136() { }
+void foo67137() { }
+void foo67138() { }
+void foo67139() { }
+void foo67140() { }
+void foo67141() { }
+void foo67142() { }
+void foo67143() { }
+void foo67144() { }
+void foo67145() { }
+void foo67146() { }
+void foo67147() { }
+void foo67148() { }
+void foo67149() { }
+void foo67150() { }
+void foo67151() { }
+void foo67152() { }
+void foo67153() { }
+void foo67154() { }
+void foo67155() { }
+void foo67156() { }
+void foo67157() { }
+void foo67158() { }
+void foo67159() { }
+void foo67160() { }
+void foo67161() { }
+void foo67162() { }
+void foo67163() { }
+void foo67164() { }
+void foo67165() { }
+void foo67166() { }
+void foo67167() { }
+void foo67168() { }
+void foo67169() { }
+void foo67170() { }
+void foo67171() { }
+void foo67172() { }
+void foo67173() { }
+void foo67174() { }
+void foo67175() { }
+void foo67176() { }
+void foo67177() { }
+void foo67178() { }
+void foo67179() { }
+void foo67180() { }
+void foo67181() { }
+void foo67182() { }
+void foo67183() { }
+void foo67184() { }
+void foo67185() { }
+void foo67186() { }
+void foo67187() { }
+void foo67188() { }
+void foo67189() { }
+void foo67190() { }
+void foo67191() { }
+void foo67192() { }
+void foo67193() { }
+void foo67194() { }
+void foo67195() { }
+void foo67196() { }
+void foo67197() { }
+void foo67198() { }
+void foo67199() { }
+void foo67200() { }
+void foo67201() { }
+void foo67202() { }
+void foo67203() { }
+void foo67204() { }
+void foo67205() { }
+void foo67206() { }
+void foo67207() { }
+void foo67208() { }
+void foo67209() { }
+void foo67210() { }
+void foo67211() { }
+void foo67212() { }
+void foo67213() { }
+void foo67214() { }
+void foo67215() { }
+void foo67216() { }
+void foo67217() { }
+void foo67218() { }
+void foo67219() { }
+void foo67220() { }
+void foo67221() { }
+void foo67222() { }
+void foo67223() { }
+void foo67224() { }
+void foo67225() { }
+void foo67226() { }
+void foo67227() { }
+void foo67228() { }
+void foo67229() { }
+void foo67230() { }
+void foo67231() { }
+void foo67232() { }
+void foo67233() { }
+void foo67234() { }
+void foo67235() { }
+void foo67236() { }
+void foo67237() { }
+void foo67238() { }
+void foo67239() { }
+void foo67240() { }
+void foo67241() { }
+void foo67242() { }
+void foo67243() { }
+void foo67244() { }
+void foo67245() { }
+void foo67246() { }
+void foo67247() { }
+void foo67248() { }
+void foo67249() { }
+void foo67250() { }
+void foo67251() { }
+void foo67252() { }
+void foo67253() { }
+void foo67254() { }
+void foo67255() { }
+void foo67256() { }
+void foo67257() { }
+void foo67258() { }
+void foo67259() { }
+void foo67260() { }
+void foo67261() { }
+void foo67262() { }
+void foo67263() { }
+void foo67264() { }
+void foo67265() { }
+void foo67266() { }
+void foo67267() { }
+void foo67268() { }
+void foo67269() { }
+void foo67270() { }
+void foo67271() { }
+void foo67272() { }
+void foo67273() { }
+void foo67274() { }
+void foo67275() { }
+void foo67276() { }
+void foo67277() { }
+void foo67278() { }
+void foo67279() { }
+void foo67280() { }
+void foo67281() { }
+void foo67282() { }
+void foo67283() { }
+void foo67284() { }
+void foo67285() { }
+void foo67286() { }
+void foo67287() { }
+void foo67288() { }
+void foo67289() { }
+void foo67290() { }
+void foo67291() { }
+void foo67292() { }
+void foo67293() { }
+void foo67294() { }
+void foo67295() { }
+void foo67296() { }
+void foo67297() { }
+void foo67298() { }
+void foo67299() { }
+void foo67300() { }
+void foo67301() { }
+void foo67302() { }
+void foo67303() { }
+void foo67304() { }
+void foo67305() { }
+void foo67306() { }
+void foo67307() { }
+void foo67308() { }
+void foo67309() { }
+void foo67310() { }
+void foo67311() { }
+void foo67312() { }
+void foo67313() { }
+void foo67314() { }
+void foo67315() { }
+void foo67316() { }
+void foo67317() { }
+void foo67318() { }
+void foo67319() { }
+void foo67320() { }
+void foo67321() { }
+void foo67322() { }
+void foo67323() { }
+void foo67324() { }
+void foo67325() { }
+void foo67326() { }
+void foo67327() { }
+void foo67328() { }
+void foo67329() { }
+void foo67330() { }
+void foo67331() { }
+void foo67332() { }
+void foo67333() { }
+void foo67334() { }
+void foo67335() { }
+void foo67336() { }
+void foo67337() { }
+void foo67338() { }
+void foo67339() { }
+void foo67340() { }
+void foo67341() { }
+void foo67342() { }
+void foo67343() { }
+void foo67344() { }
+void foo67345() { }
+void foo67346() { }
+void foo67347() { }
+void foo67348() { }
+void foo67349() { }
+void foo67350() { }
+void foo67351() { }
+void foo67352() { }
+void foo67353() { }
+void foo67354() { }
+void foo67355() { }
+void foo67356() { }
+void foo67357() { }
+void foo67358() { }
+void foo67359() { }
+void foo67360() { }
+void foo67361() { }
+void foo67362() { }
+void foo67363() { }
+void foo67364() { }
+void foo67365() { }
+void foo67366() { }
+void foo67367() { }
+void foo67368() { }
+void foo67369() { }
+void foo67370() { }
+void foo67371() { }
+void foo67372() { }
+void foo67373() { }
+void foo67374() { }
+void foo67375() { }
+void foo67376() { }
+void foo67377() { }
+void foo67378() { }
+void foo67379() { }
+void foo67380() { }
+void foo67381() { }
+void foo67382() { }
+void foo67383() { }
+void foo67384() { }
+void foo67385() { }
+void foo67386() { }
+void foo67387() { }
+void foo67388() { }
+void foo67389() { }
+void foo67390() { }
+void foo67391() { }
+void foo67392() { }
+void foo67393() { }
+void foo67394() { }
+void foo67395() { }
+void foo67396() { }
+void foo67397() { }
+void foo67398() { }
+void foo67399() { }
+void foo67400() { }
+void foo67401() { }
+void foo67402() { }
+void foo67403() { }
+void foo67404() { }
+void foo67405() { }
+void foo67406() { }
+void foo67407() { }
+void foo67408() { }
+void foo67409() { }
+void foo67410() { }
+void foo67411() { }
+void foo67412() { }
+void foo67413() { }
+void foo67414() { }
+void foo67415() { }
+void foo67416() { }
+void foo67417() { }
+void foo67418() { }
+void foo67419() { }
+void foo67420() { }
+void foo67421() { }
+void foo67422() { }
+void foo67423() { }
+void foo67424() { }
+void foo67425() { }
+void foo67426() { }
+void foo67427() { }
+void foo67428() { }
+void foo67429() { }
+void foo67430() { }
+void foo67431() { }
+void foo67432() { }
+void foo67433() { }
+void foo67434() { }
+void foo67435() { }
+void foo67436() { }
+void foo67437() { }
+void foo67438() { }
+void foo67439() { }
+void foo67440() { }
+void foo67441() { }
+void foo67442() { }
+void foo67443() { }
+void foo67444() { }
+void foo67445() { }
+void foo67446() { }
+void foo67447() { }
+void foo67448() { }
+void foo67449() { }
+void foo67450() { }
+void foo67451() { }
+void foo67452() { }
+void foo67453() { }
+void foo67454() { }
+void foo67455() { }
+void foo67456() { }
+void foo67457() { }
+void foo67458() { }
+void foo67459() { }
+void foo67460() { }
+void foo67461() { }
+void foo67462() { }
+void foo67463() { }
+void foo67464() { }
+void foo67465() { }
+void foo67466() { }
+void foo67467() { }
+void foo67468() { }
+void foo67469() { }
+void foo67470() { }
+void foo67471() { }
+void foo67472() { }
+void foo67473() { }
+void foo67474() { }
+void foo67475() { }
+void foo67476() { }
+void foo67477() { }
+void foo67478() { }
+void foo67479() { }
+void foo67480() { }
+void foo67481() { }
+void foo67482() { }
+void foo67483() { }
+void foo67484() { }
+void foo67485() { }
+void foo67486() { }
+void foo67487() { }
+void foo67488() { }
+void foo67489() { }
+void foo67490() { }
+void foo67491() { }
+void foo67492() { }
+void foo67493() { }
+void foo67494() { }
+void foo67495() { }
+void foo67496() { }
+void foo67497() { }
+void foo67498() { }
+void foo67499() { }
+void foo67500() { }
+void foo67501() { }
+void foo67502() { }
+void foo67503() { }
+void foo67504() { }
+void foo67505() { }
+void foo67506() { }
+void foo67507() { }
+void foo67508() { }
+void foo67509() { }
+void foo67510() { }
+void foo67511() { }
+void foo67512() { }
+void foo67513() { }
+void foo67514() { }
+void foo67515() { }
+void foo67516() { }
+void foo67517() { }
+void foo67518() { }
+void foo67519() { }
+void foo67520() { }
+void foo67521() { }
+void foo67522() { }
+void foo67523() { }
+void foo67524() { }
+void foo67525() { }
+void foo67526() { }
+void foo67527() { }
+void foo67528() { }
+void foo67529() { }
+void foo67530() { }
+void foo67531() { }
+void foo67532() { }
+void foo67533() { }
+void foo67534() { }
+void foo67535() { }
+void foo67536() { }
+void foo67537() { }
+void foo67538() { }
+void foo67539() { }
+void foo67540() { }
+void foo67541() { }
+void foo67542() { }
+void foo67543() { }
+void foo67544() { }
+void foo67545() { }
+void foo67546() { }
+void foo67547() { }
+void foo67548() { }
+void foo67549() { }
+void foo67550() { }
+void foo67551() { }
+void foo67552() { }
+void foo67553() { }
+void foo67554() { }
+void foo67555() { }
+void foo67556() { }
+void foo67557() { }
+void foo67558() { }
+void foo67559() { }
+void foo67560() { }
+void foo67561() { }
+void foo67562() { }
+void foo67563() { }
+void foo67564() { }
+void foo67565() { }
+void foo67566() { }
+void foo67567() { }
+void foo67568() { }
+void foo67569() { }
+void foo67570() { }
+void foo67571() { }
+void foo67572() { }
+void foo67573() { }
+void foo67574() { }
+void foo67575() { }
+void foo67576() { }
+void foo67577() { }
+void foo67578() { }
+void foo67579() { }
+void foo67580() { }
+void foo67581() { }
+void foo67582() { }
+void foo67583() { }
+void foo67584() { }
+void foo67585() { }
+void foo67586() { }
+void foo67587() { }
+void foo67588() { }
+void foo67589() { }
+void foo67590() { }
+void foo67591() { }
+void foo67592() { }
+void foo67593() { }
+void foo67594() { }
+void foo67595() { }
+void foo67596() { }
+void foo67597() { }
+void foo67598() { }
+void foo67599() { }
+void foo67600() { }
+void foo67601() { }
+void foo67602() { }
+void foo67603() { }
+void foo67604() { }
+void foo67605() { }
+void foo67606() { }
+void foo67607() { }
+void foo67608() { }
+void foo67609() { }
+void foo67610() { }
+void foo67611() { }
+void foo67612() { }
+void foo67613() { }
+void foo67614() { }
+void foo67615() { }
+void foo67616() { }
+void foo67617() { }
+void foo67618() { }
+void foo67619() { }
+void foo67620() { }
+void foo67621() { }
+void foo67622() { }
+void foo67623() { }
+void foo67624() { }
+void foo67625() { }
+void foo67626() { }
+void foo67627() { }
+void foo67628() { }
+void foo67629() { }
+void foo67630() { }
+void foo67631() { }
+void foo67632() { }
+void foo67633() { }
+void foo67634() { }
+void foo67635() { }
+void foo67636() { }
+void foo67637() { }
+void foo67638() { }
+void foo67639() { }
+void foo67640() { }
+void foo67641() { }
+void foo67642() { }
+void foo67643() { }
+void foo67644() { }
+void foo67645() { }
+void foo67646() { }
+void foo67647() { }
+void foo67648() { }
+void foo67649() { }
+void foo67650() { }
+void foo67651() { }
+void foo67652() { }
+void foo67653() { }
+void foo67654() { }
+void foo67655() { }
+void foo67656() { }
+void foo67657() { }
+void foo67658() { }
+void foo67659() { }
+void foo67660() { }
+void foo67661() { }
+void foo67662() { }
+void foo67663() { }
+void foo67664() { }
+void foo67665() { }
+void foo67666() { }
+void foo67667() { }
+void foo67668() { }
+void foo67669() { }
+void foo67670() { }
+void foo67671() { }
+void foo67672() { }
+void foo67673() { }
+void foo67674() { }
+void foo67675() { }
+void foo67676() { }
+void foo67677() { }
+void foo67678() { }
+void foo67679() { }
+void foo67680() { }
+void foo67681() { }
+void foo67682() { }
+void foo67683() { }
+void foo67684() { }
+void foo67685() { }
+void foo67686() { }
+void foo67687() { }
+void foo67688() { }
+void foo67689() { }
+void foo67690() { }
+void foo67691() { }
+void foo67692() { }
+void foo67693() { }
+void foo67694() { }
+void foo67695() { }
+void foo67696() { }
+void foo67697() { }
+void foo67698() { }
+void foo67699() { }
+void foo67700() { }
+void foo67701() { }
+void foo67702() { }
+void foo67703() { }
+void foo67704() { }
+void foo67705() { }
+void foo67706() { }
+void foo67707() { }
+void foo67708() { }
+void foo67709() { }
+void foo67710() { }
+void foo67711() { }
+void foo67712() { }
+void foo67713() { }
+void foo67714() { }
+void foo67715() { }
+void foo67716() { }
+void foo67717() { }
+void foo67718() { }
+void foo67719() { }
+void foo67720() { }
+void foo67721() { }
+void foo67722() { }
+void foo67723() { }
+void foo67724() { }
+void foo67725() { }
+void foo67726() { }
+void foo67727() { }
+void foo67728() { }
+void foo67729() { }
+void foo67730() { }
+void foo67731() { }
+void foo67732() { }
+void foo67733() { }
+void foo67734() { }
+void foo67735() { }
+void foo67736() { }
+void foo67737() { }
+void foo67738() { }
+void foo67739() { }
+void foo67740() { }
+void foo67741() { }
+void foo67742() { }
+void foo67743() { }
+void foo67744() { }
+void foo67745() { }
+void foo67746() { }
+void foo67747() { }
+void foo67748() { }
+void foo67749() { }
+void foo67750() { }
+void foo67751() { }
+void foo67752() { }
+void foo67753() { }
+void foo67754() { }
+void foo67755() { }
+void foo67756() { }
+void foo67757() { }
+void foo67758() { }
+void foo67759() { }
+void foo67760() { }
+void foo67761() { }
+void foo67762() { }
+void foo67763() { }
+void foo67764() { }
+void foo67765() { }
+void foo67766() { }
+void foo67767() { }
+void foo67768() { }
+void foo67769() { }
+void foo67770() { }
+void foo67771() { }
+void foo67772() { }
+void foo67773() { }
+void foo67774() { }
+void foo67775() { }
+void foo67776() { }
+void foo67777() { }
+void foo67778() { }
+void foo67779() { }
+void foo67780() { }
+void foo67781() { }
+void foo67782() { }
+void foo67783() { }
+void foo67784() { }
+void foo67785() { }
+void foo67786() { }
+void foo67787() { }
+void foo67788() { }
+void foo67789() { }
+void foo67790() { }
+void foo67791() { }
+void foo67792() { }
+void foo67793() { }
+void foo67794() { }
+void foo67795() { }
+void foo67796() { }
+void foo67797() { }
+void foo67798() { }
+void foo67799() { }
+void foo67800() { }
+void foo67801() { }
+void foo67802() { }
+void foo67803() { }
+void foo67804() { }
+void foo67805() { }
+void foo67806() { }
+void foo67807() { }
+void foo67808() { }
+void foo67809() { }
+void foo67810() { }
+void foo67811() { }
+void foo67812() { }
+void foo67813() { }
+void foo67814() { }
+void foo67815() { }
+void foo67816() { }
+void foo67817() { }
+void foo67818() { }
+void foo67819() { }
+void foo67820() { }
+void foo67821() { }
+void foo67822() { }
+void foo67823() { }
+void foo67824() { }
+void foo67825() { }
+void foo67826() { }
+void foo67827() { }
+void foo67828() { }
+void foo67829() { }
+void foo67830() { }
+void foo67831() { }
+void foo67832() { }
+void foo67833() { }
+void foo67834() { }
+void foo67835() { }
+void foo67836() { }
+void foo67837() { }
+void foo67838() { }
+void foo67839() { }
+void foo67840() { }
+void foo67841() { }
+void foo67842() { }
+void foo67843() { }
+void foo67844() { }
+void foo67845() { }
+void foo67846() { }
+void foo67847() { }
+void foo67848() { }
+void foo67849() { }
+void foo67850() { }
+void foo67851() { }
+void foo67852() { }
+void foo67853() { }
+void foo67854() { }
+void foo67855() { }
+void foo67856() { }
+void foo67857() { }
+void foo67858() { }
+void foo67859() { }
+void foo67860() { }
+void foo67861() { }
+void foo67862() { }
+void foo67863() { }
+void foo67864() { }
+void foo67865() { }
+void foo67866() { }
+void foo67867() { }
+void foo67868() { }
+void foo67869() { }
+void foo67870() { }
+void foo67871() { }
+void foo67872() { }
+void foo67873() { }
+void foo67874() { }
+void foo67875() { }
+void foo67876() { }
+void foo67877() { }
+void foo67878() { }
+void foo67879() { }
+void foo67880() { }
+void foo67881() { }
+void foo67882() { }
+void foo67883() { }
+void foo67884() { }
+void foo67885() { }
+void foo67886() { }
+void foo67887() { }
+void foo67888() { }
+void foo67889() { }
+void foo67890() { }
+void foo67891() { }
+void foo67892() { }
+void foo67893() { }
+void foo67894() { }
+void foo67895() { }
+void foo67896() { }
+void foo67897() { }
+void foo67898() { }
+void foo67899() { }
+void foo67900() { }
+void foo67901() { }
+void foo67902() { }
+void foo67903() { }
+void foo67904() { }
+void foo67905() { }
+void foo67906() { }
+void foo67907() { }
+void foo67908() { }
+void foo67909() { }
+void foo67910() { }
+void foo67911() { }
+void foo67912() { }
+void foo67913() { }
+void foo67914() { }
+void foo67915() { }
+void foo67916() { }
+void foo67917() { }
+void foo67918() { }
+void foo67919() { }
+void foo67920() { }
+void foo67921() { }
+void foo67922() { }
+void foo67923() { }
+void foo67924() { }
+void foo67925() { }
+void foo67926() { }
+void foo67927() { }
+void foo67928() { }
+void foo67929() { }
+void foo67930() { }
+void foo67931() { }
+void foo67932() { }
+void foo67933() { }
+void foo67934() { }
+void foo67935() { }
+void foo67936() { }
+void foo67937() { }
+void foo67938() { }
+void foo67939() { }
+void foo67940() { }
+void foo67941() { }
+void foo67942() { }
+void foo67943() { }
+void foo67944() { }
+void foo67945() { }
+void foo67946() { }
+void foo67947() { }
+void foo67948() { }
+void foo67949() { }
+void foo67950() { }
+void foo67951() { }
+void foo67952() { }
+void foo67953() { }
+void foo67954() { }
+void foo67955() { }
+void foo67956() { }
+void foo67957() { }
+void foo67958() { }
+void foo67959() { }
+void foo67960() { }
+void foo67961() { }
+void foo67962() { }
+void foo67963() { }
+void foo67964() { }
+void foo67965() { }
+void foo67966() { }
+void foo67967() { }
+void foo67968() { }
+void foo67969() { }
+void foo67970() { }
+void foo67971() { }
+void foo67972() { }
+void foo67973() { }
+void foo67974() { }
+void foo67975() { }
+void foo67976() { }
+void foo67977() { }
+void foo67978() { }
+void foo67979() { }
+void foo67980() { }
+void foo67981() { }
+void foo67982() { }
+void foo67983() { }
+void foo67984() { }
+void foo67985() { }
+void foo67986() { }
+void foo67987() { }
+void foo67988() { }
+void foo67989() { }
+void foo67990() { }
+void foo67991() { }
+void foo67992() { }
+void foo67993() { }
+void foo67994() { }
+void foo67995() { }
+void foo67996() { }
+void foo67997() { }
+void foo67998() { }
+void foo67999() { }
+void foo68000() { }
+void foo68001() { }
+void foo68002() { }
+void foo68003() { }
+void foo68004() { }
+void foo68005() { }
+void foo68006() { }
+void foo68007() { }
+void foo68008() { }
+void foo68009() { }
+void foo68010() { }
+void foo68011() { }
+void foo68012() { }
+void foo68013() { }
+void foo68014() { }
+void foo68015() { }
+void foo68016() { }
+void foo68017() { }
+void foo68018() { }
+void foo68019() { }
+void foo68020() { }
+void foo68021() { }
+void foo68022() { }
+void foo68023() { }
+void foo68024() { }
+void foo68025() { }
+void foo68026() { }
+void foo68027() { }
+void foo68028() { }
+void foo68029() { }
+void foo68030() { }
+void foo68031() { }
+void foo68032() { }
+void foo68033() { }
+void foo68034() { }
+void foo68035() { }
+void foo68036() { }
+void foo68037() { }
+void foo68038() { }
+void foo68039() { }
+void foo68040() { }
+void foo68041() { }
+void foo68042() { }
+void foo68043() { }
+void foo68044() { }
+void foo68045() { }
+void foo68046() { }
+void foo68047() { }
+void foo68048() { }
+void foo68049() { }
+void foo68050() { }
+void foo68051() { }
+void foo68052() { }
+void foo68053() { }
+void foo68054() { }
+void foo68055() { }
+void foo68056() { }
+void foo68057() { }
+void foo68058() { }
+void foo68059() { }
+void foo68060() { }
+void foo68061() { }
+void foo68062() { }
+void foo68063() { }
+void foo68064() { }
+void foo68065() { }
+void foo68066() { }
+void foo68067() { }
+void foo68068() { }
+void foo68069() { }
+void foo68070() { }
+void foo68071() { }
+void foo68072() { }
+void foo68073() { }
+void foo68074() { }
+void foo68075() { }
+void foo68076() { }
+void foo68077() { }
+void foo68078() { }
+void foo68079() { }
+void foo68080() { }
+void foo68081() { }
+void foo68082() { }
+void foo68083() { }
+void foo68084() { }
+void foo68085() { }
+void foo68086() { }
+void foo68087() { }
+void foo68088() { }
+void foo68089() { }
+void foo68090() { }
+void foo68091() { }
+void foo68092() { }
+void foo68093() { }
+void foo68094() { }
+void foo68095() { }
+void foo68096() { }
+void foo68097() { }
+void foo68098() { }
+void foo68099() { }
+void foo68100() { }
+void foo68101() { }
+void foo68102() { }
+void foo68103() { }
+void foo68104() { }
+void foo68105() { }
+void foo68106() { }
+void foo68107() { }
+void foo68108() { }
+void foo68109() { }
+void foo68110() { }
+void foo68111() { }
+void foo68112() { }
+void foo68113() { }
+void foo68114() { }
+void foo68115() { }
+void foo68116() { }
+void foo68117() { }
+void foo68118() { }
+void foo68119() { }
+void foo68120() { }
+void foo68121() { }
+void foo68122() { }
+void foo68123() { }
+void foo68124() { }
+void foo68125() { }
+void foo68126() { }
+void foo68127() { }
+void foo68128() { }
+void foo68129() { }
+void foo68130() { }
+void foo68131() { }
+void foo68132() { }
+void foo68133() { }
+void foo68134() { }
+void foo68135() { }
+void foo68136() { }
+void foo68137() { }
+void foo68138() { }
+void foo68139() { }
+void foo68140() { }
+void foo68141() { }
+void foo68142() { }
+void foo68143() { }
+void foo68144() { }
+void foo68145() { }
+void foo68146() { }
+void foo68147() { }
+void foo68148() { }
+void foo68149() { }
+void foo68150() { }
+void foo68151() { }
+void foo68152() { }
+void foo68153() { }
+void foo68154() { }
+void foo68155() { }
+void foo68156() { }
+void foo68157() { }
+void foo68158() { }
+void foo68159() { }
+void foo68160() { }
+void foo68161() { }
+void foo68162() { }
+void foo68163() { }
+void foo68164() { }
+void foo68165() { }
+void foo68166() { }
+void foo68167() { }
+void foo68168() { }
+void foo68169() { }
+void foo68170() { }
+void foo68171() { }
+void foo68172() { }
+void foo68173() { }
+void foo68174() { }
+void foo68175() { }
+void foo68176() { }
+void foo68177() { }
+void foo68178() { }
+void foo68179() { }
+void foo68180() { }
+void foo68181() { }
+void foo68182() { }
+void foo68183() { }
+void foo68184() { }
+void foo68185() { }
+void foo68186() { }
+void foo68187() { }
+void foo68188() { }
+void foo68189() { }
+void foo68190() { }
+void foo68191() { }
+void foo68192() { }
+void foo68193() { }
+void foo68194() { }
+void foo68195() { }
+void foo68196() { }
+void foo68197() { }
+void foo68198() { }
+void foo68199() { }
+void foo68200() { }
+void foo68201() { }
+void foo68202() { }
+void foo68203() { }
+void foo68204() { }
+void foo68205() { }
+void foo68206() { }
+void foo68207() { }
+void foo68208() { }
+void foo68209() { }
+void foo68210() { }
+void foo68211() { }
+void foo68212() { }
+void foo68213() { }
+void foo68214() { }
+void foo68215() { }
+void foo68216() { }
+void foo68217() { }
+void foo68218() { }
+void foo68219() { }
+void foo68220() { }
+void foo68221() { }
+void foo68222() { }
+void foo68223() { }
+void foo68224() { }
+void foo68225() { }
+void foo68226() { }
+void foo68227() { }
+void foo68228() { }
+void foo68229() { }
+void foo68230() { }
+void foo68231() { }
+void foo68232() { }
+void foo68233() { }
+void foo68234() { }
+void foo68235() { }
+void foo68236() { }
+void foo68237() { }
+void foo68238() { }
+void foo68239() { }
+void foo68240() { }
+void foo68241() { }
+void foo68242() { }
+void foo68243() { }
+void foo68244() { }
+void foo68245() { }
+void foo68246() { }
+void foo68247() { }
+void foo68248() { }
+void foo68249() { }
+void foo68250() { }
+void foo68251() { }
+void foo68252() { }
+void foo68253() { }
+void foo68254() { }
+void foo68255() { }
+void foo68256() { }
+void foo68257() { }
+void foo68258() { }
+void foo68259() { }
+void foo68260() { }
+void foo68261() { }
+void foo68262() { }
+void foo68263() { }
+void foo68264() { }
+void foo68265() { }
+void foo68266() { }
+void foo68267() { }
+void foo68268() { }
+void foo68269() { }
+void foo68270() { }
+void foo68271() { }
+void foo68272() { }
+void foo68273() { }
+void foo68274() { }
+void foo68275() { }
+void foo68276() { }
+void foo68277() { }
+void foo68278() { }
+void foo68279() { }
+void foo68280() { }
+void foo68281() { }
+void foo68282() { }
+void foo68283() { }
+void foo68284() { }
+void foo68285() { }
+void foo68286() { }
+void foo68287() { }
+void foo68288() { }
+void foo68289() { }
+void foo68290() { }
+void foo68291() { }
+void foo68292() { }
+void foo68293() { }
+void foo68294() { }
+void foo68295() { }
+void foo68296() { }
+void foo68297() { }
+void foo68298() { }
+void foo68299() { }
+void foo68300() { }
+void foo68301() { }
+void foo68302() { }
+void foo68303() { }
+void foo68304() { }
+void foo68305() { }
+void foo68306() { }
+void foo68307() { }
+void foo68308() { }
+void foo68309() { }
+void foo68310() { }
+void foo68311() { }
+void foo68312() { }
+void foo68313() { }
+void foo68314() { }
+void foo68315() { }
+void foo68316() { }
+void foo68317() { }
+void foo68318() { }
+void foo68319() { }
+void foo68320() { }
+void foo68321() { }
+void foo68322() { }
+void foo68323() { }
+void foo68324() { }
+void foo68325() { }
+void foo68326() { }
+void foo68327() { }
+void foo68328() { }
+void foo68329() { }
+void foo68330() { }
+void foo68331() { }
+void foo68332() { }
+void foo68333() { }
+void foo68334() { }
+void foo68335() { }
+void foo68336() { }
+void foo68337() { }
+void foo68338() { }
+void foo68339() { }
+void foo68340() { }
+void foo68341() { }
+void foo68342() { }
+void foo68343() { }
+void foo68344() { }
+void foo68345() { }
+void foo68346() { }
+void foo68347() { }
+void foo68348() { }
+void foo68349() { }
+void foo68350() { }
+void foo68351() { }
+void foo68352() { }
+void foo68353() { }
+void foo68354() { }
+void foo68355() { }
+void foo68356() { }
+void foo68357() { }
+void foo68358() { }
+void foo68359() { }
+void foo68360() { }
+void foo68361() { }
+void foo68362() { }
+void foo68363() { }
+void foo68364() { }
+void foo68365() { }
+void foo68366() { }
+void foo68367() { }
+void foo68368() { }
+void foo68369() { }
+void foo68370() { }
+void foo68371() { }
+void foo68372() { }
+void foo68373() { }
+void foo68374() { }
+void foo68375() { }
+void foo68376() { }
+void foo68377() { }
+void foo68378() { }
+void foo68379() { }
+void foo68380() { }
+void foo68381() { }
+void foo68382() { }
+void foo68383() { }
+void foo68384() { }
+void foo68385() { }
+void foo68386() { }
+void foo68387() { }
+void foo68388() { }
+void foo68389() { }
+void foo68390() { }
+void foo68391() { }
+void foo68392() { }
+void foo68393() { }
+void foo68394() { }
+void foo68395() { }
+void foo68396() { }
+void foo68397() { }
+void foo68398() { }
+void foo68399() { }
+void foo68400() { }
+void foo68401() { }
+void foo68402() { }
+void foo68403() { }
+void foo68404() { }
+void foo68405() { }
+void foo68406() { }
+void foo68407() { }
+void foo68408() { }
+void foo68409() { }
+void foo68410() { }
+void foo68411() { }
+void foo68412() { }
+void foo68413() { }
+void foo68414() { }
+void foo68415() { }
+void foo68416() { }
+void foo68417() { }
+void foo68418() { }
+void foo68419() { }
+void foo68420() { }
+void foo68421() { }
+void foo68422() { }
+void foo68423() { }
+void foo68424() { }
+void foo68425() { }
+void foo68426() { }
+void foo68427() { }
+void foo68428() { }
+void foo68429() { }
+void foo68430() { }
+void foo68431() { }
+void foo68432() { }
+void foo68433() { }
+void foo68434() { }
+void foo68435() { }
+void foo68436() { }
+void foo68437() { }
+void foo68438() { }
+void foo68439() { }
+void foo68440() { }
+void foo68441() { }
+void foo68442() { }
+void foo68443() { }
+void foo68444() { }
+void foo68445() { }
+void foo68446() { }
+void foo68447() { }
+void foo68448() { }
+void foo68449() { }
+void foo68450() { }
+void foo68451() { }
+void foo68452() { }
+void foo68453() { }
+void foo68454() { }
+void foo68455() { }
+void foo68456() { }
+void foo68457() { }
+void foo68458() { }
+void foo68459() { }
+void foo68460() { }
+void foo68461() { }
+void foo68462() { }
+void foo68463() { }
+void foo68464() { }
+void foo68465() { }
+void foo68466() { }
+void foo68467() { }
+void foo68468() { }
+void foo68469() { }
+void foo68470() { }
+void foo68471() { }
+void foo68472() { }
+void foo68473() { }
+void foo68474() { }
+void foo68475() { }
+void foo68476() { }
+void foo68477() { }
+void foo68478() { }
+void foo68479() { }
+void foo68480() { }
+void foo68481() { }
+void foo68482() { }
+void foo68483() { }
+void foo68484() { }
+void foo68485() { }
+void foo68486() { }
+void foo68487() { }
+void foo68488() { }
+void foo68489() { }
+void foo68490() { }
+void foo68491() { }
+void foo68492() { }
+void foo68493() { }
+void foo68494() { }
+void foo68495() { }
+void foo68496() { }
+void foo68497() { }
+void foo68498() { }
+void foo68499() { }
+void foo68500() { }
+void foo68501() { }
+void foo68502() { }
+void foo68503() { }
+void foo68504() { }
+void foo68505() { }
+void foo68506() { }
+void foo68507() { }
+void foo68508() { }
+void foo68509() { }
+void foo68510() { }
+void foo68511() { }
+void foo68512() { }
+void foo68513() { }
+void foo68514() { }
+void foo68515() { }
+void foo68516() { }
+void foo68517() { }
+void foo68518() { }
+void foo68519() { }
+void foo68520() { }
+void foo68521() { }
+void foo68522() { }
+void foo68523() { }
+void foo68524() { }
+void foo68525() { }
+void foo68526() { }
+void foo68527() { }
+void foo68528() { }
+void foo68529() { }
+void foo68530() { }
+void foo68531() { }
+void foo68532() { }
+void foo68533() { }
+void foo68534() { }
+void foo68535() { }
+void foo68536() { }
+void foo68537() { }
+void foo68538() { }
+void foo68539() { }
+void foo68540() { }
+void foo68541() { }
+void foo68542() { }
+void foo68543() { }
+void foo68544() { }
+void foo68545() { }
+void foo68546() { }
+void foo68547() { }
+void foo68548() { }
+void foo68549() { }
+void foo68550() { }
+void foo68551() { }
+void foo68552() { }
+void foo68553() { }
+void foo68554() { }
+void foo68555() { }
+void foo68556() { }
+void foo68557() { }
+void foo68558() { }
+void foo68559() { }
+void foo68560() { }
+void foo68561() { }
+void foo68562() { }
+void foo68563() { }
+void foo68564() { }
+void foo68565() { }
+void foo68566() { }
+void foo68567() { }
+void foo68568() { }
+void foo68569() { }
+void foo68570() { }
+void foo68571() { }
+void foo68572() { }
+void foo68573() { }
+void foo68574() { }
+void foo68575() { }
+void foo68576() { }
+void foo68577() { }
+void foo68578() { }
+void foo68579() { }
+void foo68580() { }
+void foo68581() { }
+void foo68582() { }
+void foo68583() { }
+void foo68584() { }
+void foo68585() { }
+void foo68586() { }
+void foo68587() { }
+void foo68588() { }
+void foo68589() { }
+void foo68590() { }
+void foo68591() { }
+void foo68592() { }
+void foo68593() { }
+void foo68594() { }
+void foo68595() { }
+void foo68596() { }
+void foo68597() { }
+void foo68598() { }
+void foo68599() { }
+void foo68600() { }
+void foo68601() { }
+void foo68602() { }
+void foo68603() { }
+void foo68604() { }
+void foo68605() { }
+void foo68606() { }
+void foo68607() { }
+void foo68608() { }
+void foo68609() { }
+void foo68610() { }
+void foo68611() { }
+void foo68612() { }
+void foo68613() { }
+void foo68614() { }
+void foo68615() { }
+void foo68616() { }
+void foo68617() { }
+void foo68618() { }
+void foo68619() { }
+void foo68620() { }
+void foo68621() { }
+void foo68622() { }
+void foo68623() { }
+void foo68624() { }
+void foo68625() { }
+void foo68626() { }
+void foo68627() { }
+void foo68628() { }
+void foo68629() { }
+void foo68630() { }
+void foo68631() { }
+void foo68632() { }
+void foo68633() { }
+void foo68634() { }
+void foo68635() { }
+void foo68636() { }
+void foo68637() { }
+void foo68638() { }
+void foo68639() { }
+void foo68640() { }
+void foo68641() { }
+void foo68642() { }
+void foo68643() { }
+void foo68644() { }
+void foo68645() { }
+void foo68646() { }
+void foo68647() { }
+void foo68648() { }
+void foo68649() { }
+void foo68650() { }
+void foo68651() { }
+void foo68652() { }
+void foo68653() { }
+void foo68654() { }
+void foo68655() { }
+void foo68656() { }
+void foo68657() { }
+void foo68658() { }
+void foo68659() { }
+void foo68660() { }
+void foo68661() { }
+void foo68662() { }
+void foo68663() { }
+void foo68664() { }
+void foo68665() { }
+void foo68666() { }
+void foo68667() { }
+void foo68668() { }
+void foo68669() { }
+void foo68670() { }
+void foo68671() { }
+void foo68672() { }
+void foo68673() { }
+void foo68674() { }
+void foo68675() { }
+void foo68676() { }
+void foo68677() { }
+void foo68678() { }
+void foo68679() { }
+void foo68680() { }
+void foo68681() { }
+void foo68682() { }
+void foo68683() { }
+void foo68684() { }
+void foo68685() { }
+void foo68686() { }
+void foo68687() { }
+void foo68688() { }
+void foo68689() { }
+void foo68690() { }
+void foo68691() { }
+void foo68692() { }
+void foo68693() { }
+void foo68694() { }
+void foo68695() { }
+void foo68696() { }
+void foo68697() { }
+void foo68698() { }
+void foo68699() { }
+void foo68700() { }
+void foo68701() { }
+void foo68702() { }
+void foo68703() { }
+void foo68704() { }
+void foo68705() { }
+void foo68706() { }
+void foo68707() { }
+void foo68708() { }
+void foo68709() { }
+void foo68710() { }
+void foo68711() { }
+void foo68712() { }
+void foo68713() { }
+void foo68714() { }
+void foo68715() { }
+void foo68716() { }
+void foo68717() { }
+void foo68718() { }
+void foo68719() { }
+void foo68720() { }
+void foo68721() { }
+void foo68722() { }
+void foo68723() { }
+void foo68724() { }
+void foo68725() { }
+void foo68726() { }
+void foo68727() { }
+void foo68728() { }
+void foo68729() { }
+void foo68730() { }
+void foo68731() { }
+void foo68732() { }
+void foo68733() { }
+void foo68734() { }
+void foo68735() { }
+void foo68736() { }
+void foo68737() { }
+void foo68738() { }
+void foo68739() { }
+void foo68740() { }
+void foo68741() { }
+void foo68742() { }
+void foo68743() { }
+void foo68744() { }
+void foo68745() { }
+void foo68746() { }
+void foo68747() { }
+void foo68748() { }
+void foo68749() { }
+void foo68750() { }
+void foo68751() { }
+void foo68752() { }
+void foo68753() { }
+void foo68754() { }
+void foo68755() { }
+void foo68756() { }
+void foo68757() { }
+void foo68758() { }
+void foo68759() { }
+void foo68760() { }
+void foo68761() { }
+void foo68762() { }
+void foo68763() { }
+void foo68764() { }
+void foo68765() { }
+void foo68766() { }
+void foo68767() { }
+void foo68768() { }
+void foo68769() { }
+void foo68770() { }
+void foo68771() { }
+void foo68772() { }
+void foo68773() { }
+void foo68774() { }
+void foo68775() { }
+void foo68776() { }
+void foo68777() { }
+void foo68778() { }
+void foo68779() { }
+void foo68780() { }
+void foo68781() { }
+void foo68782() { }
+void foo68783() { }
+void foo68784() { }
+void foo68785() { }
+void foo68786() { }
+void foo68787() { }
+void foo68788() { }
+void foo68789() { }
+void foo68790() { }
+void foo68791() { }
+void foo68792() { }
+void foo68793() { }
+void foo68794() { }
+void foo68795() { }
+void foo68796() { }
+void foo68797() { }
+void foo68798() { }
+void foo68799() { }
+void foo68800() { }
+void foo68801() { }
+void foo68802() { }
+void foo68803() { }
+void foo68804() { }
+void foo68805() { }
+void foo68806() { }
+void foo68807() { }
+void foo68808() { }
+void foo68809() { }
+void foo68810() { }
+void foo68811() { }
+void foo68812() { }
+void foo68813() { }
+void foo68814() { }
+void foo68815() { }
+void foo68816() { }
+void foo68817() { }
+void foo68818() { }
+void foo68819() { }
+void foo68820() { }
+void foo68821() { }
+void foo68822() { }
+void foo68823() { }
+void foo68824() { }
+void foo68825() { }
+void foo68826() { }
+void foo68827() { }
+void foo68828() { }
+void foo68829() { }
+void foo68830() { }
+void foo68831() { }
+void foo68832() { }
+void foo68833() { }
+void foo68834() { }
+void foo68835() { }
+void foo68836() { }
+void foo68837() { }
+void foo68838() { }
+void foo68839() { }
+void foo68840() { }
+void foo68841() { }
+void foo68842() { }
+void foo68843() { }
+void foo68844() { }
+void foo68845() { }
+void foo68846() { }
+void foo68847() { }
+void foo68848() { }
+void foo68849() { }
+void foo68850() { }
+void foo68851() { }
+void foo68852() { }
+void foo68853() { }
+void foo68854() { }
+void foo68855() { }
+void foo68856() { }
+void foo68857() { }
+void foo68858() { }
+void foo68859() { }
+void foo68860() { }
+void foo68861() { }
+void foo68862() { }
+void foo68863() { }
+void foo68864() { }
+void foo68865() { }
+void foo68866() { }
+void foo68867() { }
+void foo68868() { }
+void foo68869() { }
+void foo68870() { }
+void foo68871() { }
+void foo68872() { }
+void foo68873() { }
+void foo68874() { }
+void foo68875() { }
+void foo68876() { }
+void foo68877() { }
+void foo68878() { }
+void foo68879() { }
+void foo68880() { }
+void foo68881() { }
+void foo68882() { }
+void foo68883() { }
+void foo68884() { }
+void foo68885() { }
+void foo68886() { }
+void foo68887() { }
+void foo68888() { }
+void foo68889() { }
+void foo68890() { }
+void foo68891() { }
+void foo68892() { }
+void foo68893() { }
+void foo68894() { }
+void foo68895() { }
+void foo68896() { }
+void foo68897() { }
+void foo68898() { }
+void foo68899() { }
+void foo68900() { }
+void foo68901() { }
+void foo68902() { }
+void foo68903() { }
+void foo68904() { }
+void foo68905() { }
+void foo68906() { }
+void foo68907() { }
+void foo68908() { }
+void foo68909() { }
+void foo68910() { }
+void foo68911() { }
+void foo68912() { }
+void foo68913() { }
+void foo68914() { }
+void foo68915() { }
+void foo68916() { }
+void foo68917() { }
+void foo68918() { }
+void foo68919() { }
+void foo68920() { }
+void foo68921() { }
+void foo68922() { }
+void foo68923() { }
+void foo68924() { }
+void foo68925() { }
+void foo68926() { }
+void foo68927() { }
+void foo68928() { }
+void foo68929() { }
+void foo68930() { }
+void foo68931() { }
+void foo68932() { }
+void foo68933() { }
+void foo68934() { }
+void foo68935() { }
+void foo68936() { }
+void foo68937() { }
+void foo68938() { }
+void foo68939() { }
+void foo68940() { }
+void foo68941() { }
+void foo68942() { }
+void foo68943() { }
+void foo68944() { }
+void foo68945() { }
+void foo68946() { }
+void foo68947() { }
+void foo68948() { }
+void foo68949() { }
+void foo68950() { }
+void foo68951() { }
+void foo68952() { }
+void foo68953() { }
+void foo68954() { }
+void foo68955() { }
+void foo68956() { }
+void foo68957() { }
+void foo68958() { }
+void foo68959() { }
+void foo68960() { }
+void foo68961() { }
+void foo68962() { }
+void foo68963() { }
+void foo68964() { }
+void foo68965() { }
+void foo68966() { }
+void foo68967() { }
+void foo68968() { }
+void foo68969() { }
+void foo68970() { }
+void foo68971() { }
+void foo68972() { }
+void foo68973() { }
+void foo68974() { }
+void foo68975() { }
+void foo68976() { }
+void foo68977() { }
+void foo68978() { }
+void foo68979() { }
+void foo68980() { }
+void foo68981() { }
+void foo68982() { }
+void foo68983() { }
+void foo68984() { }
+void foo68985() { }
+void foo68986() { }
+void foo68987() { }
+void foo68988() { }
+void foo68989() { }
+void foo68990() { }
+void foo68991() { }
+void foo68992() { }
+void foo68993() { }
+void foo68994() { }
+void foo68995() { }
+void foo68996() { }
+void foo68997() { }
+void foo68998() { }
+void foo68999() { }
+void foo69000() { }
+void foo69001() { }
+void foo69002() { }
+void foo69003() { }
+void foo69004() { }
+void foo69005() { }
+void foo69006() { }
+void foo69007() { }
+void foo69008() { }
+void foo69009() { }
+void foo69010() { }
+void foo69011() { }
+void foo69012() { }
+void foo69013() { }
+void foo69014() { }
+void foo69015() { }
+void foo69016() { }
+void foo69017() { }
+void foo69018() { }
+void foo69019() { }
+void foo69020() { }
+void foo69021() { }
+void foo69022() { }
+void foo69023() { }
+void foo69024() { }
+void foo69025() { }
+void foo69026() { }
+void foo69027() { }
+void foo69028() { }
+void foo69029() { }
+void foo69030() { }
+void foo69031() { }
+void foo69032() { }
+void foo69033() { }
+void foo69034() { }
+void foo69035() { }
+void foo69036() { }
+void foo69037() { }
+void foo69038() { }
+void foo69039() { }
+void foo69040() { }
+void foo69041() { }
+void foo69042() { }
+void foo69043() { }
+void foo69044() { }
+void foo69045() { }
+void foo69046() { }
+void foo69047() { }
+void foo69048() { }
+void foo69049() { }
+void foo69050() { }
+void foo69051() { }
+void foo69052() { }
+void foo69053() { }
+void foo69054() { }
+void foo69055() { }
+void foo69056() { }
+void foo69057() { }
+void foo69058() { }
+void foo69059() { }
+void foo69060() { }
+void foo69061() { }
+void foo69062() { }
+void foo69063() { }
+void foo69064() { }
+void foo69065() { }
+void foo69066() { }
+void foo69067() { }
+void foo69068() { }
+void foo69069() { }
+void foo69070() { }
+void foo69071() { }
+void foo69072() { }
+void foo69073() { }
+void foo69074() { }
+void foo69075() { }
+void foo69076() { }
+void foo69077() { }
+void foo69078() { }
+void foo69079() { }
+void foo69080() { }
+void foo69081() { }
+void foo69082() { }
+void foo69083() { }
+void foo69084() { }
+void foo69085() { }
+void foo69086() { }
+void foo69087() { }
+void foo69088() { }
+void foo69089() { }
+void foo69090() { }
+void foo69091() { }
+void foo69092() { }
+void foo69093() { }
+void foo69094() { }
+void foo69095() { }
+void foo69096() { }
+void foo69097() { }
+void foo69098() { }
+void foo69099() { }
+void foo69100() { }
+void foo69101() { }
+void foo69102() { }
+void foo69103() { }
+void foo69104() { }
+void foo69105() { }
+void foo69106() { }
+void foo69107() { }
+void foo69108() { }
+void foo69109() { }
+void foo69110() { }
+void foo69111() { }
+void foo69112() { }
+void foo69113() { }
+void foo69114() { }
+void foo69115() { }
+void foo69116() { }
+void foo69117() { }
+void foo69118() { }
+void foo69119() { }
+void foo69120() { }
+void foo69121() { }
+void foo69122() { }
+void foo69123() { }
+void foo69124() { }
+void foo69125() { }
+void foo69126() { }
+void foo69127() { }
+void foo69128() { }
+void foo69129() { }
+void foo69130() { }
+void foo69131() { }
+void foo69132() { }
+void foo69133() { }
+void foo69134() { }
+void foo69135() { }
+void foo69136() { }
+void foo69137() { }
+void foo69138() { }
+void foo69139() { }
+void foo69140() { }
+void foo69141() { }
+void foo69142() { }
+void foo69143() { }
+void foo69144() { }
+void foo69145() { }
+void foo69146() { }
+void foo69147() { }
+void foo69148() { }
+void foo69149() { }
+void foo69150() { }
+void foo69151() { }
+void foo69152() { }
+void foo69153() { }
+void foo69154() { }
+void foo69155() { }
+void foo69156() { }
+void foo69157() { }
+void foo69158() { }
+void foo69159() { }
+void foo69160() { }
+void foo69161() { }
+void foo69162() { }
+void foo69163() { }
+void foo69164() { }
+void foo69165() { }
+void foo69166() { }
+void foo69167() { }
+void foo69168() { }
+void foo69169() { }
+void foo69170() { }
+void foo69171() { }
+void foo69172() { }
+void foo69173() { }
+void foo69174() { }
+void foo69175() { }
+void foo69176() { }
+void foo69177() { }
+void foo69178() { }
+void foo69179() { }
+void foo69180() { }
+void foo69181() { }
+void foo69182() { }
+void foo69183() { }
+void foo69184() { }
+void foo69185() { }
+void foo69186() { }
+void foo69187() { }
+void foo69188() { }
+void foo69189() { }
+void foo69190() { }
+void foo69191() { }
+void foo69192() { }
+void foo69193() { }
+void foo69194() { }
+void foo69195() { }
+void foo69196() { }
+void foo69197() { }
+void foo69198() { }
+void foo69199() { }
+void foo69200() { }
+void foo69201() { }
+void foo69202() { }
+void foo69203() { }
+void foo69204() { }
+void foo69205() { }
+void foo69206() { }
+void foo69207() { }
+void foo69208() { }
+void foo69209() { }
+void foo69210() { }
+void foo69211() { }
+void foo69212() { }
+void foo69213() { }
+void foo69214() { }
+void foo69215() { }
+void foo69216() { }
+void foo69217() { }
+void foo69218() { }
+void foo69219() { }
+void foo69220() { }
+void foo69221() { }
+void foo69222() { }
+void foo69223() { }
+void foo69224() { }
+void foo69225() { }
+void foo69226() { }
+void foo69227() { }
+void foo69228() { }
+void foo69229() { }
+void foo69230() { }
+void foo69231() { }
+void foo69232() { }
+void foo69233() { }
+void foo69234() { }
+void foo69235() { }
+void foo69236() { }
+void foo69237() { }
+void foo69238() { }
+void foo69239() { }
+void foo69240() { }
+void foo69241() { }
+void foo69242() { }
+void foo69243() { }
+void foo69244() { }
+void foo69245() { }
+void foo69246() { }
+void foo69247() { }
+void foo69248() { }
+void foo69249() { }
+void foo69250() { }
+void foo69251() { }
+void foo69252() { }
+void foo69253() { }
+void foo69254() { }
+void foo69255() { }
+void foo69256() { }
+void foo69257() { }
+void foo69258() { }
+void foo69259() { }
+void foo69260() { }
+void foo69261() { }
+void foo69262() { }
+void foo69263() { }
+void foo69264() { }
+void foo69265() { }
+void foo69266() { }
+void foo69267() { }
+void foo69268() { }
+void foo69269() { }
+void foo69270() { }
+void foo69271() { }
+void foo69272() { }
+void foo69273() { }
+void foo69274() { }
+void foo69275() { }
+void foo69276() { }
+void foo69277() { }
+void foo69278() { }
+void foo69279() { }
+void foo69280() { }
+void foo69281() { }
+void foo69282() { }
+void foo69283() { }
+void foo69284() { }
+void foo69285() { }
+void foo69286() { }
+void foo69287() { }
+void foo69288() { }
+void foo69289() { }
+void foo69290() { }
+void foo69291() { }
+void foo69292() { }
+void foo69293() { }
+void foo69294() { }
+void foo69295() { }
+void foo69296() { }
+void foo69297() { }
+void foo69298() { }
+void foo69299() { }
+void foo69300() { }
+void foo69301() { }
+void foo69302() { }
+void foo69303() { }
+void foo69304() { }
+void foo69305() { }
+void foo69306() { }
+void foo69307() { }
+void foo69308() { }
+void foo69309() { }
+void foo69310() { }
+void foo69311() { }
+void foo69312() { }
+void foo69313() { }
+void foo69314() { }
+void foo69315() { }
+void foo69316() { }
+void foo69317() { }
+void foo69318() { }
+void foo69319() { }
+void foo69320() { }
+void foo69321() { }
+void foo69322() { }
+void foo69323() { }
+void foo69324() { }
+void foo69325() { }
+void foo69326() { }
+void foo69327() { }
+void foo69328() { }
+void foo69329() { }
+void foo69330() { }
+void foo69331() { }
+void foo69332() { }
+void foo69333() { }
+void foo69334() { }
+void foo69335() { }
+void foo69336() { }
+void foo69337() { }
+void foo69338() { }
+void foo69339() { }
+void foo69340() { }
+void foo69341() { }
+void foo69342() { }
+void foo69343() { }
+void foo69344() { }
+void foo69345() { }
+void foo69346() { }
+void foo69347() { }
+void foo69348() { }
+void foo69349() { }
+void foo69350() { }
+void foo69351() { }
+void foo69352() { }
+void foo69353() { }
+void foo69354() { }
+void foo69355() { }
+void foo69356() { }
+void foo69357() { }
+void foo69358() { }
+void foo69359() { }
+void foo69360() { }
+void foo69361() { }
+void foo69362() { }
+void foo69363() { }
+void foo69364() { }
+void foo69365() { }
+void foo69366() { }
+void foo69367() { }
+void foo69368() { }
+void foo69369() { }
+void foo69370() { }
+void foo69371() { }
+void foo69372() { }
+void foo69373() { }
+void foo69374() { }
+void foo69375() { }
+void foo69376() { }
+void foo69377() { }
+void foo69378() { }
+void foo69379() { }
+void foo69380() { }
+void foo69381() { }
+void foo69382() { }
+void foo69383() { }
+void foo69384() { }
+void foo69385() { }
+void foo69386() { }
+void foo69387() { }
+void foo69388() { }
+void foo69389() { }
+void foo69390() { }
+void foo69391() { }
+void foo69392() { }
+void foo69393() { }
+void foo69394() { }
+void foo69395() { }
+void foo69396() { }
+void foo69397() { }
+void foo69398() { }
+void foo69399() { }
+void foo69400() { }
+void foo69401() { }
+void foo69402() { }
+void foo69403() { }
+void foo69404() { }
+void foo69405() { }
+void foo69406() { }
+void foo69407() { }
+void foo69408() { }
+void foo69409() { }
+void foo69410() { }
+void foo69411() { }
+void foo69412() { }
+void foo69413() { }
+void foo69414() { }
+void foo69415() { }
+void foo69416() { }
+void foo69417() { }
+void foo69418() { }
+void foo69419() { }
+void foo69420() { }
+void foo69421() { }
+void foo69422() { }
+void foo69423() { }
+void foo69424() { }
+void foo69425() { }
+void foo69426() { }
+void foo69427() { }
+void foo69428() { }
+void foo69429() { }
+void foo69430() { }
+void foo69431() { }
+void foo69432() { }
+void foo69433() { }
+void foo69434() { }
+void foo69435() { }
+void foo69436() { }
+void foo69437() { }
+void foo69438() { }
+void foo69439() { }
+void foo69440() { }
+void foo69441() { }
+void foo69442() { }
+void foo69443() { }
+void foo69444() { }
+void foo69445() { }
+void foo69446() { }
+void foo69447() { }
+void foo69448() { }
+void foo69449() { }
+void foo69450() { }
+void foo69451() { }
+void foo69452() { }
+void foo69453() { }
+void foo69454() { }
+void foo69455() { }
+void foo69456() { }
+void foo69457() { }
+void foo69458() { }
+void foo69459() { }
+void foo69460() { }
+void foo69461() { }
+void foo69462() { }
+void foo69463() { }
+void foo69464() { }
+void foo69465() { }
+void foo69466() { }
+void foo69467() { }
+void foo69468() { }
+void foo69469() { }
+void foo69470() { }
+void foo69471() { }
+void foo69472() { }
+void foo69473() { }
+void foo69474() { }
+void foo69475() { }
+void foo69476() { }
+void foo69477() { }
+void foo69478() { }
+void foo69479() { }
+void foo69480() { }
+void foo69481() { }
+void foo69482() { }
+void foo69483() { }
+void foo69484() { }
+void foo69485() { }
+void foo69486() { }
+void foo69487() { }
+void foo69488() { }
+void foo69489() { }
+void foo69490() { }
+void foo69491() { }
+void foo69492() { }
+void foo69493() { }
+void foo69494() { }
+void foo69495() { }
+void foo69496() { }
+void foo69497() { }
+void foo69498() { }
+void foo69499() { }
+void foo69500() { }
+void foo69501() { }
+void foo69502() { }
+void foo69503() { }
+void foo69504() { }
+void foo69505() { }
+void foo69506() { }
+void foo69507() { }
+void foo69508() { }
+void foo69509() { }
+void foo69510() { }
+void foo69511() { }
+void foo69512() { }
+void foo69513() { }
+void foo69514() { }
+void foo69515() { }
+void foo69516() { }
+void foo69517() { }
+void foo69518() { }
+void foo69519() { }
+void foo69520() { }
+void foo69521() { }
+void foo69522() { }
+void foo69523() { }
+void foo69524() { }
+void foo69525() { }
+void foo69526() { }
+void foo69527() { }
+void foo69528() { }
+void foo69529() { }
+void foo69530() { }
+void foo69531() { }
+void foo69532() { }
+void foo69533() { }
+void foo69534() { }
+void foo69535() { }
+void foo69536() { }
+void foo69537() { }
+void foo69538() { }
+void foo69539() { }
+void foo69540() { }
+void foo69541() { }
+void foo69542() { }
+void foo69543() { }
+void foo69544() { }
+void foo69545() { }
+void foo69546() { }
+void foo69547() { }
+void foo69548() { }
+void foo69549() { }
+void foo69550() { }
+void foo69551() { }
+void foo69552() { }
+void foo69553() { }
+void foo69554() { }
+void foo69555() { }
+void foo69556() { }
+void foo69557() { }
+void foo69558() { }
+void foo69559() { }
+void foo69560() { }
+void foo69561() { }
+void foo69562() { }
+void foo69563() { }
+void foo69564() { }
+void foo69565() { }
+void foo69566() { }
+void foo69567() { }
+void foo69568() { }
+void foo69569() { }
+void foo69570() { }
+void foo69571() { }
+void foo69572() { }
+void foo69573() { }
+void foo69574() { }
+void foo69575() { }
+void foo69576() { }
+void foo69577() { }
+void foo69578() { }
+void foo69579() { }
+void foo69580() { }
+void foo69581() { }
+void foo69582() { }
+void foo69583() { }
+void foo69584() { }
+void foo69585() { }
+void foo69586() { }
+void foo69587() { }
+void foo69588() { }
+void foo69589() { }
+void foo69590() { }
+void foo69591() { }
+void foo69592() { }
+void foo69593() { }
+void foo69594() { }
+void foo69595() { }
+void foo69596() { }
+void foo69597() { }
+void foo69598() { }
+void foo69599() { }
+void foo69600() { }
+void foo69601() { }
+void foo69602() { }
+void foo69603() { }
+void foo69604() { }
+void foo69605() { }
+void foo69606() { }
+void foo69607() { }
+void foo69608() { }
+void foo69609() { }
+void foo69610() { }
+void foo69611() { }
+void foo69612() { }
+void foo69613() { }
+void foo69614() { }
+void foo69615() { }
+void foo69616() { }
+void foo69617() { }
+void foo69618() { }
+void foo69619() { }
+void foo69620() { }
+void foo69621() { }
+void foo69622() { }
+void foo69623() { }
+void foo69624() { }
+void foo69625() { }
+void foo69626() { }
+void foo69627() { }
+void foo69628() { }
+void foo69629() { }
+void foo69630() { }
+void foo69631() { }
+void foo69632() { }
+void foo69633() { }
+void foo69634() { }
+void foo69635() { }
+void foo69636() { }
+void foo69637() { }
+void foo69638() { }
+void foo69639() { }
+void foo69640() { }
+void foo69641() { }
+void foo69642() { }
+void foo69643() { }
+void foo69644() { }
+void foo69645() { }
+void foo69646() { }
+void foo69647() { }
+void foo69648() { }
+void foo69649() { }
+void foo69650() { }
+void foo69651() { }
+void foo69652() { }
+void foo69653() { }
+void foo69654() { }
+void foo69655() { }
+void foo69656() { }
+void foo69657() { }
+void foo69658() { }
+void foo69659() { }
+void foo69660() { }
+void foo69661() { }
+void foo69662() { }
+void foo69663() { }
+void foo69664() { }
+void foo69665() { }
+void foo69666() { }
+void foo69667() { }
+void foo69668() { }
+void foo69669() { }
+void foo69670() { }
+void foo69671() { }
+void foo69672() { }
+void foo69673() { }
+void foo69674() { }
+void foo69675() { }
+void foo69676() { }
+void foo69677() { }
+void foo69678() { }
+void foo69679() { }
+void foo69680() { }
+void foo69681() { }
+void foo69682() { }
+void foo69683() { }
+void foo69684() { }
+void foo69685() { }
+void foo69686() { }
+void foo69687() { }
+void foo69688() { }
+void foo69689() { }
+void foo69690() { }
+void foo69691() { }
+void foo69692() { }
+void foo69693() { }
+void foo69694() { }
+void foo69695() { }
+void foo69696() { }
+void foo69697() { }
+void foo69698() { }
+void foo69699() { }
+void foo69700() { }
+void foo69701() { }
+void foo69702() { }
+void foo69703() { }
+void foo69704() { }
+void foo69705() { }
+void foo69706() { }
+void foo69707() { }
+void foo69708() { }
+void foo69709() { }
+void foo69710() { }
+void foo69711() { }
+void foo69712() { }
+void foo69713() { }
+void foo69714() { }
+void foo69715() { }
+void foo69716() { }
+void foo69717() { }
+void foo69718() { }
+void foo69719() { }
+void foo69720() { }
+void foo69721() { }
+void foo69722() { }
+void foo69723() { }
+void foo69724() { }
+void foo69725() { }
+void foo69726() { }
+void foo69727() { }
+void foo69728() { }
+void foo69729() { }
+void foo69730() { }
+void foo69731() { }
+void foo69732() { }
+void foo69733() { }
+void foo69734() { }
+void foo69735() { }
+void foo69736() { }
+void foo69737() { }
+void foo69738() { }
+void foo69739() { }
+void foo69740() { }
+void foo69741() { }
+void foo69742() { }
+void foo69743() { }
+void foo69744() { }
+void foo69745() { }
+void foo69746() { }
+void foo69747() { }
+void foo69748() { }
+void foo69749() { }
+void foo69750() { }
+void foo69751() { }
+void foo69752() { }
+void foo69753() { }
+void foo69754() { }
+void foo69755() { }
+void foo69756() { }
+void foo69757() { }
+void foo69758() { }
+void foo69759() { }
+void foo69760() { }
+void foo69761() { }
+void foo69762() { }
+void foo69763() { }
+void foo69764() { }
+void foo69765() { }
+void foo69766() { }
+void foo69767() { }
+void foo69768() { }
+void foo69769() { }
+void foo69770() { }
+void foo69771() { }
+void foo69772() { }
+void foo69773() { }
+void foo69774() { }
+void foo69775() { }
+void foo69776() { }
+void foo69777() { }
+void foo69778() { }
+void foo69779() { }
+void foo69780() { }
+void foo69781() { }
+void foo69782() { }
+void foo69783() { }
+void foo69784() { }
+void foo69785() { }
+void foo69786() { }
+void foo69787() { }
+void foo69788() { }
+void foo69789() { }
+void foo69790() { }
+void foo69791() { }
+void foo69792() { }
+void foo69793() { }
+void foo69794() { }
+void foo69795() { }
+void foo69796() { }
+void foo69797() { }
+void foo69798() { }
+void foo69799() { }
+void foo69800() { }
+void foo69801() { }
+void foo69802() { }
+void foo69803() { }
+void foo69804() { }
+void foo69805() { }
+void foo69806() { }
+void foo69807() { }
+void foo69808() { }
+void foo69809() { }
+void foo69810() { }
+void foo69811() { }
+void foo69812() { }
+void foo69813() { }
+void foo69814() { }
+void foo69815() { }
+void foo69816() { }
+void foo69817() { }
+void foo69818() { }
+void foo69819() { }
+void foo69820() { }
+void foo69821() { }
+void foo69822() { }
+void foo69823() { }
+void foo69824() { }
+void foo69825() { }
+void foo69826() { }
+void foo69827() { }
+void foo69828() { }
+void foo69829() { }
+void foo69830() { }
+void foo69831() { }
+void foo69832() { }
+void foo69833() { }
+void foo69834() { }
+void foo69835() { }
+void foo69836() { }
+void foo69837() { }
+void foo69838() { }
+void foo69839() { }
+void foo69840() { }
+void foo69841() { }
+void foo69842() { }
+void foo69843() { }
+void foo69844() { }
+void foo69845() { }
+void foo69846() { }
+void foo69847() { }
+void foo69848() { }
+void foo69849() { }
+void foo69850() { }
+void foo69851() { }
+void foo69852() { }
+void foo69853() { }
+void foo69854() { }
+void foo69855() { }
+void foo69856() { }
+void foo69857() { }
+void foo69858() { }
+void foo69859() { }
+void foo69860() { }
+void foo69861() { }
+void foo69862() { }
+void foo69863() { }
+void foo69864() { }
+void foo69865() { }
+void foo69866() { }
+void foo69867() { }
+void foo69868() { }
+void foo69869() { }
+void foo69870() { }
+void foo69871() { }
+void foo69872() { }
+void foo69873() { }
+void foo69874() { }
+void foo69875() { }
+void foo69876() { }
+void foo69877() { }
+void foo69878() { }
+void foo69879() { }
+void foo69880() { }
+void foo69881() { }
+void foo69882() { }
+void foo69883() { }
+void foo69884() { }
+void foo69885() { }
+void foo69886() { }
+void foo69887() { }
+void foo69888() { }
+void foo69889() { }
+void foo69890() { }
+void foo69891() { }
+void foo69892() { }
+void foo69893() { }
+void foo69894() { }
+void foo69895() { }
+void foo69896() { }
+void foo69897() { }
+void foo69898() { }
+void foo69899() { }
+void foo69900() { }
+void foo69901() { }
+void foo69902() { }
+void foo69903() { }
+void foo69904() { }
+void foo69905() { }
+void foo69906() { }
+void foo69907() { }
+void foo69908() { }
+void foo69909() { }
+void foo69910() { }
+void foo69911() { }
+void foo69912() { }
+void foo69913() { }
+void foo69914() { }
+void foo69915() { }
+void foo69916() { }
+void foo69917() { }
+void foo69918() { }
+void foo69919() { }
+void foo69920() { }
+void foo69921() { }
+void foo69922() { }
+void foo69923() { }
+void foo69924() { }
+void foo69925() { }
+void foo69926() { }
+void foo69927() { }
+void foo69928() { }
+void foo69929() { }
+void foo69930() { }
+void foo69931() { }
+void foo69932() { }
+void foo69933() { }
+void foo69934() { }
+void foo69935() { }
+void foo69936() { }
+void foo69937() { }
+void foo69938() { }
+void foo69939() { }
+void foo69940() { }
+void foo69941() { }
+void foo69942() { }
+void foo69943() { }
+void foo69944() { }
+void foo69945() { }
+void foo69946() { }
+void foo69947() { }
+void foo69948() { }
+void foo69949() { }
+void foo69950() { }
+void foo69951() { }
+void foo69952() { }
+void foo69953() { }
+void foo69954() { }
+void foo69955() { }
+void foo69956() { }
+void foo69957() { }
+void foo69958() { }
+void foo69959() { }
+void foo69960() { }
+void foo69961() { }
+void foo69962() { }
+void foo69963() { }
+void foo69964() { }
+void foo69965() { }
+void foo69966() { }
+void foo69967() { }
+void foo69968() { }
+void foo69969() { }
+void foo69970() { }
+void foo69971() { }
+void foo69972() { }
+void foo69973() { }
+void foo69974() { }
+void foo69975() { }
+void foo69976() { }
+void foo69977() { }
+void foo69978() { }
+void foo69979() { }
+void foo69980() { }
+void foo69981() { }
+void foo69982() { }
+void foo69983() { }
+void foo69984() { }
+void foo69985() { }
+void foo69986() { }
+void foo69987() { }
+void foo69988() { }
+void foo69989() { }
+void foo69990() { }
+void foo69991() { }
+void foo69992() { }
+void foo69993() { }
+void foo69994() { }
+void foo69995() { }
+void foo69996() { }
+void foo69997() { }
+void foo69998() { }
+void foo69999() { }
+void foo70000() { }
index 77fe3abdf0de44271eb3f026f1faf5fe1b98ee9f..d0e5162807a6b84ca2d96829de705cb2d7b320fb 100644 (file)
@@ -64998,3 +64998,5003 @@ extern void foo64997();
 extern void foo64998();
 extern void foo64999();
 extern void foo65000();
+extern void foo65001();
+extern void foo65002();
+extern void foo65003();
+extern void foo65004();
+extern void foo65005();
+extern void foo65006();
+extern void foo65007();
+extern void foo65008();
+extern void foo65009();
+extern void foo65010();
+extern void foo65011();
+extern void foo65012();
+extern void foo65013();
+extern void foo65014();
+extern void foo65015();
+extern void foo65016();
+extern void foo65017();
+extern void foo65018();
+extern void foo65019();
+extern void foo65020();
+extern void foo65021();
+extern void foo65022();
+extern void foo65023();
+extern void foo65024();
+extern void foo65025();
+extern void foo65026();
+extern void foo65027();
+extern void foo65028();
+extern void foo65029();
+extern void foo65030();
+extern void foo65031();
+extern void foo65032();
+extern void foo65033();
+extern void foo65034();
+extern void foo65035();
+extern void foo65036();
+extern void foo65037();
+extern void foo65038();
+extern void foo65039();
+extern void foo65040();
+extern void foo65041();
+extern void foo65042();
+extern void foo65043();
+extern void foo65044();
+extern void foo65045();
+extern void foo65046();
+extern void foo65047();
+extern void foo65048();
+extern void foo65049();
+extern void foo65050();
+extern void foo65051();
+extern void foo65052();
+extern void foo65053();
+extern void foo65054();
+extern void foo65055();
+extern void foo65056();
+extern void foo65057();
+extern void foo65058();
+extern void foo65059();
+extern void foo65060();
+extern void foo65061();
+extern void foo65062();
+extern void foo65063();
+extern void foo65064();
+extern void foo65065();
+extern void foo65066();
+extern void foo65067();
+extern void foo65068();
+extern void foo65069();
+extern void foo65070();
+extern void foo65071();
+extern void foo65072();
+extern void foo65073();
+extern void foo65074();
+extern void foo65075();
+extern void foo65076();
+extern void foo65077();
+extern void foo65078();
+extern void foo65079();
+extern void foo65080();
+extern void foo65081();
+extern void foo65082();
+extern void foo65083();
+extern void foo65084();
+extern void foo65085();
+extern void foo65086();
+extern void foo65087();
+extern void foo65088();
+extern void foo65089();
+extern void foo65090();
+extern void foo65091();
+extern void foo65092();
+extern void foo65093();
+extern void foo65094();
+extern void foo65095();
+extern void foo65096();
+extern void foo65097();
+extern void foo65098();
+extern void foo65099();
+extern void foo65100();
+extern void foo65101();
+extern void foo65102();
+extern void foo65103();
+extern void foo65104();
+extern void foo65105();
+extern void foo65106();
+extern void foo65107();
+extern void foo65108();
+extern void foo65109();
+extern void foo65110();
+extern void foo65111();
+extern void foo65112();
+extern void foo65113();
+extern void foo65114();
+extern void foo65115();
+extern void foo65116();
+extern void foo65117();
+extern void foo65118();
+extern void foo65119();
+extern void foo65120();
+extern void foo65121();
+extern void foo65122();
+extern void foo65123();
+extern void foo65124();
+extern void foo65125();
+extern void foo65126();
+extern void foo65127();
+extern void foo65128();
+extern void foo65129();
+extern void foo65130();
+extern void foo65131();
+extern void foo65132();
+extern void foo65133();
+extern void foo65134();
+extern void foo65135();
+extern void foo65136();
+extern void foo65137();
+extern void foo65138();
+extern void foo65139();
+extern void foo65140();
+extern void foo65141();
+extern void foo65142();
+extern void foo65143();
+extern void foo65144();
+extern void foo65145();
+extern void foo65146();
+extern void foo65147();
+extern void foo65148();
+extern void foo65149();
+extern void foo65150();
+extern void foo65151();
+extern void foo65152();
+extern void foo65153();
+extern void foo65154();
+extern void foo65155();
+extern void foo65156();
+extern void foo65157();
+extern void foo65158();
+extern void foo65159();
+extern void foo65160();
+extern void foo65161();
+extern void foo65162();
+extern void foo65163();
+extern void foo65164();
+extern void foo65165();
+extern void foo65166();
+extern void foo65167();
+extern void foo65168();
+extern void foo65169();
+extern void foo65170();
+extern void foo65171();
+extern void foo65172();
+extern void foo65173();
+extern void foo65174();
+extern void foo65175();
+extern void foo65176();
+extern void foo65177();
+extern void foo65178();
+extern void foo65179();
+extern void foo65180();
+extern void foo65181();
+extern void foo65182();
+extern void foo65183();
+extern void foo65184();
+extern void foo65185();
+extern void foo65186();
+extern void foo65187();
+extern void foo65188();
+extern void foo65189();
+extern void foo65190();
+extern void foo65191();
+extern void foo65192();
+extern void foo65193();
+extern void foo65194();
+extern void foo65195();
+extern void foo65196();
+extern void foo65197();
+extern void foo65198();
+extern void foo65199();
+extern void foo65200();
+extern void foo65201();
+extern void foo65202();
+extern void foo65203();
+extern void foo65204();
+extern void foo65205();
+extern void foo65206();
+extern void foo65207();
+extern void foo65208();
+extern void foo65209();
+extern void foo65210();
+extern void foo65211();
+extern void foo65212();
+extern void foo65213();
+extern void foo65214();
+extern void foo65215();
+extern void foo65216();
+extern void foo65217();
+extern void foo65218();
+extern void foo65219();
+extern void foo65220();
+extern void foo65221();
+extern void foo65222();
+extern void foo65223();
+extern void foo65224();
+extern void foo65225();
+extern void foo65226();
+extern void foo65227();
+extern void foo65228();
+extern void foo65229();
+extern void foo65230();
+extern void foo65231();
+extern void foo65232();
+extern void foo65233();
+extern void foo65234();
+extern void foo65235();
+extern void foo65236();
+extern void foo65237();
+extern void foo65238();
+extern void foo65239();
+extern void foo65240();
+extern void foo65241();
+extern void foo65242();
+extern void foo65243();
+extern void foo65244();
+extern void foo65245();
+extern void foo65246();
+extern void foo65247();
+extern void foo65248();
+extern void foo65249();
+extern void foo65250();
+extern void foo65251();
+extern void foo65252();
+extern void foo65253();
+extern void foo65254();
+extern void foo65255();
+extern void foo65256();
+extern void foo65257();
+extern void foo65258();
+extern void foo65259();
+extern void foo65260();
+extern void foo65261();
+extern void foo65262();
+extern void foo65263();
+extern void foo65264();
+extern void foo65265();
+extern void foo65266();
+extern void foo65267();
+extern void foo65268();
+extern void foo65269();
+extern void foo65270();
+extern void foo65271();
+extern void foo65272();
+extern void foo65273();
+extern void foo65274();
+extern void foo65275();
+extern void foo65276();
+extern void foo65277();
+extern void foo65278();
+extern void foo65279();
+extern void foo65280();
+extern void foo65281();
+extern void foo65282();
+extern void foo65283();
+extern void foo65284();
+extern void foo65285();
+extern void foo65286();
+extern void foo65287();
+extern void foo65288();
+extern void foo65289();
+extern void foo65290();
+extern void foo65291();
+extern void foo65292();
+extern void foo65293();
+extern void foo65294();
+extern void foo65295();
+extern void foo65296();
+extern void foo65297();
+extern void foo65298();
+extern void foo65299();
+extern void foo65300();
+extern void foo65301();
+extern void foo65302();
+extern void foo65303();
+extern void foo65304();
+extern void foo65305();
+extern void foo65306();
+extern void foo65307();
+extern void foo65308();
+extern void foo65309();
+extern void foo65310();
+extern void foo65311();
+extern void foo65312();
+extern void foo65313();
+extern void foo65314();
+extern void foo65315();
+extern void foo65316();
+extern void foo65317();
+extern void foo65318();
+extern void foo65319();
+extern void foo65320();
+extern void foo65321();
+extern void foo65322();
+extern void foo65323();
+extern void foo65324();
+extern void foo65325();
+extern void foo65326();
+extern void foo65327();
+extern void foo65328();
+extern void foo65329();
+extern void foo65330();
+extern void foo65331();
+extern void foo65332();
+extern void foo65333();
+extern void foo65334();
+extern void foo65335();
+extern void foo65336();
+extern void foo65337();
+extern void foo65338();
+extern void foo65339();
+extern void foo65340();
+extern void foo65341();
+extern void foo65342();
+extern void foo65343();
+extern void foo65344();
+extern void foo65345();
+extern void foo65346();
+extern void foo65347();
+extern void foo65348();
+extern void foo65349();
+extern void foo65350();
+extern void foo65351();
+extern void foo65352();
+extern void foo65353();
+extern void foo65354();
+extern void foo65355();
+extern void foo65356();
+extern void foo65357();
+extern void foo65358();
+extern void foo65359();
+extern void foo65360();
+extern void foo65361();
+extern void foo65362();
+extern void foo65363();
+extern void foo65364();
+extern void foo65365();
+extern void foo65366();
+extern void foo65367();
+extern void foo65368();
+extern void foo65369();
+extern void foo65370();
+extern void foo65371();
+extern void foo65372();
+extern void foo65373();
+extern void foo65374();
+extern void foo65375();
+extern void foo65376();
+extern void foo65377();
+extern void foo65378();
+extern void foo65379();
+extern void foo65380();
+extern void foo65381();
+extern void foo65382();
+extern void foo65383();
+extern void foo65384();
+extern void foo65385();
+extern void foo65386();
+extern void foo65387();
+extern void foo65388();
+extern void foo65389();
+extern void foo65390();
+extern void foo65391();
+extern void foo65392();
+extern void foo65393();
+extern void foo65394();
+extern void foo65395();
+extern void foo65396();
+extern void foo65397();
+extern void foo65398();
+extern void foo65399();
+extern void foo65400();
+extern void foo65401();
+extern void foo65402();
+extern void foo65403();
+extern void foo65404();
+extern void foo65405();
+extern void foo65406();
+extern void foo65407();
+extern void foo65408();
+extern void foo65409();
+extern void foo65410();
+extern void foo65411();
+extern void foo65412();
+extern void foo65413();
+extern void foo65414();
+extern void foo65415();
+extern void foo65416();
+extern void foo65417();
+extern void foo65418();
+extern void foo65419();
+extern void foo65420();
+extern void foo65421();
+extern void foo65422();
+extern void foo65423();
+extern void foo65424();
+extern void foo65425();
+extern void foo65426();
+extern void foo65427();
+extern void foo65428();
+extern void foo65429();
+extern void foo65430();
+extern void foo65431();
+extern void foo65432();
+extern void foo65433();
+extern void foo65434();
+extern void foo65435();
+extern void foo65436();
+extern void foo65437();
+extern void foo65438();
+extern void foo65439();
+extern void foo65440();
+extern void foo65441();
+extern void foo65442();
+extern void foo65443();
+extern void foo65444();
+extern void foo65445();
+extern void foo65446();
+extern void foo65447();
+extern void foo65448();
+extern void foo65449();
+extern void foo65450();
+extern void foo65451();
+extern void foo65452();
+extern void foo65453();
+extern void foo65454();
+extern void foo65455();
+extern void foo65456();
+extern void foo65457();
+extern void foo65458();
+extern void foo65459();
+extern void foo65460();
+extern void foo65461();
+extern void foo65462();
+extern void foo65463();
+extern void foo65464();
+extern void foo65465();
+extern void foo65466();
+extern void foo65467();
+extern void foo65468();
+extern void foo65469();
+extern void foo65470();
+extern void foo65471();
+extern void foo65472();
+extern void foo65473();
+extern void foo65474();
+extern void foo65475();
+extern void foo65476();
+extern void foo65477();
+extern void foo65478();
+extern void foo65479();
+extern void foo65480();
+extern void foo65481();
+extern void foo65482();
+extern void foo65483();
+extern void foo65484();
+extern void foo65485();
+extern void foo65486();
+extern void foo65487();
+extern void foo65488();
+extern void foo65489();
+extern void foo65490();
+extern void foo65491();
+extern void foo65492();
+extern void foo65493();
+extern void foo65494();
+extern void foo65495();
+extern void foo65496();
+extern void foo65497();
+extern void foo65498();
+extern void foo65499();
+extern void foo65500();
+extern void foo65501();
+extern void foo65502();
+extern void foo65503();
+extern void foo65504();
+extern void foo65505();
+extern void foo65506();
+extern void foo65507();
+extern void foo65508();
+extern void foo65509();
+extern void foo65510();
+extern void foo65511();
+extern void foo65512();
+extern void foo65513();
+extern void foo65514();
+extern void foo65515();
+extern void foo65516();
+extern void foo65517();
+extern void foo65518();
+extern void foo65519();
+extern void foo65520();
+extern void foo65521();
+extern void foo65522();
+extern void foo65523();
+extern void foo65524();
+extern void foo65525();
+extern void foo65526();
+extern void foo65527();
+extern void foo65528();
+extern void foo65529();
+extern void foo65530();
+extern void foo65531();
+extern void foo65532();
+extern void foo65533();
+extern void foo65534();
+extern void foo65535();
+extern void foo65536();
+extern void foo65537();
+extern void foo65538();
+extern void foo65539();
+extern void foo65540();
+extern void foo65541();
+extern void foo65542();
+extern void foo65543();
+extern void foo65544();
+extern void foo65545();
+extern void foo65546();
+extern void foo65547();
+extern void foo65548();
+extern void foo65549();
+extern void foo65550();
+extern void foo65551();
+extern void foo65552();
+extern void foo65553();
+extern void foo65554();
+extern void foo65555();
+extern void foo65556();
+extern void foo65557();
+extern void foo65558();
+extern void foo65559();
+extern void foo65560();
+extern void foo65561();
+extern void foo65562();
+extern void foo65563();
+extern void foo65564();
+extern void foo65565();
+extern void foo65566();
+extern void foo65567();
+extern void foo65568();
+extern void foo65569();
+extern void foo65570();
+extern void foo65571();
+extern void foo65572();
+extern void foo65573();
+extern void foo65574();
+extern void foo65575();
+extern void foo65576();
+extern void foo65577();
+extern void foo65578();
+extern void foo65579();
+extern void foo65580();
+extern void foo65581();
+extern void foo65582();
+extern void foo65583();
+extern void foo65584();
+extern void foo65585();
+extern void foo65586();
+extern void foo65587();
+extern void foo65588();
+extern void foo65589();
+extern void foo65590();
+extern void foo65591();
+extern void foo65592();
+extern void foo65593();
+extern void foo65594();
+extern void foo65595();
+extern void foo65596();
+extern void foo65597();
+extern void foo65598();
+extern void foo65599();
+extern void foo65600();
+extern void foo65601();
+extern void foo65602();
+extern void foo65603();
+extern void foo65604();
+extern void foo65605();
+extern void foo65606();
+extern void foo65607();
+extern void foo65608();
+extern void foo65609();
+extern void foo65610();
+extern void foo65611();
+extern void foo65612();
+extern void foo65613();
+extern void foo65614();
+extern void foo65615();
+extern void foo65616();
+extern void foo65617();
+extern void foo65618();
+extern void foo65619();
+extern void foo65620();
+extern void foo65621();
+extern void foo65622();
+extern void foo65623();
+extern void foo65624();
+extern void foo65625();
+extern void foo65626();
+extern void foo65627();
+extern void foo65628();
+extern void foo65629();
+extern void foo65630();
+extern void foo65631();
+extern void foo65632();
+extern void foo65633();
+extern void foo65634();
+extern void foo65635();
+extern void foo65636();
+extern void foo65637();
+extern void foo65638();
+extern void foo65639();
+extern void foo65640();
+extern void foo65641();
+extern void foo65642();
+extern void foo65643();
+extern void foo65644();
+extern void foo65645();
+extern void foo65646();
+extern void foo65647();
+extern void foo65648();
+extern void foo65649();
+extern void foo65650();
+extern void foo65651();
+extern void foo65652();
+extern void foo65653();
+extern void foo65654();
+extern void foo65655();
+extern void foo65656();
+extern void foo65657();
+extern void foo65658();
+extern void foo65659();
+extern void foo65660();
+extern void foo65661();
+extern void foo65662();
+extern void foo65663();
+extern void foo65664();
+extern void foo65665();
+extern void foo65666();
+extern void foo65667();
+extern void foo65668();
+extern void foo65669();
+extern void foo65670();
+extern void foo65671();
+extern void foo65672();
+extern void foo65673();
+extern void foo65674();
+extern void foo65675();
+extern void foo65676();
+extern void foo65677();
+extern void foo65678();
+extern void foo65679();
+extern void foo65680();
+extern void foo65681();
+extern void foo65682();
+extern void foo65683();
+extern void foo65684();
+extern void foo65685();
+extern void foo65686();
+extern void foo65687();
+extern void foo65688();
+extern void foo65689();
+extern void foo65690();
+extern void foo65691();
+extern void foo65692();
+extern void foo65693();
+extern void foo65694();
+extern void foo65695();
+extern void foo65696();
+extern void foo65697();
+extern void foo65698();
+extern void foo65699();
+extern void foo65700();
+extern void foo65701();
+extern void foo65702();
+extern void foo65703();
+extern void foo65704();
+extern void foo65705();
+extern void foo65706();
+extern void foo65707();
+extern void foo65708();
+extern void foo65709();
+extern void foo65710();
+extern void foo65711();
+extern void foo65712();
+extern void foo65713();
+extern void foo65714();
+extern void foo65715();
+extern void foo65716();
+extern void foo65717();
+extern void foo65718();
+extern void foo65719();
+extern void foo65720();
+extern void foo65721();
+extern void foo65722();
+extern void foo65723();
+extern void foo65724();
+extern void foo65725();
+extern void foo65726();
+extern void foo65727();
+extern void foo65728();
+extern void foo65729();
+extern void foo65730();
+extern void foo65731();
+extern void foo65732();
+extern void foo65733();
+extern void foo65734();
+extern void foo65735();
+extern void foo65736();
+extern void foo65737();
+extern void foo65738();
+extern void foo65739();
+extern void foo65740();
+extern void foo65741();
+extern void foo65742();
+extern void foo65743();
+extern void foo65744();
+extern void foo65745();
+extern void foo65746();
+extern void foo65747();
+extern void foo65748();
+extern void foo65749();
+extern void foo65750();
+extern void foo65751();
+extern void foo65752();
+extern void foo65753();
+extern void foo65754();
+extern void foo65755();
+extern void foo65756();
+extern void foo65757();
+extern void foo65758();
+extern void foo65759();
+extern void foo65760();
+extern void foo65761();
+extern void foo65762();
+extern void foo65763();
+extern void foo65764();
+extern void foo65765();
+extern void foo65766();
+extern void foo65767();
+extern void foo65768();
+extern void foo65769();
+extern void foo65770();
+extern void foo65771();
+extern void foo65772();
+extern void foo65773();
+extern void foo65774();
+extern void foo65775();
+extern void foo65776();
+extern void foo65777();
+extern void foo65778();
+extern void foo65779();
+extern void foo65780();
+extern void foo65781();
+extern void foo65782();
+extern void foo65783();
+extern void foo65784();
+extern void foo65785();
+extern void foo65786();
+extern void foo65787();
+extern void foo65788();
+extern void foo65789();
+extern void foo65790();
+extern void foo65791();
+extern void foo65792();
+extern void foo65793();
+extern void foo65794();
+extern void foo65795();
+extern void foo65796();
+extern void foo65797();
+extern void foo65798();
+extern void foo65799();
+extern void foo65800();
+extern void foo65801();
+extern void foo65802();
+extern void foo65803();
+extern void foo65804();
+extern void foo65805();
+extern void foo65806();
+extern void foo65807();
+extern void foo65808();
+extern void foo65809();
+extern void foo65810();
+extern void foo65811();
+extern void foo65812();
+extern void foo65813();
+extern void foo65814();
+extern void foo65815();
+extern void foo65816();
+extern void foo65817();
+extern void foo65818();
+extern void foo65819();
+extern void foo65820();
+extern void foo65821();
+extern void foo65822();
+extern void foo65823();
+extern void foo65824();
+extern void foo65825();
+extern void foo65826();
+extern void foo65827();
+extern void foo65828();
+extern void foo65829();
+extern void foo65830();
+extern void foo65831();
+extern void foo65832();
+extern void foo65833();
+extern void foo65834();
+extern void foo65835();
+extern void foo65836();
+extern void foo65837();
+extern void foo65838();
+extern void foo65839();
+extern void foo65840();
+extern void foo65841();
+extern void foo65842();
+extern void foo65843();
+extern void foo65844();
+extern void foo65845();
+extern void foo65846();
+extern void foo65847();
+extern void foo65848();
+extern void foo65849();
+extern void foo65850();
+extern void foo65851();
+extern void foo65852();
+extern void foo65853();
+extern void foo65854();
+extern void foo65855();
+extern void foo65856();
+extern void foo65857();
+extern void foo65858();
+extern void foo65859();
+extern void foo65860();
+extern void foo65861();
+extern void foo65862();
+extern void foo65863();
+extern void foo65864();
+extern void foo65865();
+extern void foo65866();
+extern void foo65867();
+extern void foo65868();
+extern void foo65869();
+extern void foo65870();
+extern void foo65871();
+extern void foo65872();
+extern void foo65873();
+extern void foo65874();
+extern void foo65875();
+extern void foo65876();
+extern void foo65877();
+extern void foo65878();
+extern void foo65879();
+extern void foo65880();
+extern void foo65881();
+extern void foo65882();
+extern void foo65883();
+extern void foo65884();
+extern void foo65885();
+extern void foo65886();
+extern void foo65887();
+extern void foo65888();
+extern void foo65889();
+extern void foo65890();
+extern void foo65891();
+extern void foo65892();
+extern void foo65893();
+extern void foo65894();
+extern void foo65895();
+extern void foo65896();
+extern void foo65897();
+extern void foo65898();
+extern void foo65899();
+extern void foo65900();
+extern void foo65901();
+extern void foo65902();
+extern void foo65903();
+extern void foo65904();
+extern void foo65905();
+extern void foo65906();
+extern void foo65907();
+extern void foo65908();
+extern void foo65909();
+extern void foo65910();
+extern void foo65911();
+extern void foo65912();
+extern void foo65913();
+extern void foo65914();
+extern void foo65915();
+extern void foo65916();
+extern void foo65917();
+extern void foo65918();
+extern void foo65919();
+extern void foo65920();
+extern void foo65921();
+extern void foo65922();
+extern void foo65923();
+extern void foo65924();
+extern void foo65925();
+extern void foo65926();
+extern void foo65927();
+extern void foo65928();
+extern void foo65929();
+extern void foo65930();
+extern void foo65931();
+extern void foo65932();
+extern void foo65933();
+extern void foo65934();
+extern void foo65935();
+extern void foo65936();
+extern void foo65937();
+extern void foo65938();
+extern void foo65939();
+extern void foo65940();
+extern void foo65941();
+extern void foo65942();
+extern void foo65943();
+extern void foo65944();
+extern void foo65945();
+extern void foo65946();
+extern void foo65947();
+extern void foo65948();
+extern void foo65949();
+extern void foo65950();
+extern void foo65951();
+extern void foo65952();
+extern void foo65953();
+extern void foo65954();
+extern void foo65955();
+extern void foo65956();
+extern void foo65957();
+extern void foo65958();
+extern void foo65959();
+extern void foo65960();
+extern void foo65961();
+extern void foo65962();
+extern void foo65963();
+extern void foo65964();
+extern void foo65965();
+extern void foo65966();
+extern void foo65967();
+extern void foo65968();
+extern void foo65969();
+extern void foo65970();
+extern void foo65971();
+extern void foo65972();
+extern void foo65973();
+extern void foo65974();
+extern void foo65975();
+extern void foo65976();
+extern void foo65977();
+extern void foo65978();
+extern void foo65979();
+extern void foo65980();
+extern void foo65981();
+extern void foo65982();
+extern void foo65983();
+extern void foo65984();
+extern void foo65985();
+extern void foo65986();
+extern void foo65987();
+extern void foo65988();
+extern void foo65989();
+extern void foo65990();
+extern void foo65991();
+extern void foo65992();
+extern void foo65993();
+extern void foo65994();
+extern void foo65995();
+extern void foo65996();
+extern void foo65997();
+extern void foo65998();
+extern void foo65999();
+extern void foo66000();
+extern void foo66001();
+extern void foo66002();
+extern void foo66003();
+extern void foo66004();
+extern void foo66005();
+extern void foo66006();
+extern void foo66007();
+extern void foo66008();
+extern void foo66009();
+extern void foo66010();
+extern void foo66011();
+extern void foo66012();
+extern void foo66013();
+extern void foo66014();
+extern void foo66015();
+extern void foo66016();
+extern void foo66017();
+extern void foo66018();
+extern void foo66019();
+extern void foo66020();
+extern void foo66021();
+extern void foo66022();
+extern void foo66023();
+extern void foo66024();
+extern void foo66025();
+extern void foo66026();
+extern void foo66027();
+extern void foo66028();
+extern void foo66029();
+extern void foo66030();
+extern void foo66031();
+extern void foo66032();
+extern void foo66033();
+extern void foo66034();
+extern void foo66035();
+extern void foo66036();
+extern void foo66037();
+extern void foo66038();
+extern void foo66039();
+extern void foo66040();
+extern void foo66041();
+extern void foo66042();
+extern void foo66043();
+extern void foo66044();
+extern void foo66045();
+extern void foo66046();
+extern void foo66047();
+extern void foo66048();
+extern void foo66049();
+extern void foo66050();
+extern void foo66051();
+extern void foo66052();
+extern void foo66053();
+extern void foo66054();
+extern void foo66055();
+extern void foo66056();
+extern void foo66057();
+extern void foo66058();
+extern void foo66059();
+extern void foo66060();
+extern void foo66061();
+extern void foo66062();
+extern void foo66063();
+extern void foo66064();
+extern void foo66065();
+extern void foo66066();
+extern void foo66067();
+extern void foo66068();
+extern void foo66069();
+extern void foo66070();
+extern void foo66071();
+extern void foo66072();
+extern void foo66073();
+extern void foo66074();
+extern void foo66075();
+extern void foo66076();
+extern void foo66077();
+extern void foo66078();
+extern void foo66079();
+extern void foo66080();
+extern void foo66081();
+extern void foo66082();
+extern void foo66083();
+extern void foo66084();
+extern void foo66085();
+extern void foo66086();
+extern void foo66087();
+extern void foo66088();
+extern void foo66089();
+extern void foo66090();
+extern void foo66091();
+extern void foo66092();
+extern void foo66093();
+extern void foo66094();
+extern void foo66095();
+extern void foo66096();
+extern void foo66097();
+extern void foo66098();
+extern void foo66099();
+extern void foo66100();
+extern void foo66101();
+extern void foo66102();
+extern void foo66103();
+extern void foo66104();
+extern void foo66105();
+extern void foo66106();
+extern void foo66107();
+extern void foo66108();
+extern void foo66109();
+extern void foo66110();
+extern void foo66111();
+extern void foo66112();
+extern void foo66113();
+extern void foo66114();
+extern void foo66115();
+extern void foo66116();
+extern void foo66117();
+extern void foo66118();
+extern void foo66119();
+extern void foo66120();
+extern void foo66121();
+extern void foo66122();
+extern void foo66123();
+extern void foo66124();
+extern void foo66125();
+extern void foo66126();
+extern void foo66127();
+extern void foo66128();
+extern void foo66129();
+extern void foo66130();
+extern void foo66131();
+extern void foo66132();
+extern void foo66133();
+extern void foo66134();
+extern void foo66135();
+extern void foo66136();
+extern void foo66137();
+extern void foo66138();
+extern void foo66139();
+extern void foo66140();
+extern void foo66141();
+extern void foo66142();
+extern void foo66143();
+extern void foo66144();
+extern void foo66145();
+extern void foo66146();
+extern void foo66147();
+extern void foo66148();
+extern void foo66149();
+extern void foo66150();
+extern void foo66151();
+extern void foo66152();
+extern void foo66153();
+extern void foo66154();
+extern void foo66155();
+extern void foo66156();
+extern void foo66157();
+extern void foo66158();
+extern void foo66159();
+extern void foo66160();
+extern void foo66161();
+extern void foo66162();
+extern void foo66163();
+extern void foo66164();
+extern void foo66165();
+extern void foo66166();
+extern void foo66167();
+extern void foo66168();
+extern void foo66169();
+extern void foo66170();
+extern void foo66171();
+extern void foo66172();
+extern void foo66173();
+extern void foo66174();
+extern void foo66175();
+extern void foo66176();
+extern void foo66177();
+extern void foo66178();
+extern void foo66179();
+extern void foo66180();
+extern void foo66181();
+extern void foo66182();
+extern void foo66183();
+extern void foo66184();
+extern void foo66185();
+extern void foo66186();
+extern void foo66187();
+extern void foo66188();
+extern void foo66189();
+extern void foo66190();
+extern void foo66191();
+extern void foo66192();
+extern void foo66193();
+extern void foo66194();
+extern void foo66195();
+extern void foo66196();
+extern void foo66197();
+extern void foo66198();
+extern void foo66199();
+extern void foo66200();
+extern void foo66201();
+extern void foo66202();
+extern void foo66203();
+extern void foo66204();
+extern void foo66205();
+extern void foo66206();
+extern void foo66207();
+extern void foo66208();
+extern void foo66209();
+extern void foo66210();
+extern void foo66211();
+extern void foo66212();
+extern void foo66213();
+extern void foo66214();
+extern void foo66215();
+extern void foo66216();
+extern void foo66217();
+extern void foo66218();
+extern void foo66219();
+extern void foo66220();
+extern void foo66221();
+extern void foo66222();
+extern void foo66223();
+extern void foo66224();
+extern void foo66225();
+extern void foo66226();
+extern void foo66227();
+extern void foo66228();
+extern void foo66229();
+extern void foo66230();
+extern void foo66231();
+extern void foo66232();
+extern void foo66233();
+extern void foo66234();
+extern void foo66235();
+extern void foo66236();
+extern void foo66237();
+extern void foo66238();
+extern void foo66239();
+extern void foo66240();
+extern void foo66241();
+extern void foo66242();
+extern void foo66243();
+extern void foo66244();
+extern void foo66245();
+extern void foo66246();
+extern void foo66247();
+extern void foo66248();
+extern void foo66249();
+extern void foo66250();
+extern void foo66251();
+extern void foo66252();
+extern void foo66253();
+extern void foo66254();
+extern void foo66255();
+extern void foo66256();
+extern void foo66257();
+extern void foo66258();
+extern void foo66259();
+extern void foo66260();
+extern void foo66261();
+extern void foo66262();
+extern void foo66263();
+extern void foo66264();
+extern void foo66265();
+extern void foo66266();
+extern void foo66267();
+extern void foo66268();
+extern void foo66269();
+extern void foo66270();
+extern void foo66271();
+extern void foo66272();
+extern void foo66273();
+extern void foo66274();
+extern void foo66275();
+extern void foo66276();
+extern void foo66277();
+extern void foo66278();
+extern void foo66279();
+extern void foo66280();
+extern void foo66281();
+extern void foo66282();
+extern void foo66283();
+extern void foo66284();
+extern void foo66285();
+extern void foo66286();
+extern void foo66287();
+extern void foo66288();
+extern void foo66289();
+extern void foo66290();
+extern void foo66291();
+extern void foo66292();
+extern void foo66293();
+extern void foo66294();
+extern void foo66295();
+extern void foo66296();
+extern void foo66297();
+extern void foo66298();
+extern void foo66299();
+extern void foo66300();
+extern void foo66301();
+extern void foo66302();
+extern void foo66303();
+extern void foo66304();
+extern void foo66305();
+extern void foo66306();
+extern void foo66307();
+extern void foo66308();
+extern void foo66309();
+extern void foo66310();
+extern void foo66311();
+extern void foo66312();
+extern void foo66313();
+extern void foo66314();
+extern void foo66315();
+extern void foo66316();
+extern void foo66317();
+extern void foo66318();
+extern void foo66319();
+extern void foo66320();
+extern void foo66321();
+extern void foo66322();
+extern void foo66323();
+extern void foo66324();
+extern void foo66325();
+extern void foo66326();
+extern void foo66327();
+extern void foo66328();
+extern void foo66329();
+extern void foo66330();
+extern void foo66331();
+extern void foo66332();
+extern void foo66333();
+extern void foo66334();
+extern void foo66335();
+extern void foo66336();
+extern void foo66337();
+extern void foo66338();
+extern void foo66339();
+extern void foo66340();
+extern void foo66341();
+extern void foo66342();
+extern void foo66343();
+extern void foo66344();
+extern void foo66345();
+extern void foo66346();
+extern void foo66347();
+extern void foo66348();
+extern void foo66349();
+extern void foo66350();
+extern void foo66351();
+extern void foo66352();
+extern void foo66353();
+extern void foo66354();
+extern void foo66355();
+extern void foo66356();
+extern void foo66357();
+extern void foo66358();
+extern void foo66359();
+extern void foo66360();
+extern void foo66361();
+extern void foo66362();
+extern void foo66363();
+extern void foo66364();
+extern void foo66365();
+extern void foo66366();
+extern void foo66367();
+extern void foo66368();
+extern void foo66369();
+extern void foo66370();
+extern void foo66371();
+extern void foo66372();
+extern void foo66373();
+extern void foo66374();
+extern void foo66375();
+extern void foo66376();
+extern void foo66377();
+extern void foo66378();
+extern void foo66379();
+extern void foo66380();
+extern void foo66381();
+extern void foo66382();
+extern void foo66383();
+extern void foo66384();
+extern void foo66385();
+extern void foo66386();
+extern void foo66387();
+extern void foo66388();
+extern void foo66389();
+extern void foo66390();
+extern void foo66391();
+extern void foo66392();
+extern void foo66393();
+extern void foo66394();
+extern void foo66395();
+extern void foo66396();
+extern void foo66397();
+extern void foo66398();
+extern void foo66399();
+extern void foo66400();
+extern void foo66401();
+extern void foo66402();
+extern void foo66403();
+extern void foo66404();
+extern void foo66405();
+extern void foo66406();
+extern void foo66407();
+extern void foo66408();
+extern void foo66409();
+extern void foo66410();
+extern void foo66411();
+extern void foo66412();
+extern void foo66413();
+extern void foo66414();
+extern void foo66415();
+extern void foo66416();
+extern void foo66417();
+extern void foo66418();
+extern void foo66419();
+extern void foo66420();
+extern void foo66421();
+extern void foo66422();
+extern void foo66423();
+extern void foo66424();
+extern void foo66425();
+extern void foo66426();
+extern void foo66427();
+extern void foo66428();
+extern void foo66429();
+extern void foo66430();
+extern void foo66431();
+extern void foo66432();
+extern void foo66433();
+extern void foo66434();
+extern void foo66435();
+extern void foo66436();
+extern void foo66437();
+extern void foo66438();
+extern void foo66439();
+extern void foo66440();
+extern void foo66441();
+extern void foo66442();
+extern void foo66443();
+extern void foo66444();
+extern void foo66445();
+extern void foo66446();
+extern void foo66447();
+extern void foo66448();
+extern void foo66449();
+extern void foo66450();
+extern void foo66451();
+extern void foo66452();
+extern void foo66453();
+extern void foo66454();
+extern void foo66455();
+extern void foo66456();
+extern void foo66457();
+extern void foo66458();
+extern void foo66459();
+extern void foo66460();
+extern void foo66461();
+extern void foo66462();
+extern void foo66463();
+extern void foo66464();
+extern void foo66465();
+extern void foo66466();
+extern void foo66467();
+extern void foo66468();
+extern void foo66469();
+extern void foo66470();
+extern void foo66471();
+extern void foo66472();
+extern void foo66473();
+extern void foo66474();
+extern void foo66475();
+extern void foo66476();
+extern void foo66477();
+extern void foo66478();
+extern void foo66479();
+extern void foo66480();
+extern void foo66481();
+extern void foo66482();
+extern void foo66483();
+extern void foo66484();
+extern void foo66485();
+extern void foo66486();
+extern void foo66487();
+extern void foo66488();
+extern void foo66489();
+extern void foo66490();
+extern void foo66491();
+extern void foo66492();
+extern void foo66493();
+extern void foo66494();
+extern void foo66495();
+extern void foo66496();
+extern void foo66497();
+extern void foo66498();
+extern void foo66499();
+extern void foo66500();
+extern void foo66501();
+extern void foo66502();
+extern void foo66503();
+extern void foo66504();
+extern void foo66505();
+extern void foo66506();
+extern void foo66507();
+extern void foo66508();
+extern void foo66509();
+extern void foo66510();
+extern void foo66511();
+extern void foo66512();
+extern void foo66513();
+extern void foo66514();
+extern void foo66515();
+extern void foo66516();
+extern void foo66517();
+extern void foo66518();
+extern void foo66519();
+extern void foo66520();
+extern void foo66521();
+extern void foo66522();
+extern void foo66523();
+extern void foo66524();
+extern void foo66525();
+extern void foo66526();
+extern void foo66527();
+extern void foo66528();
+extern void foo66529();
+extern void foo66530();
+extern void foo66531();
+extern void foo66532();
+extern void foo66533();
+extern void foo66534();
+extern void foo66535();
+extern void foo66536();
+extern void foo66537();
+extern void foo66538();
+extern void foo66539();
+extern void foo66540();
+extern void foo66541();
+extern void foo66542();
+extern void foo66543();
+extern void foo66544();
+extern void foo66545();
+extern void foo66546();
+extern void foo66547();
+extern void foo66548();
+extern void foo66549();
+extern void foo66550();
+extern void foo66551();
+extern void foo66552();
+extern void foo66553();
+extern void foo66554();
+extern void foo66555();
+extern void foo66556();
+extern void foo66557();
+extern void foo66558();
+extern void foo66559();
+extern void foo66560();
+extern void foo66561();
+extern void foo66562();
+extern void foo66563();
+extern void foo66564();
+extern void foo66565();
+extern void foo66566();
+extern void foo66567();
+extern void foo66568();
+extern void foo66569();
+extern void foo66570();
+extern void foo66571();
+extern void foo66572();
+extern void foo66573();
+extern void foo66574();
+extern void foo66575();
+extern void foo66576();
+extern void foo66577();
+extern void foo66578();
+extern void foo66579();
+extern void foo66580();
+extern void foo66581();
+extern void foo66582();
+extern void foo66583();
+extern void foo66584();
+extern void foo66585();
+extern void foo66586();
+extern void foo66587();
+extern void foo66588();
+extern void foo66589();
+extern void foo66590();
+extern void foo66591();
+extern void foo66592();
+extern void foo66593();
+extern void foo66594();
+extern void foo66595();
+extern void foo66596();
+extern void foo66597();
+extern void foo66598();
+extern void foo66599();
+extern void foo66600();
+extern void foo66601();
+extern void foo66602();
+extern void foo66603();
+extern void foo66604();
+extern void foo66605();
+extern void foo66606();
+extern void foo66607();
+extern void foo66608();
+extern void foo66609();
+extern void foo66610();
+extern void foo66611();
+extern void foo66612();
+extern void foo66613();
+extern void foo66614();
+extern void foo66615();
+extern void foo66616();
+extern void foo66617();
+extern void foo66618();
+extern void foo66619();
+extern void foo66620();
+extern void foo66621();
+extern void foo66622();
+extern void foo66623();
+extern void foo66624();
+extern void foo66625();
+extern void foo66626();
+extern void foo66627();
+extern void foo66628();
+extern void foo66629();
+extern void foo66630();
+extern void foo66631();
+extern void foo66632();
+extern void foo66633();
+extern void foo66634();
+extern void foo66635();
+extern void foo66636();
+extern void foo66637();
+extern void foo66638();
+extern void foo66639();
+extern void foo66640();
+extern void foo66641();
+extern void foo66642();
+extern void foo66643();
+extern void foo66644();
+extern void foo66645();
+extern void foo66646();
+extern void foo66647();
+extern void foo66648();
+extern void foo66649();
+extern void foo66650();
+extern void foo66651();
+extern void foo66652();
+extern void foo66653();
+extern void foo66654();
+extern void foo66655();
+extern void foo66656();
+extern void foo66657();
+extern void foo66658();
+extern void foo66659();
+extern void foo66660();
+extern void foo66661();
+extern void foo66662();
+extern void foo66663();
+extern void foo66664();
+extern void foo66665();
+extern void foo66666();
+extern void foo66667();
+extern void foo66668();
+extern void foo66669();
+extern void foo66670();
+extern void foo66671();
+extern void foo66672();
+extern void foo66673();
+extern void foo66674();
+extern void foo66675();
+extern void foo66676();
+extern void foo66677();
+extern void foo66678();
+extern void foo66679();
+extern void foo66680();
+extern void foo66681();
+extern void foo66682();
+extern void foo66683();
+extern void foo66684();
+extern void foo66685();
+extern void foo66686();
+extern void foo66687();
+extern void foo66688();
+extern void foo66689();
+extern void foo66690();
+extern void foo66691();
+extern void foo66692();
+extern void foo66693();
+extern void foo66694();
+extern void foo66695();
+extern void foo66696();
+extern void foo66697();
+extern void foo66698();
+extern void foo66699();
+extern void foo66700();
+extern void foo66701();
+extern void foo66702();
+extern void foo66703();
+extern void foo66704();
+extern void foo66705();
+extern void foo66706();
+extern void foo66707();
+extern void foo66708();
+extern void foo66709();
+extern void foo66710();
+extern void foo66711();
+extern void foo66712();
+extern void foo66713();
+extern void foo66714();
+extern void foo66715();
+extern void foo66716();
+extern void foo66717();
+extern void foo66718();
+extern void foo66719();
+extern void foo66720();
+extern void foo66721();
+extern void foo66722();
+extern void foo66723();
+extern void foo66724();
+extern void foo66725();
+extern void foo66726();
+extern void foo66727();
+extern void foo66728();
+extern void foo66729();
+extern void foo66730();
+extern void foo66731();
+extern void foo66732();
+extern void foo66733();
+extern void foo66734();
+extern void foo66735();
+extern void foo66736();
+extern void foo66737();
+extern void foo66738();
+extern void foo66739();
+extern void foo66740();
+extern void foo66741();
+extern void foo66742();
+extern void foo66743();
+extern void foo66744();
+extern void foo66745();
+extern void foo66746();
+extern void foo66747();
+extern void foo66748();
+extern void foo66749();
+extern void foo66750();
+extern void foo66751();
+extern void foo66752();
+extern void foo66753();
+extern void foo66754();
+extern void foo66755();
+extern void foo66756();
+extern void foo66757();
+extern void foo66758();
+extern void foo66759();
+extern void foo66760();
+extern void foo66761();
+extern void foo66762();
+extern void foo66763();
+extern void foo66764();
+extern void foo66765();
+extern void foo66766();
+extern void foo66767();
+extern void foo66768();
+extern void foo66769();
+extern void foo66770();
+extern void foo66771();
+extern void foo66772();
+extern void foo66773();
+extern void foo66774();
+extern void foo66775();
+extern void foo66776();
+extern void foo66777();
+extern void foo66778();
+extern void foo66779();
+extern void foo66780();
+extern void foo66781();
+extern void foo66782();
+extern void foo66783();
+extern void foo66784();
+extern void foo66785();
+extern void foo66786();
+extern void foo66787();
+extern void foo66788();
+extern void foo66789();
+extern void foo66790();
+extern void foo66791();
+extern void foo66792();
+extern void foo66793();
+extern void foo66794();
+extern void foo66795();
+extern void foo66796();
+extern void foo66797();
+extern void foo66798();
+extern void foo66799();
+extern void foo66800();
+extern void foo66801();
+extern void foo66802();
+extern void foo66803();
+extern void foo66804();
+extern void foo66805();
+extern void foo66806();
+extern void foo66807();
+extern void foo66808();
+extern void foo66809();
+extern void foo66810();
+extern void foo66811();
+extern void foo66812();
+extern void foo66813();
+extern void foo66814();
+extern void foo66815();
+extern void foo66816();
+extern void foo66817();
+extern void foo66818();
+extern void foo66819();
+extern void foo66820();
+extern void foo66821();
+extern void foo66822();
+extern void foo66823();
+extern void foo66824();
+extern void foo66825();
+extern void foo66826();
+extern void foo66827();
+extern void foo66828();
+extern void foo66829();
+extern void foo66830();
+extern void foo66831();
+extern void foo66832();
+extern void foo66833();
+extern void foo66834();
+extern void foo66835();
+extern void foo66836();
+extern void foo66837();
+extern void foo66838();
+extern void foo66839();
+extern void foo66840();
+extern void foo66841();
+extern void foo66842();
+extern void foo66843();
+extern void foo66844();
+extern void foo66845();
+extern void foo66846();
+extern void foo66847();
+extern void foo66848();
+extern void foo66849();
+extern void foo66850();
+extern void foo66851();
+extern void foo66852();
+extern void foo66853();
+extern void foo66854();
+extern void foo66855();
+extern void foo66856();
+extern void foo66857();
+extern void foo66858();
+extern void foo66859();
+extern void foo66860();
+extern void foo66861();
+extern void foo66862();
+extern void foo66863();
+extern void foo66864();
+extern void foo66865();
+extern void foo66866();
+extern void foo66867();
+extern void foo66868();
+extern void foo66869();
+extern void foo66870();
+extern void foo66871();
+extern void foo66872();
+extern void foo66873();
+extern void foo66874();
+extern void foo66875();
+extern void foo66876();
+extern void foo66877();
+extern void foo66878();
+extern void foo66879();
+extern void foo66880();
+extern void foo66881();
+extern void foo66882();
+extern void foo66883();
+extern void foo66884();
+extern void foo66885();
+extern void foo66886();
+extern void foo66887();
+extern void foo66888();
+extern void foo66889();
+extern void foo66890();
+extern void foo66891();
+extern void foo66892();
+extern void foo66893();
+extern void foo66894();
+extern void foo66895();
+extern void foo66896();
+extern void foo66897();
+extern void foo66898();
+extern void foo66899();
+extern void foo66900();
+extern void foo66901();
+extern void foo66902();
+extern void foo66903();
+extern void foo66904();
+extern void foo66905();
+extern void foo66906();
+extern void foo66907();
+extern void foo66908();
+extern void foo66909();
+extern void foo66910();
+extern void foo66911();
+extern void foo66912();
+extern void foo66913();
+extern void foo66914();
+extern void foo66915();
+extern void foo66916();
+extern void foo66917();
+extern void foo66918();
+extern void foo66919();
+extern void foo66920();
+extern void foo66921();
+extern void foo66922();
+extern void foo66923();
+extern void foo66924();
+extern void foo66925();
+extern void foo66926();
+extern void foo66927();
+extern void foo66928();
+extern void foo66929();
+extern void foo66930();
+extern void foo66931();
+extern void foo66932();
+extern void foo66933();
+extern void foo66934();
+extern void foo66935();
+extern void foo66936();
+extern void foo66937();
+extern void foo66938();
+extern void foo66939();
+extern void foo66940();
+extern void foo66941();
+extern void foo66942();
+extern void foo66943();
+extern void foo66944();
+extern void foo66945();
+extern void foo66946();
+extern void foo66947();
+extern void foo66948();
+extern void foo66949();
+extern void foo66950();
+extern void foo66951();
+extern void foo66952();
+extern void foo66953();
+extern void foo66954();
+extern void foo66955();
+extern void foo66956();
+extern void foo66957();
+extern void foo66958();
+extern void foo66959();
+extern void foo66960();
+extern void foo66961();
+extern void foo66962();
+extern void foo66963();
+extern void foo66964();
+extern void foo66965();
+extern void foo66966();
+extern void foo66967();
+extern void foo66968();
+extern void foo66969();
+extern void foo66970();
+extern void foo66971();
+extern void foo66972();
+extern void foo66973();
+extern void foo66974();
+extern void foo66975();
+extern void foo66976();
+extern void foo66977();
+extern void foo66978();
+extern void foo66979();
+extern void foo66980();
+extern void foo66981();
+extern void foo66982();
+extern void foo66983();
+extern void foo66984();
+extern void foo66985();
+extern void foo66986();
+extern void foo66987();
+extern void foo66988();
+extern void foo66989();
+extern void foo66990();
+extern void foo66991();
+extern void foo66992();
+extern void foo66993();
+extern void foo66994();
+extern void foo66995();
+extern void foo66996();
+extern void foo66997();
+extern void foo66998();
+extern void foo66999();
+extern void foo67000();
+extern void foo67001();
+extern void foo67002();
+extern void foo67003();
+extern void foo67004();
+extern void foo67005();
+extern void foo67006();
+extern void foo67007();
+extern void foo67008();
+extern void foo67009();
+extern void foo67010();
+extern void foo67011();
+extern void foo67012();
+extern void foo67013();
+extern void foo67014();
+extern void foo67015();
+extern void foo67016();
+extern void foo67017();
+extern void foo67018();
+extern void foo67019();
+extern void foo67020();
+extern void foo67021();
+extern void foo67022();
+extern void foo67023();
+extern void foo67024();
+extern void foo67025();
+extern void foo67026();
+extern void foo67027();
+extern void foo67028();
+extern void foo67029();
+extern void foo67030();
+extern void foo67031();
+extern void foo67032();
+extern void foo67033();
+extern void foo67034();
+extern void foo67035();
+extern void foo67036();
+extern void foo67037();
+extern void foo67038();
+extern void foo67039();
+extern void foo67040();
+extern void foo67041();
+extern void foo67042();
+extern void foo67043();
+extern void foo67044();
+extern void foo67045();
+extern void foo67046();
+extern void foo67047();
+extern void foo67048();
+extern void foo67049();
+extern void foo67050();
+extern void foo67051();
+extern void foo67052();
+extern void foo67053();
+extern void foo67054();
+extern void foo67055();
+extern void foo67056();
+extern void foo67057();
+extern void foo67058();
+extern void foo67059();
+extern void foo67060();
+extern void foo67061();
+extern void foo67062();
+extern void foo67063();
+extern void foo67064();
+extern void foo67065();
+extern void foo67066();
+extern void foo67067();
+extern void foo67068();
+extern void foo67069();
+extern void foo67070();
+extern void foo67071();
+extern void foo67072();
+extern void foo67073();
+extern void foo67074();
+extern void foo67075();
+extern void foo67076();
+extern void foo67077();
+extern void foo67078();
+extern void foo67079();
+extern void foo67080();
+extern void foo67081();
+extern void foo67082();
+extern void foo67083();
+extern void foo67084();
+extern void foo67085();
+extern void foo67086();
+extern void foo67087();
+extern void foo67088();
+extern void foo67089();
+extern void foo67090();
+extern void foo67091();
+extern void foo67092();
+extern void foo67093();
+extern void foo67094();
+extern void foo67095();
+extern void foo67096();
+extern void foo67097();
+extern void foo67098();
+extern void foo67099();
+extern void foo67100();
+extern void foo67101();
+extern void foo67102();
+extern void foo67103();
+extern void foo67104();
+extern void foo67105();
+extern void foo67106();
+extern void foo67107();
+extern void foo67108();
+extern void foo67109();
+extern void foo67110();
+extern void foo67111();
+extern void foo67112();
+extern void foo67113();
+extern void foo67114();
+extern void foo67115();
+extern void foo67116();
+extern void foo67117();
+extern void foo67118();
+extern void foo67119();
+extern void foo67120();
+extern void foo67121();
+extern void foo67122();
+extern void foo67123();
+extern void foo67124();
+extern void foo67125();
+extern void foo67126();
+extern void foo67127();
+extern void foo67128();
+extern void foo67129();
+extern void foo67130();
+extern void foo67131();
+extern void foo67132();
+extern void foo67133();
+extern void foo67134();
+extern void foo67135();
+extern void foo67136();
+extern void foo67137();
+extern void foo67138();
+extern void foo67139();
+extern void foo67140();
+extern void foo67141();
+extern void foo67142();
+extern void foo67143();
+extern void foo67144();
+extern void foo67145();
+extern void foo67146();
+extern void foo67147();
+extern void foo67148();
+extern void foo67149();
+extern void foo67150();
+extern void foo67151();
+extern void foo67152();
+extern void foo67153();
+extern void foo67154();
+extern void foo67155();
+extern void foo67156();
+extern void foo67157();
+extern void foo67158();
+extern void foo67159();
+extern void foo67160();
+extern void foo67161();
+extern void foo67162();
+extern void foo67163();
+extern void foo67164();
+extern void foo67165();
+extern void foo67166();
+extern void foo67167();
+extern void foo67168();
+extern void foo67169();
+extern void foo67170();
+extern void foo67171();
+extern void foo67172();
+extern void foo67173();
+extern void foo67174();
+extern void foo67175();
+extern void foo67176();
+extern void foo67177();
+extern void foo67178();
+extern void foo67179();
+extern void foo67180();
+extern void foo67181();
+extern void foo67182();
+extern void foo67183();
+extern void foo67184();
+extern void foo67185();
+extern void foo67186();
+extern void foo67187();
+extern void foo67188();
+extern void foo67189();
+extern void foo67190();
+extern void foo67191();
+extern void foo67192();
+extern void foo67193();
+extern void foo67194();
+extern void foo67195();
+extern void foo67196();
+extern void foo67197();
+extern void foo67198();
+extern void foo67199();
+extern void foo67200();
+extern void foo67201();
+extern void foo67202();
+extern void foo67203();
+extern void foo67204();
+extern void foo67205();
+extern void foo67206();
+extern void foo67207();
+extern void foo67208();
+extern void foo67209();
+extern void foo67210();
+extern void foo67211();
+extern void foo67212();
+extern void foo67213();
+extern void foo67214();
+extern void foo67215();
+extern void foo67216();
+extern void foo67217();
+extern void foo67218();
+extern void foo67219();
+extern void foo67220();
+extern void foo67221();
+extern void foo67222();
+extern void foo67223();
+extern void foo67224();
+extern void foo67225();
+extern void foo67226();
+extern void foo67227();
+extern void foo67228();
+extern void foo67229();
+extern void foo67230();
+extern void foo67231();
+extern void foo67232();
+extern void foo67233();
+extern void foo67234();
+extern void foo67235();
+extern void foo67236();
+extern void foo67237();
+extern void foo67238();
+extern void foo67239();
+extern void foo67240();
+extern void foo67241();
+extern void foo67242();
+extern void foo67243();
+extern void foo67244();
+extern void foo67245();
+extern void foo67246();
+extern void foo67247();
+extern void foo67248();
+extern void foo67249();
+extern void foo67250();
+extern void foo67251();
+extern void foo67252();
+extern void foo67253();
+extern void foo67254();
+extern void foo67255();
+extern void foo67256();
+extern void foo67257();
+extern void foo67258();
+extern void foo67259();
+extern void foo67260();
+extern void foo67261();
+extern void foo67262();
+extern void foo67263();
+extern void foo67264();
+extern void foo67265();
+extern void foo67266();
+extern void foo67267();
+extern void foo67268();
+extern void foo67269();
+extern void foo67270();
+extern void foo67271();
+extern void foo67272();
+extern void foo67273();
+extern void foo67274();
+extern void foo67275();
+extern void foo67276();
+extern void foo67277();
+extern void foo67278();
+extern void foo67279();
+extern void foo67280();
+extern void foo67281();
+extern void foo67282();
+extern void foo67283();
+extern void foo67284();
+extern void foo67285();
+extern void foo67286();
+extern void foo67287();
+extern void foo67288();
+extern void foo67289();
+extern void foo67290();
+extern void foo67291();
+extern void foo67292();
+extern void foo67293();
+extern void foo67294();
+extern void foo67295();
+extern void foo67296();
+extern void foo67297();
+extern void foo67298();
+extern void foo67299();
+extern void foo67300();
+extern void foo67301();
+extern void foo67302();
+extern void foo67303();
+extern void foo67304();
+extern void foo67305();
+extern void foo67306();
+extern void foo67307();
+extern void foo67308();
+extern void foo67309();
+extern void foo67310();
+extern void foo67311();
+extern void foo67312();
+extern void foo67313();
+extern void foo67314();
+extern void foo67315();
+extern void foo67316();
+extern void foo67317();
+extern void foo67318();
+extern void foo67319();
+extern void foo67320();
+extern void foo67321();
+extern void foo67322();
+extern void foo67323();
+extern void foo67324();
+extern void foo67325();
+extern void foo67326();
+extern void foo67327();
+extern void foo67328();
+extern void foo67329();
+extern void foo67330();
+extern void foo67331();
+extern void foo67332();
+extern void foo67333();
+extern void foo67334();
+extern void foo67335();
+extern void foo67336();
+extern void foo67337();
+extern void foo67338();
+extern void foo67339();
+extern void foo67340();
+extern void foo67341();
+extern void foo67342();
+extern void foo67343();
+extern void foo67344();
+extern void foo67345();
+extern void foo67346();
+extern void foo67347();
+extern void foo67348();
+extern void foo67349();
+extern void foo67350();
+extern void foo67351();
+extern void foo67352();
+extern void foo67353();
+extern void foo67354();
+extern void foo67355();
+extern void foo67356();
+extern void foo67357();
+extern void foo67358();
+extern void foo67359();
+extern void foo67360();
+extern void foo67361();
+extern void foo67362();
+extern void foo67363();
+extern void foo67364();
+extern void foo67365();
+extern void foo67366();
+extern void foo67367();
+extern void foo67368();
+extern void foo67369();
+extern void foo67370();
+extern void foo67371();
+extern void foo67372();
+extern void foo67373();
+extern void foo67374();
+extern void foo67375();
+extern void foo67376();
+extern void foo67377();
+extern void foo67378();
+extern void foo67379();
+extern void foo67380();
+extern void foo67381();
+extern void foo67382();
+extern void foo67383();
+extern void foo67384();
+extern void foo67385();
+extern void foo67386();
+extern void foo67387();
+extern void foo67388();
+extern void foo67389();
+extern void foo67390();
+extern void foo67391();
+extern void foo67392();
+extern void foo67393();
+extern void foo67394();
+extern void foo67395();
+extern void foo67396();
+extern void foo67397();
+extern void foo67398();
+extern void foo67399();
+extern void foo67400();
+extern void foo67401();
+extern void foo67402();
+extern void foo67403();
+extern void foo67404();
+extern void foo67405();
+extern void foo67406();
+extern void foo67407();
+extern void foo67408();
+extern void foo67409();
+extern void foo67410();
+extern void foo67411();
+extern void foo67412();
+extern void foo67413();
+extern void foo67414();
+extern void foo67415();
+extern void foo67416();
+extern void foo67417();
+extern void foo67418();
+extern void foo67419();
+extern void foo67420();
+extern void foo67421();
+extern void foo67422();
+extern void foo67423();
+extern void foo67424();
+extern void foo67425();
+extern void foo67426();
+extern void foo67427();
+extern void foo67428();
+extern void foo67429();
+extern void foo67430();
+extern void foo67431();
+extern void foo67432();
+extern void foo67433();
+extern void foo67434();
+extern void foo67435();
+extern void foo67436();
+extern void foo67437();
+extern void foo67438();
+extern void foo67439();
+extern void foo67440();
+extern void foo67441();
+extern void foo67442();
+extern void foo67443();
+extern void foo67444();
+extern void foo67445();
+extern void foo67446();
+extern void foo67447();
+extern void foo67448();
+extern void foo67449();
+extern void foo67450();
+extern void foo67451();
+extern void foo67452();
+extern void foo67453();
+extern void foo67454();
+extern void foo67455();
+extern void foo67456();
+extern void foo67457();
+extern void foo67458();
+extern void foo67459();
+extern void foo67460();
+extern void foo67461();
+extern void foo67462();
+extern void foo67463();
+extern void foo67464();
+extern void foo67465();
+extern void foo67466();
+extern void foo67467();
+extern void foo67468();
+extern void foo67469();
+extern void foo67470();
+extern void foo67471();
+extern void foo67472();
+extern void foo67473();
+extern void foo67474();
+extern void foo67475();
+extern void foo67476();
+extern void foo67477();
+extern void foo67478();
+extern void foo67479();
+extern void foo67480();
+extern void foo67481();
+extern void foo67482();
+extern void foo67483();
+extern void foo67484();
+extern void foo67485();
+extern void foo67486();
+extern void foo67487();
+extern void foo67488();
+extern void foo67489();
+extern void foo67490();
+extern void foo67491();
+extern void foo67492();
+extern void foo67493();
+extern void foo67494();
+extern void foo67495();
+extern void foo67496();
+extern void foo67497();
+extern void foo67498();
+extern void foo67499();
+extern void foo67500();
+extern void foo67501();
+extern void foo67502();
+extern void foo67503();
+extern void foo67504();
+extern void foo67505();
+extern void foo67506();
+extern void foo67507();
+extern void foo67508();
+extern void foo67509();
+extern void foo67510();
+extern void foo67511();
+extern void foo67512();
+extern void foo67513();
+extern void foo67514();
+extern void foo67515();
+extern void foo67516();
+extern void foo67517();
+extern void foo67518();
+extern void foo67519();
+extern void foo67520();
+extern void foo67521();
+extern void foo67522();
+extern void foo67523();
+extern void foo67524();
+extern void foo67525();
+extern void foo67526();
+extern void foo67527();
+extern void foo67528();
+extern void foo67529();
+extern void foo67530();
+extern void foo67531();
+extern void foo67532();
+extern void foo67533();
+extern void foo67534();
+extern void foo67535();
+extern void foo67536();
+extern void foo67537();
+extern void foo67538();
+extern void foo67539();
+extern void foo67540();
+extern void foo67541();
+extern void foo67542();
+extern void foo67543();
+extern void foo67544();
+extern void foo67545();
+extern void foo67546();
+extern void foo67547();
+extern void foo67548();
+extern void foo67549();
+extern void foo67550();
+extern void foo67551();
+extern void foo67552();
+extern void foo67553();
+extern void foo67554();
+extern void foo67555();
+extern void foo67556();
+extern void foo67557();
+extern void foo67558();
+extern void foo67559();
+extern void foo67560();
+extern void foo67561();
+extern void foo67562();
+extern void foo67563();
+extern void foo67564();
+extern void foo67565();
+extern void foo67566();
+extern void foo67567();
+extern void foo67568();
+extern void foo67569();
+extern void foo67570();
+extern void foo67571();
+extern void foo67572();
+extern void foo67573();
+extern void foo67574();
+extern void foo67575();
+extern void foo67576();
+extern void foo67577();
+extern void foo67578();
+extern void foo67579();
+extern void foo67580();
+extern void foo67581();
+extern void foo67582();
+extern void foo67583();
+extern void foo67584();
+extern void foo67585();
+extern void foo67586();
+extern void foo67587();
+extern void foo67588();
+extern void foo67589();
+extern void foo67590();
+extern void foo67591();
+extern void foo67592();
+extern void foo67593();
+extern void foo67594();
+extern void foo67595();
+extern void foo67596();
+extern void foo67597();
+extern void foo67598();
+extern void foo67599();
+extern void foo67600();
+extern void foo67601();
+extern void foo67602();
+extern void foo67603();
+extern void foo67604();
+extern void foo67605();
+extern void foo67606();
+extern void foo67607();
+extern void foo67608();
+extern void foo67609();
+extern void foo67610();
+extern void foo67611();
+extern void foo67612();
+extern void foo67613();
+extern void foo67614();
+extern void foo67615();
+extern void foo67616();
+extern void foo67617();
+extern void foo67618();
+extern void foo67619();
+extern void foo67620();
+extern void foo67621();
+extern void foo67622();
+extern void foo67623();
+extern void foo67624();
+extern void foo67625();
+extern void foo67626();
+extern void foo67627();
+extern void foo67628();
+extern void foo67629();
+extern void foo67630();
+extern void foo67631();
+extern void foo67632();
+extern void foo67633();
+extern void foo67634();
+extern void foo67635();
+extern void foo67636();
+extern void foo67637();
+extern void foo67638();
+extern void foo67639();
+extern void foo67640();
+extern void foo67641();
+extern void foo67642();
+extern void foo67643();
+extern void foo67644();
+extern void foo67645();
+extern void foo67646();
+extern void foo67647();
+extern void foo67648();
+extern void foo67649();
+extern void foo67650();
+extern void foo67651();
+extern void foo67652();
+extern void foo67653();
+extern void foo67654();
+extern void foo67655();
+extern void foo67656();
+extern void foo67657();
+extern void foo67658();
+extern void foo67659();
+extern void foo67660();
+extern void foo67661();
+extern void foo67662();
+extern void foo67663();
+extern void foo67664();
+extern void foo67665();
+extern void foo67666();
+extern void foo67667();
+extern void foo67668();
+extern void foo67669();
+extern void foo67670();
+extern void foo67671();
+extern void foo67672();
+extern void foo67673();
+extern void foo67674();
+extern void foo67675();
+extern void foo67676();
+extern void foo67677();
+extern void foo67678();
+extern void foo67679();
+extern void foo67680();
+extern void foo67681();
+extern void foo67682();
+extern void foo67683();
+extern void foo67684();
+extern void foo67685();
+extern void foo67686();
+extern void foo67687();
+extern void foo67688();
+extern void foo67689();
+extern void foo67690();
+extern void foo67691();
+extern void foo67692();
+extern void foo67693();
+extern void foo67694();
+extern void foo67695();
+extern void foo67696();
+extern void foo67697();
+extern void foo67698();
+extern void foo67699();
+extern void foo67700();
+extern void foo67701();
+extern void foo67702();
+extern void foo67703();
+extern void foo67704();
+extern void foo67705();
+extern void foo67706();
+extern void foo67707();
+extern void foo67708();
+extern void foo67709();
+extern void foo67710();
+extern void foo67711();
+extern void foo67712();
+extern void foo67713();
+extern void foo67714();
+extern void foo67715();
+extern void foo67716();
+extern void foo67717();
+extern void foo67718();
+extern void foo67719();
+extern void foo67720();
+extern void foo67721();
+extern void foo67722();
+extern void foo67723();
+extern void foo67724();
+extern void foo67725();
+extern void foo67726();
+extern void foo67727();
+extern void foo67728();
+extern void foo67729();
+extern void foo67730();
+extern void foo67731();
+extern void foo67732();
+extern void foo67733();
+extern void foo67734();
+extern void foo67735();
+extern void foo67736();
+extern void foo67737();
+extern void foo67738();
+extern void foo67739();
+extern void foo67740();
+extern void foo67741();
+extern void foo67742();
+extern void foo67743();
+extern void foo67744();
+extern void foo67745();
+extern void foo67746();
+extern void foo67747();
+extern void foo67748();
+extern void foo67749();
+extern void foo67750();
+extern void foo67751();
+extern void foo67752();
+extern void foo67753();
+extern void foo67754();
+extern void foo67755();
+extern void foo67756();
+extern void foo67757();
+extern void foo67758();
+extern void foo67759();
+extern void foo67760();
+extern void foo67761();
+extern void foo67762();
+extern void foo67763();
+extern void foo67764();
+extern void foo67765();
+extern void foo67766();
+extern void foo67767();
+extern void foo67768();
+extern void foo67769();
+extern void foo67770();
+extern void foo67771();
+extern void foo67772();
+extern void foo67773();
+extern void foo67774();
+extern void foo67775();
+extern void foo67776();
+extern void foo67777();
+extern void foo67778();
+extern void foo67779();
+extern void foo67780();
+extern void foo67781();
+extern void foo67782();
+extern void foo67783();
+extern void foo67784();
+extern void foo67785();
+extern void foo67786();
+extern void foo67787();
+extern void foo67788();
+extern void foo67789();
+extern void foo67790();
+extern void foo67791();
+extern void foo67792();
+extern void foo67793();
+extern void foo67794();
+extern void foo67795();
+extern void foo67796();
+extern void foo67797();
+extern void foo67798();
+extern void foo67799();
+extern void foo67800();
+extern void foo67801();
+extern void foo67802();
+extern void foo67803();
+extern void foo67804();
+extern void foo67805();
+extern void foo67806();
+extern void foo67807();
+extern void foo67808();
+extern void foo67809();
+extern void foo67810();
+extern void foo67811();
+extern void foo67812();
+extern void foo67813();
+extern void foo67814();
+extern void foo67815();
+extern void foo67816();
+extern void foo67817();
+extern void foo67818();
+extern void foo67819();
+extern void foo67820();
+extern void foo67821();
+extern void foo67822();
+extern void foo67823();
+extern void foo67824();
+extern void foo67825();
+extern void foo67826();
+extern void foo67827();
+extern void foo67828();
+extern void foo67829();
+extern void foo67830();
+extern void foo67831();
+extern void foo67832();
+extern void foo67833();
+extern void foo67834();
+extern void foo67835();
+extern void foo67836();
+extern void foo67837();
+extern void foo67838();
+extern void foo67839();
+extern void foo67840();
+extern void foo67841();
+extern void foo67842();
+extern void foo67843();
+extern void foo67844();
+extern void foo67845();
+extern void foo67846();
+extern void foo67847();
+extern void foo67848();
+extern void foo67849();
+extern void foo67850();
+extern void foo67851();
+extern void foo67852();
+extern void foo67853();
+extern void foo67854();
+extern void foo67855();
+extern void foo67856();
+extern void foo67857();
+extern void foo67858();
+extern void foo67859();
+extern void foo67860();
+extern void foo67861();
+extern void foo67862();
+extern void foo67863();
+extern void foo67864();
+extern void foo67865();
+extern void foo67866();
+extern void foo67867();
+extern void foo67868();
+extern void foo67869();
+extern void foo67870();
+extern void foo67871();
+extern void foo67872();
+extern void foo67873();
+extern void foo67874();
+extern void foo67875();
+extern void foo67876();
+extern void foo67877();
+extern void foo67878();
+extern void foo67879();
+extern void foo67880();
+extern void foo67881();
+extern void foo67882();
+extern void foo67883();
+extern void foo67884();
+extern void foo67885();
+extern void foo67886();
+extern void foo67887();
+extern void foo67888();
+extern void foo67889();
+extern void foo67890();
+extern void foo67891();
+extern void foo67892();
+extern void foo67893();
+extern void foo67894();
+extern void foo67895();
+extern void foo67896();
+extern void foo67897();
+extern void foo67898();
+extern void foo67899();
+extern void foo67900();
+extern void foo67901();
+extern void foo67902();
+extern void foo67903();
+extern void foo67904();
+extern void foo67905();
+extern void foo67906();
+extern void foo67907();
+extern void foo67908();
+extern void foo67909();
+extern void foo67910();
+extern void foo67911();
+extern void foo67912();
+extern void foo67913();
+extern void foo67914();
+extern void foo67915();
+extern void foo67916();
+extern void foo67917();
+extern void foo67918();
+extern void foo67919();
+extern void foo67920();
+extern void foo67921();
+extern void foo67922();
+extern void foo67923();
+extern void foo67924();
+extern void foo67925();
+extern void foo67926();
+extern void foo67927();
+extern void foo67928();
+extern void foo67929();
+extern void foo67930();
+extern void foo67931();
+extern void foo67932();
+extern void foo67933();
+extern void foo67934();
+extern void foo67935();
+extern void foo67936();
+extern void foo67937();
+extern void foo67938();
+extern void foo67939();
+extern void foo67940();
+extern void foo67941();
+extern void foo67942();
+extern void foo67943();
+extern void foo67944();
+extern void foo67945();
+extern void foo67946();
+extern void foo67947();
+extern void foo67948();
+extern void foo67949();
+extern void foo67950();
+extern void foo67951();
+extern void foo67952();
+extern void foo67953();
+extern void foo67954();
+extern void foo67955();
+extern void foo67956();
+extern void foo67957();
+extern void foo67958();
+extern void foo67959();
+extern void foo67960();
+extern void foo67961();
+extern void foo67962();
+extern void foo67963();
+extern void foo67964();
+extern void foo67965();
+extern void foo67966();
+extern void foo67967();
+extern void foo67968();
+extern void foo67969();
+extern void foo67970();
+extern void foo67971();
+extern void foo67972();
+extern void foo67973();
+extern void foo67974();
+extern void foo67975();
+extern void foo67976();
+extern void foo67977();
+extern void foo67978();
+extern void foo67979();
+extern void foo67980();
+extern void foo67981();
+extern void foo67982();
+extern void foo67983();
+extern void foo67984();
+extern void foo67985();
+extern void foo67986();
+extern void foo67987();
+extern void foo67988();
+extern void foo67989();
+extern void foo67990();
+extern void foo67991();
+extern void foo67992();
+extern void foo67993();
+extern void foo67994();
+extern void foo67995();
+extern void foo67996();
+extern void foo67997();
+extern void foo67998();
+extern void foo67999();
+extern void foo68000();
+extern void foo68001();
+extern void foo68002();
+extern void foo68003();
+extern void foo68004();
+extern void foo68005();
+extern void foo68006();
+extern void foo68007();
+extern void foo68008();
+extern void foo68009();
+extern void foo68010();
+extern void foo68011();
+extern void foo68012();
+extern void foo68013();
+extern void foo68014();
+extern void foo68015();
+extern void foo68016();
+extern void foo68017();
+extern void foo68018();
+extern void foo68019();
+extern void foo68020();
+extern void foo68021();
+extern void foo68022();
+extern void foo68023();
+extern void foo68024();
+extern void foo68025();
+extern void foo68026();
+extern void foo68027();
+extern void foo68028();
+extern void foo68029();
+extern void foo68030();
+extern void foo68031();
+extern void foo68032();
+extern void foo68033();
+extern void foo68034();
+extern void foo68035();
+extern void foo68036();
+extern void foo68037();
+extern void foo68038();
+extern void foo68039();
+extern void foo68040();
+extern void foo68041();
+extern void foo68042();
+extern void foo68043();
+extern void foo68044();
+extern void foo68045();
+extern void foo68046();
+extern void foo68047();
+extern void foo68048();
+extern void foo68049();
+extern void foo68050();
+extern void foo68051();
+extern void foo68052();
+extern void foo68053();
+extern void foo68054();
+extern void foo68055();
+extern void foo68056();
+extern void foo68057();
+extern void foo68058();
+extern void foo68059();
+extern void foo68060();
+extern void foo68061();
+extern void foo68062();
+extern void foo68063();
+extern void foo68064();
+extern void foo68065();
+extern void foo68066();
+extern void foo68067();
+extern void foo68068();
+extern void foo68069();
+extern void foo68070();
+extern void foo68071();
+extern void foo68072();
+extern void foo68073();
+extern void foo68074();
+extern void foo68075();
+extern void foo68076();
+extern void foo68077();
+extern void foo68078();
+extern void foo68079();
+extern void foo68080();
+extern void foo68081();
+extern void foo68082();
+extern void foo68083();
+extern void foo68084();
+extern void foo68085();
+extern void foo68086();
+extern void foo68087();
+extern void foo68088();
+extern void foo68089();
+extern void foo68090();
+extern void foo68091();
+extern void foo68092();
+extern void foo68093();
+extern void foo68094();
+extern void foo68095();
+extern void foo68096();
+extern void foo68097();
+extern void foo68098();
+extern void foo68099();
+extern void foo68100();
+extern void foo68101();
+extern void foo68102();
+extern void foo68103();
+extern void foo68104();
+extern void foo68105();
+extern void foo68106();
+extern void foo68107();
+extern void foo68108();
+extern void foo68109();
+extern void foo68110();
+extern void foo68111();
+extern void foo68112();
+extern void foo68113();
+extern void foo68114();
+extern void foo68115();
+extern void foo68116();
+extern void foo68117();
+extern void foo68118();
+extern void foo68119();
+extern void foo68120();
+extern void foo68121();
+extern void foo68122();
+extern void foo68123();
+extern void foo68124();
+extern void foo68125();
+extern void foo68126();
+extern void foo68127();
+extern void foo68128();
+extern void foo68129();
+extern void foo68130();
+extern void foo68131();
+extern void foo68132();
+extern void foo68133();
+extern void foo68134();
+extern void foo68135();
+extern void foo68136();
+extern void foo68137();
+extern void foo68138();
+extern void foo68139();
+extern void foo68140();
+extern void foo68141();
+extern void foo68142();
+extern void foo68143();
+extern void foo68144();
+extern void foo68145();
+extern void foo68146();
+extern void foo68147();
+extern void foo68148();
+extern void foo68149();
+extern void foo68150();
+extern void foo68151();
+extern void foo68152();
+extern void foo68153();
+extern void foo68154();
+extern void foo68155();
+extern void foo68156();
+extern void foo68157();
+extern void foo68158();
+extern void foo68159();
+extern void foo68160();
+extern void foo68161();
+extern void foo68162();
+extern void foo68163();
+extern void foo68164();
+extern void foo68165();
+extern void foo68166();
+extern void foo68167();
+extern void foo68168();
+extern void foo68169();
+extern void foo68170();
+extern void foo68171();
+extern void foo68172();
+extern void foo68173();
+extern void foo68174();
+extern void foo68175();
+extern void foo68176();
+extern void foo68177();
+extern void foo68178();
+extern void foo68179();
+extern void foo68180();
+extern void foo68181();
+extern void foo68182();
+extern void foo68183();
+extern void foo68184();
+extern void foo68185();
+extern void foo68186();
+extern void foo68187();
+extern void foo68188();
+extern void foo68189();
+extern void foo68190();
+extern void foo68191();
+extern void foo68192();
+extern void foo68193();
+extern void foo68194();
+extern void foo68195();
+extern void foo68196();
+extern void foo68197();
+extern void foo68198();
+extern void foo68199();
+extern void foo68200();
+extern void foo68201();
+extern void foo68202();
+extern void foo68203();
+extern void foo68204();
+extern void foo68205();
+extern void foo68206();
+extern void foo68207();
+extern void foo68208();
+extern void foo68209();
+extern void foo68210();
+extern void foo68211();
+extern void foo68212();
+extern void foo68213();
+extern void foo68214();
+extern void foo68215();
+extern void foo68216();
+extern void foo68217();
+extern void foo68218();
+extern void foo68219();
+extern void foo68220();
+extern void foo68221();
+extern void foo68222();
+extern void foo68223();
+extern void foo68224();
+extern void foo68225();
+extern void foo68226();
+extern void foo68227();
+extern void foo68228();
+extern void foo68229();
+extern void foo68230();
+extern void foo68231();
+extern void foo68232();
+extern void foo68233();
+extern void foo68234();
+extern void foo68235();
+extern void foo68236();
+extern void foo68237();
+extern void foo68238();
+extern void foo68239();
+extern void foo68240();
+extern void foo68241();
+extern void foo68242();
+extern void foo68243();
+extern void foo68244();
+extern void foo68245();
+extern void foo68246();
+extern void foo68247();
+extern void foo68248();
+extern void foo68249();
+extern void foo68250();
+extern void foo68251();
+extern void foo68252();
+extern void foo68253();
+extern void foo68254();
+extern void foo68255();
+extern void foo68256();
+extern void foo68257();
+extern void foo68258();
+extern void foo68259();
+extern void foo68260();
+extern void foo68261();
+extern void foo68262();
+extern void foo68263();
+extern void foo68264();
+extern void foo68265();
+extern void foo68266();
+extern void foo68267();
+extern void foo68268();
+extern void foo68269();
+extern void foo68270();
+extern void foo68271();
+extern void foo68272();
+extern void foo68273();
+extern void foo68274();
+extern void foo68275();
+extern void foo68276();
+extern void foo68277();
+extern void foo68278();
+extern void foo68279();
+extern void foo68280();
+extern void foo68281();
+extern void foo68282();
+extern void foo68283();
+extern void foo68284();
+extern void foo68285();
+extern void foo68286();
+extern void foo68287();
+extern void foo68288();
+extern void foo68289();
+extern void foo68290();
+extern void foo68291();
+extern void foo68292();
+extern void foo68293();
+extern void foo68294();
+extern void foo68295();
+extern void foo68296();
+extern void foo68297();
+extern void foo68298();
+extern void foo68299();
+extern void foo68300();
+extern void foo68301();
+extern void foo68302();
+extern void foo68303();
+extern void foo68304();
+extern void foo68305();
+extern void foo68306();
+extern void foo68307();
+extern void foo68308();
+extern void foo68309();
+extern void foo68310();
+extern void foo68311();
+extern void foo68312();
+extern void foo68313();
+extern void foo68314();
+extern void foo68315();
+extern void foo68316();
+extern void foo68317();
+extern void foo68318();
+extern void foo68319();
+extern void foo68320();
+extern void foo68321();
+extern void foo68322();
+extern void foo68323();
+extern void foo68324();
+extern void foo68325();
+extern void foo68326();
+extern void foo68327();
+extern void foo68328();
+extern void foo68329();
+extern void foo68330();
+extern void foo68331();
+extern void foo68332();
+extern void foo68333();
+extern void foo68334();
+extern void foo68335();
+extern void foo68336();
+extern void foo68337();
+extern void foo68338();
+extern void foo68339();
+extern void foo68340();
+extern void foo68341();
+extern void foo68342();
+extern void foo68343();
+extern void foo68344();
+extern void foo68345();
+extern void foo68346();
+extern void foo68347();
+extern void foo68348();
+extern void foo68349();
+extern void foo68350();
+extern void foo68351();
+extern void foo68352();
+extern void foo68353();
+extern void foo68354();
+extern void foo68355();
+extern void foo68356();
+extern void foo68357();
+extern void foo68358();
+extern void foo68359();
+extern void foo68360();
+extern void foo68361();
+extern void foo68362();
+extern void foo68363();
+extern void foo68364();
+extern void foo68365();
+extern void foo68366();
+extern void foo68367();
+extern void foo68368();
+extern void foo68369();
+extern void foo68370();
+extern void foo68371();
+extern void foo68372();
+extern void foo68373();
+extern void foo68374();
+extern void foo68375();
+extern void foo68376();
+extern void foo68377();
+extern void foo68378();
+extern void foo68379();
+extern void foo68380();
+extern void foo68381();
+extern void foo68382();
+extern void foo68383();
+extern void foo68384();
+extern void foo68385();
+extern void foo68386();
+extern void foo68387();
+extern void foo68388();
+extern void foo68389();
+extern void foo68390();
+extern void foo68391();
+extern void foo68392();
+extern void foo68393();
+extern void foo68394();
+extern void foo68395();
+extern void foo68396();
+extern void foo68397();
+extern void foo68398();
+extern void foo68399();
+extern void foo68400();
+extern void foo68401();
+extern void foo68402();
+extern void foo68403();
+extern void foo68404();
+extern void foo68405();
+extern void foo68406();
+extern void foo68407();
+extern void foo68408();
+extern void foo68409();
+extern void foo68410();
+extern void foo68411();
+extern void foo68412();
+extern void foo68413();
+extern void foo68414();
+extern void foo68415();
+extern void foo68416();
+extern void foo68417();
+extern void foo68418();
+extern void foo68419();
+extern void foo68420();
+extern void foo68421();
+extern void foo68422();
+extern void foo68423();
+extern void foo68424();
+extern void foo68425();
+extern void foo68426();
+extern void foo68427();
+extern void foo68428();
+extern void foo68429();
+extern void foo68430();
+extern void foo68431();
+extern void foo68432();
+extern void foo68433();
+extern void foo68434();
+extern void foo68435();
+extern void foo68436();
+extern void foo68437();
+extern void foo68438();
+extern void foo68439();
+extern void foo68440();
+extern void foo68441();
+extern void foo68442();
+extern void foo68443();
+extern void foo68444();
+extern void foo68445();
+extern void foo68446();
+extern void foo68447();
+extern void foo68448();
+extern void foo68449();
+extern void foo68450();
+extern void foo68451();
+extern void foo68452();
+extern void foo68453();
+extern void foo68454();
+extern void foo68455();
+extern void foo68456();
+extern void foo68457();
+extern void foo68458();
+extern void foo68459();
+extern void foo68460();
+extern void foo68461();
+extern void foo68462();
+extern void foo68463();
+extern void foo68464();
+extern void foo68465();
+extern void foo68466();
+extern void foo68467();
+extern void foo68468();
+extern void foo68469();
+extern void foo68470();
+extern void foo68471();
+extern void foo68472();
+extern void foo68473();
+extern void foo68474();
+extern void foo68475();
+extern void foo68476();
+extern void foo68477();
+extern void foo68478();
+extern void foo68479();
+extern void foo68480();
+extern void foo68481();
+extern void foo68482();
+extern void foo68483();
+extern void foo68484();
+extern void foo68485();
+extern void foo68486();
+extern void foo68487();
+extern void foo68488();
+extern void foo68489();
+extern void foo68490();
+extern void foo68491();
+extern void foo68492();
+extern void foo68493();
+extern void foo68494();
+extern void foo68495();
+extern void foo68496();
+extern void foo68497();
+extern void foo68498();
+extern void foo68499();
+extern void foo68500();
+extern void foo68501();
+extern void foo68502();
+extern void foo68503();
+extern void foo68504();
+extern void foo68505();
+extern void foo68506();
+extern void foo68507();
+extern void foo68508();
+extern void foo68509();
+extern void foo68510();
+extern void foo68511();
+extern void foo68512();
+extern void foo68513();
+extern void foo68514();
+extern void foo68515();
+extern void foo68516();
+extern void foo68517();
+extern void foo68518();
+extern void foo68519();
+extern void foo68520();
+extern void foo68521();
+extern void foo68522();
+extern void foo68523();
+extern void foo68524();
+extern void foo68525();
+extern void foo68526();
+extern void foo68527();
+extern void foo68528();
+extern void foo68529();
+extern void foo68530();
+extern void foo68531();
+extern void foo68532();
+extern void foo68533();
+extern void foo68534();
+extern void foo68535();
+extern void foo68536();
+extern void foo68537();
+extern void foo68538();
+extern void foo68539();
+extern void foo68540();
+extern void foo68541();
+extern void foo68542();
+extern void foo68543();
+extern void foo68544();
+extern void foo68545();
+extern void foo68546();
+extern void foo68547();
+extern void foo68548();
+extern void foo68549();
+extern void foo68550();
+extern void foo68551();
+extern void foo68552();
+extern void foo68553();
+extern void foo68554();
+extern void foo68555();
+extern void foo68556();
+extern void foo68557();
+extern void foo68558();
+extern void foo68559();
+extern void foo68560();
+extern void foo68561();
+extern void foo68562();
+extern void foo68563();
+extern void foo68564();
+extern void foo68565();
+extern void foo68566();
+extern void foo68567();
+extern void foo68568();
+extern void foo68569();
+extern void foo68570();
+extern void foo68571();
+extern void foo68572();
+extern void foo68573();
+extern void foo68574();
+extern void foo68575();
+extern void foo68576();
+extern void foo68577();
+extern void foo68578();
+extern void foo68579();
+extern void foo68580();
+extern void foo68581();
+extern void foo68582();
+extern void foo68583();
+extern void foo68584();
+extern void foo68585();
+extern void foo68586();
+extern void foo68587();
+extern void foo68588();
+extern void foo68589();
+extern void foo68590();
+extern void foo68591();
+extern void foo68592();
+extern void foo68593();
+extern void foo68594();
+extern void foo68595();
+extern void foo68596();
+extern void foo68597();
+extern void foo68598();
+extern void foo68599();
+extern void foo68600();
+extern void foo68601();
+extern void foo68602();
+extern void foo68603();
+extern void foo68604();
+extern void foo68605();
+extern void foo68606();
+extern void foo68607();
+extern void foo68608();
+extern void foo68609();
+extern void foo68610();
+extern void foo68611();
+extern void foo68612();
+extern void foo68613();
+extern void foo68614();
+extern void foo68615();
+extern void foo68616();
+extern void foo68617();
+extern void foo68618();
+extern void foo68619();
+extern void foo68620();
+extern void foo68621();
+extern void foo68622();
+extern void foo68623();
+extern void foo68624();
+extern void foo68625();
+extern void foo68626();
+extern void foo68627();
+extern void foo68628();
+extern void foo68629();
+extern void foo68630();
+extern void foo68631();
+extern void foo68632();
+extern void foo68633();
+extern void foo68634();
+extern void foo68635();
+extern void foo68636();
+extern void foo68637();
+extern void foo68638();
+extern void foo68639();
+extern void foo68640();
+extern void foo68641();
+extern void foo68642();
+extern void foo68643();
+extern void foo68644();
+extern void foo68645();
+extern void foo68646();
+extern void foo68647();
+extern void foo68648();
+extern void foo68649();
+extern void foo68650();
+extern void foo68651();
+extern void foo68652();
+extern void foo68653();
+extern void foo68654();
+extern void foo68655();
+extern void foo68656();
+extern void foo68657();
+extern void foo68658();
+extern void foo68659();
+extern void foo68660();
+extern void foo68661();
+extern void foo68662();
+extern void foo68663();
+extern void foo68664();
+extern void foo68665();
+extern void foo68666();
+extern void foo68667();
+extern void foo68668();
+extern void foo68669();
+extern void foo68670();
+extern void foo68671();
+extern void foo68672();
+extern void foo68673();
+extern void foo68674();
+extern void foo68675();
+extern void foo68676();
+extern void foo68677();
+extern void foo68678();
+extern void foo68679();
+extern void foo68680();
+extern void foo68681();
+extern void foo68682();
+extern void foo68683();
+extern void foo68684();
+extern void foo68685();
+extern void foo68686();
+extern void foo68687();
+extern void foo68688();
+extern void foo68689();
+extern void foo68690();
+extern void foo68691();
+extern void foo68692();
+extern void foo68693();
+extern void foo68694();
+extern void foo68695();
+extern void foo68696();
+extern void foo68697();
+extern void foo68698();
+extern void foo68699();
+extern void foo68700();
+extern void foo68701();
+extern void foo68702();
+extern void foo68703();
+extern void foo68704();
+extern void foo68705();
+extern void foo68706();
+extern void foo68707();
+extern void foo68708();
+extern void foo68709();
+extern void foo68710();
+extern void foo68711();
+extern void foo68712();
+extern void foo68713();
+extern void foo68714();
+extern void foo68715();
+extern void foo68716();
+extern void foo68717();
+extern void foo68718();
+extern void foo68719();
+extern void foo68720();
+extern void foo68721();
+extern void foo68722();
+extern void foo68723();
+extern void foo68724();
+extern void foo68725();
+extern void foo68726();
+extern void foo68727();
+extern void foo68728();
+extern void foo68729();
+extern void foo68730();
+extern void foo68731();
+extern void foo68732();
+extern void foo68733();
+extern void foo68734();
+extern void foo68735();
+extern void foo68736();
+extern void foo68737();
+extern void foo68738();
+extern void foo68739();
+extern void foo68740();
+extern void foo68741();
+extern void foo68742();
+extern void foo68743();
+extern void foo68744();
+extern void foo68745();
+extern void foo68746();
+extern void foo68747();
+extern void foo68748();
+extern void foo68749();
+extern void foo68750();
+extern void foo68751();
+extern void foo68752();
+extern void foo68753();
+extern void foo68754();
+extern void foo68755();
+extern void foo68756();
+extern void foo68757();
+extern void foo68758();
+extern void foo68759();
+extern void foo68760();
+extern void foo68761();
+extern void foo68762();
+extern void foo68763();
+extern void foo68764();
+extern void foo68765();
+extern void foo68766();
+extern void foo68767();
+extern void foo68768();
+extern void foo68769();
+extern void foo68770();
+extern void foo68771();
+extern void foo68772();
+extern void foo68773();
+extern void foo68774();
+extern void foo68775();
+extern void foo68776();
+extern void foo68777();
+extern void foo68778();
+extern void foo68779();
+extern void foo68780();
+extern void foo68781();
+extern void foo68782();
+extern void foo68783();
+extern void foo68784();
+extern void foo68785();
+extern void foo68786();
+extern void foo68787();
+extern void foo68788();
+extern void foo68789();
+extern void foo68790();
+extern void foo68791();
+extern void foo68792();
+extern void foo68793();
+extern void foo68794();
+extern void foo68795();
+extern void foo68796();
+extern void foo68797();
+extern void foo68798();
+extern void foo68799();
+extern void foo68800();
+extern void foo68801();
+extern void foo68802();
+extern void foo68803();
+extern void foo68804();
+extern void foo68805();
+extern void foo68806();
+extern void foo68807();
+extern void foo68808();
+extern void foo68809();
+extern void foo68810();
+extern void foo68811();
+extern void foo68812();
+extern void foo68813();
+extern void foo68814();
+extern void foo68815();
+extern void foo68816();
+extern void foo68817();
+extern void foo68818();
+extern void foo68819();
+extern void foo68820();
+extern void foo68821();
+extern void foo68822();
+extern void foo68823();
+extern void foo68824();
+extern void foo68825();
+extern void foo68826();
+extern void foo68827();
+extern void foo68828();
+extern void foo68829();
+extern void foo68830();
+extern void foo68831();
+extern void foo68832();
+extern void foo68833();
+extern void foo68834();
+extern void foo68835();
+extern void foo68836();
+extern void foo68837();
+extern void foo68838();
+extern void foo68839();
+extern void foo68840();
+extern void foo68841();
+extern void foo68842();
+extern void foo68843();
+extern void foo68844();
+extern void foo68845();
+extern void foo68846();
+extern void foo68847();
+extern void foo68848();
+extern void foo68849();
+extern void foo68850();
+extern void foo68851();
+extern void foo68852();
+extern void foo68853();
+extern void foo68854();
+extern void foo68855();
+extern void foo68856();
+extern void foo68857();
+extern void foo68858();
+extern void foo68859();
+extern void foo68860();
+extern void foo68861();
+extern void foo68862();
+extern void foo68863();
+extern void foo68864();
+extern void foo68865();
+extern void foo68866();
+extern void foo68867();
+extern void foo68868();
+extern void foo68869();
+extern void foo68870();
+extern void foo68871();
+extern void foo68872();
+extern void foo68873();
+extern void foo68874();
+extern void foo68875();
+extern void foo68876();
+extern void foo68877();
+extern void foo68878();
+extern void foo68879();
+extern void foo68880();
+extern void foo68881();
+extern void foo68882();
+extern void foo68883();
+extern void foo68884();
+extern void foo68885();
+extern void foo68886();
+extern void foo68887();
+extern void foo68888();
+extern void foo68889();
+extern void foo68890();
+extern void foo68891();
+extern void foo68892();
+extern void foo68893();
+extern void foo68894();
+extern void foo68895();
+extern void foo68896();
+extern void foo68897();
+extern void foo68898();
+extern void foo68899();
+extern void foo68900();
+extern void foo68901();
+extern void foo68902();
+extern void foo68903();
+extern void foo68904();
+extern void foo68905();
+extern void foo68906();
+extern void foo68907();
+extern void foo68908();
+extern void foo68909();
+extern void foo68910();
+extern void foo68911();
+extern void foo68912();
+extern void foo68913();
+extern void foo68914();
+extern void foo68915();
+extern void foo68916();
+extern void foo68917();
+extern void foo68918();
+extern void foo68919();
+extern void foo68920();
+extern void foo68921();
+extern void foo68922();
+extern void foo68923();
+extern void foo68924();
+extern void foo68925();
+extern void foo68926();
+extern void foo68927();
+extern void foo68928();
+extern void foo68929();
+extern void foo68930();
+extern void foo68931();
+extern void foo68932();
+extern void foo68933();
+extern void foo68934();
+extern void foo68935();
+extern void foo68936();
+extern void foo68937();
+extern void foo68938();
+extern void foo68939();
+extern void foo68940();
+extern void foo68941();
+extern void foo68942();
+extern void foo68943();
+extern void foo68944();
+extern void foo68945();
+extern void foo68946();
+extern void foo68947();
+extern void foo68948();
+extern void foo68949();
+extern void foo68950();
+extern void foo68951();
+extern void foo68952();
+extern void foo68953();
+extern void foo68954();
+extern void foo68955();
+extern void foo68956();
+extern void foo68957();
+extern void foo68958();
+extern void foo68959();
+extern void foo68960();
+extern void foo68961();
+extern void foo68962();
+extern void foo68963();
+extern void foo68964();
+extern void foo68965();
+extern void foo68966();
+extern void foo68967();
+extern void foo68968();
+extern void foo68969();
+extern void foo68970();
+extern void foo68971();
+extern void foo68972();
+extern void foo68973();
+extern void foo68974();
+extern void foo68975();
+extern void foo68976();
+extern void foo68977();
+extern void foo68978();
+extern void foo68979();
+extern void foo68980();
+extern void foo68981();
+extern void foo68982();
+extern void foo68983();
+extern void foo68984();
+extern void foo68985();
+extern void foo68986();
+extern void foo68987();
+extern void foo68988();
+extern void foo68989();
+extern void foo68990();
+extern void foo68991();
+extern void foo68992();
+extern void foo68993();
+extern void foo68994();
+extern void foo68995();
+extern void foo68996();
+extern void foo68997();
+extern void foo68998();
+extern void foo68999();
+extern void foo69000();
+extern void foo69001();
+extern void foo69002();
+extern void foo69003();
+extern void foo69004();
+extern void foo69005();
+extern void foo69006();
+extern void foo69007();
+extern void foo69008();
+extern void foo69009();
+extern void foo69010();
+extern void foo69011();
+extern void foo69012();
+extern void foo69013();
+extern void foo69014();
+extern void foo69015();
+extern void foo69016();
+extern void foo69017();
+extern void foo69018();
+extern void foo69019();
+extern void foo69020();
+extern void foo69021();
+extern void foo69022();
+extern void foo69023();
+extern void foo69024();
+extern void foo69025();
+extern void foo69026();
+extern void foo69027();
+extern void foo69028();
+extern void foo69029();
+extern void foo69030();
+extern void foo69031();
+extern void foo69032();
+extern void foo69033();
+extern void foo69034();
+extern void foo69035();
+extern void foo69036();
+extern void foo69037();
+extern void foo69038();
+extern void foo69039();
+extern void foo69040();
+extern void foo69041();
+extern void foo69042();
+extern void foo69043();
+extern void foo69044();
+extern void foo69045();
+extern void foo69046();
+extern void foo69047();
+extern void foo69048();
+extern void foo69049();
+extern void foo69050();
+extern void foo69051();
+extern void foo69052();
+extern void foo69053();
+extern void foo69054();
+extern void foo69055();
+extern void foo69056();
+extern void foo69057();
+extern void foo69058();
+extern void foo69059();
+extern void foo69060();
+extern void foo69061();
+extern void foo69062();
+extern void foo69063();
+extern void foo69064();
+extern void foo69065();
+extern void foo69066();
+extern void foo69067();
+extern void foo69068();
+extern void foo69069();
+extern void foo69070();
+extern void foo69071();
+extern void foo69072();
+extern void foo69073();
+extern void foo69074();
+extern void foo69075();
+extern void foo69076();
+extern void foo69077();
+extern void foo69078();
+extern void foo69079();
+extern void foo69080();
+extern void foo69081();
+extern void foo69082();
+extern void foo69083();
+extern void foo69084();
+extern void foo69085();
+extern void foo69086();
+extern void foo69087();
+extern void foo69088();
+extern void foo69089();
+extern void foo69090();
+extern void foo69091();
+extern void foo69092();
+extern void foo69093();
+extern void foo69094();
+extern void foo69095();
+extern void foo69096();
+extern void foo69097();
+extern void foo69098();
+extern void foo69099();
+extern void foo69100();
+extern void foo69101();
+extern void foo69102();
+extern void foo69103();
+extern void foo69104();
+extern void foo69105();
+extern void foo69106();
+extern void foo69107();
+extern void foo69108();
+extern void foo69109();
+extern void foo69110();
+extern void foo69111();
+extern void foo69112();
+extern void foo69113();
+extern void foo69114();
+extern void foo69115();
+extern void foo69116();
+extern void foo69117();
+extern void foo69118();
+extern void foo69119();
+extern void foo69120();
+extern void foo69121();
+extern void foo69122();
+extern void foo69123();
+extern void foo69124();
+extern void foo69125();
+extern void foo69126();
+extern void foo69127();
+extern void foo69128();
+extern void foo69129();
+extern void foo69130();
+extern void foo69131();
+extern void foo69132();
+extern void foo69133();
+extern void foo69134();
+extern void foo69135();
+extern void foo69136();
+extern void foo69137();
+extern void foo69138();
+extern void foo69139();
+extern void foo69140();
+extern void foo69141();
+extern void foo69142();
+extern void foo69143();
+extern void foo69144();
+extern void foo69145();
+extern void foo69146();
+extern void foo69147();
+extern void foo69148();
+extern void foo69149();
+extern void foo69150();
+extern void foo69151();
+extern void foo69152();
+extern void foo69153();
+extern void foo69154();
+extern void foo69155();
+extern void foo69156();
+extern void foo69157();
+extern void foo69158();
+extern void foo69159();
+extern void foo69160();
+extern void foo69161();
+extern void foo69162();
+extern void foo69163();
+extern void foo69164();
+extern void foo69165();
+extern void foo69166();
+extern void foo69167();
+extern void foo69168();
+extern void foo69169();
+extern void foo69170();
+extern void foo69171();
+extern void foo69172();
+extern void foo69173();
+extern void foo69174();
+extern void foo69175();
+extern void foo69176();
+extern void foo69177();
+extern void foo69178();
+extern void foo69179();
+extern void foo69180();
+extern void foo69181();
+extern void foo69182();
+extern void foo69183();
+extern void foo69184();
+extern void foo69185();
+extern void foo69186();
+extern void foo69187();
+extern void foo69188();
+extern void foo69189();
+extern void foo69190();
+extern void foo69191();
+extern void foo69192();
+extern void foo69193();
+extern void foo69194();
+extern void foo69195();
+extern void foo69196();
+extern void foo69197();
+extern void foo69198();
+extern void foo69199();
+extern void foo69200();
+extern void foo69201();
+extern void foo69202();
+extern void foo69203();
+extern void foo69204();
+extern void foo69205();
+extern void foo69206();
+extern void foo69207();
+extern void foo69208();
+extern void foo69209();
+extern void foo69210();
+extern void foo69211();
+extern void foo69212();
+extern void foo69213();
+extern void foo69214();
+extern void foo69215();
+extern void foo69216();
+extern void foo69217();
+extern void foo69218();
+extern void foo69219();
+extern void foo69220();
+extern void foo69221();
+extern void foo69222();
+extern void foo69223();
+extern void foo69224();
+extern void foo69225();
+extern void foo69226();
+extern void foo69227();
+extern void foo69228();
+extern void foo69229();
+extern void foo69230();
+extern void foo69231();
+extern void foo69232();
+extern void foo69233();
+extern void foo69234();
+extern void foo69235();
+extern void foo69236();
+extern void foo69237();
+extern void foo69238();
+extern void foo69239();
+extern void foo69240();
+extern void foo69241();
+extern void foo69242();
+extern void foo69243();
+extern void foo69244();
+extern void foo69245();
+extern void foo69246();
+extern void foo69247();
+extern void foo69248();
+extern void foo69249();
+extern void foo69250();
+extern void foo69251();
+extern void foo69252();
+extern void foo69253();
+extern void foo69254();
+extern void foo69255();
+extern void foo69256();
+extern void foo69257();
+extern void foo69258();
+extern void foo69259();
+extern void foo69260();
+extern void foo69261();
+extern void foo69262();
+extern void foo69263();
+extern void foo69264();
+extern void foo69265();
+extern void foo69266();
+extern void foo69267();
+extern void foo69268();
+extern void foo69269();
+extern void foo69270();
+extern void foo69271();
+extern void foo69272();
+extern void foo69273();
+extern void foo69274();
+extern void foo69275();
+extern void foo69276();
+extern void foo69277();
+extern void foo69278();
+extern void foo69279();
+extern void foo69280();
+extern void foo69281();
+extern void foo69282();
+extern void foo69283();
+extern void foo69284();
+extern void foo69285();
+extern void foo69286();
+extern void foo69287();
+extern void foo69288();
+extern void foo69289();
+extern void foo69290();
+extern void foo69291();
+extern void foo69292();
+extern void foo69293();
+extern void foo69294();
+extern void foo69295();
+extern void foo69296();
+extern void foo69297();
+extern void foo69298();
+extern void foo69299();
+extern void foo69300();
+extern void foo69301();
+extern void foo69302();
+extern void foo69303();
+extern void foo69304();
+extern void foo69305();
+extern void foo69306();
+extern void foo69307();
+extern void foo69308();
+extern void foo69309();
+extern void foo69310();
+extern void foo69311();
+extern void foo69312();
+extern void foo69313();
+extern void foo69314();
+extern void foo69315();
+extern void foo69316();
+extern void foo69317();
+extern void foo69318();
+extern void foo69319();
+extern void foo69320();
+extern void foo69321();
+extern void foo69322();
+extern void foo69323();
+extern void foo69324();
+extern void foo69325();
+extern void foo69326();
+extern void foo69327();
+extern void foo69328();
+extern void foo69329();
+extern void foo69330();
+extern void foo69331();
+extern void foo69332();
+extern void foo69333();
+extern void foo69334();
+extern void foo69335();
+extern void foo69336();
+extern void foo69337();
+extern void foo69338();
+extern void foo69339();
+extern void foo69340();
+extern void foo69341();
+extern void foo69342();
+extern void foo69343();
+extern void foo69344();
+extern void foo69345();
+extern void foo69346();
+extern void foo69347();
+extern void foo69348();
+extern void foo69349();
+extern void foo69350();
+extern void foo69351();
+extern void foo69352();
+extern void foo69353();
+extern void foo69354();
+extern void foo69355();
+extern void foo69356();
+extern void foo69357();
+extern void foo69358();
+extern void foo69359();
+extern void foo69360();
+extern void foo69361();
+extern void foo69362();
+extern void foo69363();
+extern void foo69364();
+extern void foo69365();
+extern void foo69366();
+extern void foo69367();
+extern void foo69368();
+extern void foo69369();
+extern void foo69370();
+extern void foo69371();
+extern void foo69372();
+extern void foo69373();
+extern void foo69374();
+extern void foo69375();
+extern void foo69376();
+extern void foo69377();
+extern void foo69378();
+extern void foo69379();
+extern void foo69380();
+extern void foo69381();
+extern void foo69382();
+extern void foo69383();
+extern void foo69384();
+extern void foo69385();
+extern void foo69386();
+extern void foo69387();
+extern void foo69388();
+extern void foo69389();
+extern void foo69390();
+extern void foo69391();
+extern void foo69392();
+extern void foo69393();
+extern void foo69394();
+extern void foo69395();
+extern void foo69396();
+extern void foo69397();
+extern void foo69398();
+extern void foo69399();
+extern void foo69400();
+extern void foo69401();
+extern void foo69402();
+extern void foo69403();
+extern void foo69404();
+extern void foo69405();
+extern void foo69406();
+extern void foo69407();
+extern void foo69408();
+extern void foo69409();
+extern void foo69410();
+extern void foo69411();
+extern void foo69412();
+extern void foo69413();
+extern void foo69414();
+extern void foo69415();
+extern void foo69416();
+extern void foo69417();
+extern void foo69418();
+extern void foo69419();
+extern void foo69420();
+extern void foo69421();
+extern void foo69422();
+extern void foo69423();
+extern void foo69424();
+extern void foo69425();
+extern void foo69426();
+extern void foo69427();
+extern void foo69428();
+extern void foo69429();
+extern void foo69430();
+extern void foo69431();
+extern void foo69432();
+extern void foo69433();
+extern void foo69434();
+extern void foo69435();
+extern void foo69436();
+extern void foo69437();
+extern void foo69438();
+extern void foo69439();
+extern void foo69440();
+extern void foo69441();
+extern void foo69442();
+extern void foo69443();
+extern void foo69444();
+extern void foo69445();
+extern void foo69446();
+extern void foo69447();
+extern void foo69448();
+extern void foo69449();
+extern void foo69450();
+extern void foo69451();
+extern void foo69452();
+extern void foo69453();
+extern void foo69454();
+extern void foo69455();
+extern void foo69456();
+extern void foo69457();
+extern void foo69458();
+extern void foo69459();
+extern void foo69460();
+extern void foo69461();
+extern void foo69462();
+extern void foo69463();
+extern void foo69464();
+extern void foo69465();
+extern void foo69466();
+extern void foo69467();
+extern void foo69468();
+extern void foo69469();
+extern void foo69470();
+extern void foo69471();
+extern void foo69472();
+extern void foo69473();
+extern void foo69474();
+extern void foo69475();
+extern void foo69476();
+extern void foo69477();
+extern void foo69478();
+extern void foo69479();
+extern void foo69480();
+extern void foo69481();
+extern void foo69482();
+extern void foo69483();
+extern void foo69484();
+extern void foo69485();
+extern void foo69486();
+extern void foo69487();
+extern void foo69488();
+extern void foo69489();
+extern void foo69490();
+extern void foo69491();
+extern void foo69492();
+extern void foo69493();
+extern void foo69494();
+extern void foo69495();
+extern void foo69496();
+extern void foo69497();
+extern void foo69498();
+extern void foo69499();
+extern void foo69500();
+extern void foo69501();
+extern void foo69502();
+extern void foo69503();
+extern void foo69504();
+extern void foo69505();
+extern void foo69506();
+extern void foo69507();
+extern void foo69508();
+extern void foo69509();
+extern void foo69510();
+extern void foo69511();
+extern void foo69512();
+extern void foo69513();
+extern void foo69514();
+extern void foo69515();
+extern void foo69516();
+extern void foo69517();
+extern void foo69518();
+extern void foo69519();
+extern void foo69520();
+extern void foo69521();
+extern void foo69522();
+extern void foo69523();
+extern void foo69524();
+extern void foo69525();
+extern void foo69526();
+extern void foo69527();
+extern void foo69528();
+extern void foo69529();
+extern void foo69530();
+extern void foo69531();
+extern void foo69532();
+extern void foo69533();
+extern void foo69534();
+extern void foo69535();
+extern void foo69536();
+extern void foo69537();
+extern void foo69538();
+extern void foo69539();
+extern void foo69540();
+extern void foo69541();
+extern void foo69542();
+extern void foo69543();
+extern void foo69544();
+extern void foo69545();
+extern void foo69546();
+extern void foo69547();
+extern void foo69548();
+extern void foo69549();
+extern void foo69550();
+extern void foo69551();
+extern void foo69552();
+extern void foo69553();
+extern void foo69554();
+extern void foo69555();
+extern void foo69556();
+extern void foo69557();
+extern void foo69558();
+extern void foo69559();
+extern void foo69560();
+extern void foo69561();
+extern void foo69562();
+extern void foo69563();
+extern void foo69564();
+extern void foo69565();
+extern void foo69566();
+extern void foo69567();
+extern void foo69568();
+extern void foo69569();
+extern void foo69570();
+extern void foo69571();
+extern void foo69572();
+extern void foo69573();
+extern void foo69574();
+extern void foo69575();
+extern void foo69576();
+extern void foo69577();
+extern void foo69578();
+extern void foo69579();
+extern void foo69580();
+extern void foo69581();
+extern void foo69582();
+extern void foo69583();
+extern void foo69584();
+extern void foo69585();
+extern void foo69586();
+extern void foo69587();
+extern void foo69588();
+extern void foo69589();
+extern void foo69590();
+extern void foo69591();
+extern void foo69592();
+extern void foo69593();
+extern void foo69594();
+extern void foo69595();
+extern void foo69596();
+extern void foo69597();
+extern void foo69598();
+extern void foo69599();
+extern void foo69600();
+extern void foo69601();
+extern void foo69602();
+extern void foo69603();
+extern void foo69604();
+extern void foo69605();
+extern void foo69606();
+extern void foo69607();
+extern void foo69608();
+extern void foo69609();
+extern void foo69610();
+extern void foo69611();
+extern void foo69612();
+extern void foo69613();
+extern void foo69614();
+extern void foo69615();
+extern void foo69616();
+extern void foo69617();
+extern void foo69618();
+extern void foo69619();
+extern void foo69620();
+extern void foo69621();
+extern void foo69622();
+extern void foo69623();
+extern void foo69624();
+extern void foo69625();
+extern void foo69626();
+extern void foo69627();
+extern void foo69628();
+extern void foo69629();
+extern void foo69630();
+extern void foo69631();
+extern void foo69632();
+extern void foo69633();
+extern void foo69634();
+extern void foo69635();
+extern void foo69636();
+extern void foo69637();
+extern void foo69638();
+extern void foo69639();
+extern void foo69640();
+extern void foo69641();
+extern void foo69642();
+extern void foo69643();
+extern void foo69644();
+extern void foo69645();
+extern void foo69646();
+extern void foo69647();
+extern void foo69648();
+extern void foo69649();
+extern void foo69650();
+extern void foo69651();
+extern void foo69652();
+extern void foo69653();
+extern void foo69654();
+extern void foo69655();
+extern void foo69656();
+extern void foo69657();
+extern void foo69658();
+extern void foo69659();
+extern void foo69660();
+extern void foo69661();
+extern void foo69662();
+extern void foo69663();
+extern void foo69664();
+extern void foo69665();
+extern void foo69666();
+extern void foo69667();
+extern void foo69668();
+extern void foo69669();
+extern void foo69670();
+extern void foo69671();
+extern void foo69672();
+extern void foo69673();
+extern void foo69674();
+extern void foo69675();
+extern void foo69676();
+extern void foo69677();
+extern void foo69678();
+extern void foo69679();
+extern void foo69680();
+extern void foo69681();
+extern void foo69682();
+extern void foo69683();
+extern void foo69684();
+extern void foo69685();
+extern void foo69686();
+extern void foo69687();
+extern void foo69688();
+extern void foo69689();
+extern void foo69690();
+extern void foo69691();
+extern void foo69692();
+extern void foo69693();
+extern void foo69694();
+extern void foo69695();
+extern void foo69696();
+extern void foo69697();
+extern void foo69698();
+extern void foo69699();
+extern void foo69700();
+extern void foo69701();
+extern void foo69702();
+extern void foo69703();
+extern void foo69704();
+extern void foo69705();
+extern void foo69706();
+extern void foo69707();
+extern void foo69708();
+extern void foo69709();
+extern void foo69710();
+extern void foo69711();
+extern void foo69712();
+extern void foo69713();
+extern void foo69714();
+extern void foo69715();
+extern void foo69716();
+extern void foo69717();
+extern void foo69718();
+extern void foo69719();
+extern void foo69720();
+extern void foo69721();
+extern void foo69722();
+extern void foo69723();
+extern void foo69724();
+extern void foo69725();
+extern void foo69726();
+extern void foo69727();
+extern void foo69728();
+extern void foo69729();
+extern void foo69730();
+extern void foo69731();
+extern void foo69732();
+extern void foo69733();
+extern void foo69734();
+extern void foo69735();
+extern void foo69736();
+extern void foo69737();
+extern void foo69738();
+extern void foo69739();
+extern void foo69740();
+extern void foo69741();
+extern void foo69742();
+extern void foo69743();
+extern void foo69744();
+extern void foo69745();
+extern void foo69746();
+extern void foo69747();
+extern void foo69748();
+extern void foo69749();
+extern void foo69750();
+extern void foo69751();
+extern void foo69752();
+extern void foo69753();
+extern void foo69754();
+extern void foo69755();
+extern void foo69756();
+extern void foo69757();
+extern void foo69758();
+extern void foo69759();
+extern void foo69760();
+extern void foo69761();
+extern void foo69762();
+extern void foo69763();
+extern void foo69764();
+extern void foo69765();
+extern void foo69766();
+extern void foo69767();
+extern void foo69768();
+extern void foo69769();
+extern void foo69770();
+extern void foo69771();
+extern void foo69772();
+extern void foo69773();
+extern void foo69774();
+extern void foo69775();
+extern void foo69776();
+extern void foo69777();
+extern void foo69778();
+extern void foo69779();
+extern void foo69780();
+extern void foo69781();
+extern void foo69782();
+extern void foo69783();
+extern void foo69784();
+extern void foo69785();
+extern void foo69786();
+extern void foo69787();
+extern void foo69788();
+extern void foo69789();
+extern void foo69790();
+extern void foo69791();
+extern void foo69792();
+extern void foo69793();
+extern void foo69794();
+extern void foo69795();
+extern void foo69796();
+extern void foo69797();
+extern void foo69798();
+extern void foo69799();
+extern void foo69800();
+extern void foo69801();
+extern void foo69802();
+extern void foo69803();
+extern void foo69804();
+extern void foo69805();
+extern void foo69806();
+extern void foo69807();
+extern void foo69808();
+extern void foo69809();
+extern void foo69810();
+extern void foo69811();
+extern void foo69812();
+extern void foo69813();
+extern void foo69814();
+extern void foo69815();
+extern void foo69816();
+extern void foo69817();
+extern void foo69818();
+extern void foo69819();
+extern void foo69820();
+extern void foo69821();
+extern void foo69822();
+extern void foo69823();
+extern void foo69824();
+extern void foo69825();
+extern void foo69826();
+extern void foo69827();
+extern void foo69828();
+extern void foo69829();
+extern void foo69830();
+extern void foo69831();
+extern void foo69832();
+extern void foo69833();
+extern void foo69834();
+extern void foo69835();
+extern void foo69836();
+extern void foo69837();
+extern void foo69838();
+extern void foo69839();
+extern void foo69840();
+extern void foo69841();
+extern void foo69842();
+extern void foo69843();
+extern void foo69844();
+extern void foo69845();
+extern void foo69846();
+extern void foo69847();
+extern void foo69848();
+extern void foo69849();
+extern void foo69850();
+extern void foo69851();
+extern void foo69852();
+extern void foo69853();
+extern void foo69854();
+extern void foo69855();
+extern void foo69856();
+extern void foo69857();
+extern void foo69858();
+extern void foo69859();
+extern void foo69860();
+extern void foo69861();
+extern void foo69862();
+extern void foo69863();
+extern void foo69864();
+extern void foo69865();
+extern void foo69866();
+extern void foo69867();
+extern void foo69868();
+extern void foo69869();
+extern void foo69870();
+extern void foo69871();
+extern void foo69872();
+extern void foo69873();
+extern void foo69874();
+extern void foo69875();
+extern void foo69876();
+extern void foo69877();
+extern void foo69878();
+extern void foo69879();
+extern void foo69880();
+extern void foo69881();
+extern void foo69882();
+extern void foo69883();
+extern void foo69884();
+extern void foo69885();
+extern void foo69886();
+extern void foo69887();
+extern void foo69888();
+extern void foo69889();
+extern void foo69890();
+extern void foo69891();
+extern void foo69892();
+extern void foo69893();
+extern void foo69894();
+extern void foo69895();
+extern void foo69896();
+extern void foo69897();
+extern void foo69898();
+extern void foo69899();
+extern void foo69900();
+extern void foo69901();
+extern void foo69902();
+extern void foo69903();
+extern void foo69904();
+extern void foo69905();
+extern void foo69906();
+extern void foo69907();
+extern void foo69908();
+extern void foo69909();
+extern void foo69910();
+extern void foo69911();
+extern void foo69912();
+extern void foo69913();
+extern void foo69914();
+extern void foo69915();
+extern void foo69916();
+extern void foo69917();
+extern void foo69918();
+extern void foo69919();
+extern void foo69920();
+extern void foo69921();
+extern void foo69922();
+extern void foo69923();
+extern void foo69924();
+extern void foo69925();
+extern void foo69926();
+extern void foo69927();
+extern void foo69928();
+extern void foo69929();
+extern void foo69930();
+extern void foo69931();
+extern void foo69932();
+extern void foo69933();
+extern void foo69934();
+extern void foo69935();
+extern void foo69936();
+extern void foo69937();
+extern void foo69938();
+extern void foo69939();
+extern void foo69940();
+extern void foo69941();
+extern void foo69942();
+extern void foo69943();
+extern void foo69944();
+extern void foo69945();
+extern void foo69946();
+extern void foo69947();
+extern void foo69948();
+extern void foo69949();
+extern void foo69950();
+extern void foo69951();
+extern void foo69952();
+extern void foo69953();
+extern void foo69954();
+extern void foo69955();
+extern void foo69956();
+extern void foo69957();
+extern void foo69958();
+extern void foo69959();
+extern void foo69960();
+extern void foo69961();
+extern void foo69962();
+extern void foo69963();
+extern void foo69964();
+extern void foo69965();
+extern void foo69966();
+extern void foo69967();
+extern void foo69968();
+extern void foo69969();
+extern void foo69970();
+extern void foo69971();
+extern void foo69972();
+extern void foo69973();
+extern void foo69974();
+extern void foo69975();
+extern void foo69976();
+extern void foo69977();
+extern void foo69978();
+extern void foo69979();
+extern void foo69980();
+extern void foo69981();
+extern void foo69982();
+extern void foo69983();
+extern void foo69984();
+extern void foo69985();
+extern void foo69986();
+extern void foo69987();
+extern void foo69988();
+extern void foo69989();
+extern void foo69990();
+extern void foo69991();
+extern void foo69992();
+extern void foo69993();
+extern void foo69994();
+extern void foo69995();
+extern void foo69996();
+extern void foo69997();
+extern void foo69998();
+extern void foo69999();
+extern void foo70000();
index 35b0e779cf233368272c796c5c2c9bc8d6ed79ce..4d7b4e01ee977d1ac63c934508293dbdc1a8362c 100644 (file)
@@ -5,9 +5,9 @@
 // RUN:  ./chained-fixups-many-binds.exe
 
 // Here's how to generate this monster
-// ( for i in `seq 1 65000`; do echo "void foo$i() { }"; done ) > foo.c
-// ( for i in `seq 1 65000`; do echo "extern void foo$i();"; done ) > foo.h
-// ( for i in `seq 1 65000`; do echo "__attribute__((used)) void* use$i = (void*)&foo$i;"; done ) > uses.h
+// ( for i in `seq 1 70000`; do echo "void foo$i() { }"; done ) > foo.c
+// ( for i in `seq 1 70000`; do echo "extern void foo$i();"; done ) > foo.h
+// ( for i in `seq 1 70000`; do echo "__attribute__((used)) void* use$i = (void*)&foo$i;"; done ) > uses.h
 
 #include <stdio.h>
 
index d61c61f401325e5ce2514680ec421b962c41eb49..6a86ce3b23b48d6408a2bde99647b58b4c1e80c7 100644 (file)
@@ -64998,3 +64998,5003 @@ __attribute__((used)) void* use64997 = (void*)&foo64997;
 __attribute__((used)) void* use64998 = (void*)&foo64998;
 __attribute__((used)) void* use64999 = (void*)&foo64999;
 __attribute__((used)) void* use65000 = (void*)&foo65000;
+__attribute__((used)) void* use65001 = (void*)&foo65001;
+__attribute__((used)) void* use65002 = (void*)&foo65002;
+__attribute__((used)) void* use65003 = (void*)&foo65003;
+__attribute__((used)) void* use65004 = (void*)&foo65004;
+__attribute__((used)) void* use65005 = (void*)&foo65005;
+__attribute__((used)) void* use65006 = (void*)&foo65006;
+__attribute__((used)) void* use65007 = (void*)&foo65007;
+__attribute__((used)) void* use65008 = (void*)&foo65008;
+__attribute__((used)) void* use65009 = (void*)&foo65009;
+__attribute__((used)) void* use65010 = (void*)&foo65010;
+__attribute__((used)) void* use65011 = (void*)&foo65011;
+__attribute__((used)) void* use65012 = (void*)&foo65012;
+__attribute__((used)) void* use65013 = (void*)&foo65013;
+__attribute__((used)) void* use65014 = (void*)&foo65014;
+__attribute__((used)) void* use65015 = (void*)&foo65015;
+__attribute__((used)) void* use65016 = (void*)&foo65016;
+__attribute__((used)) void* use65017 = (void*)&foo65017;
+__attribute__((used)) void* use65018 = (void*)&foo65018;
+__attribute__((used)) void* use65019 = (void*)&foo65019;
+__attribute__((used)) void* use65020 = (void*)&foo65020;
+__attribute__((used)) void* use65021 = (void*)&foo65021;
+__attribute__((used)) void* use65022 = (void*)&foo65022;
+__attribute__((used)) void* use65023 = (void*)&foo65023;
+__attribute__((used)) void* use65024 = (void*)&foo65024;
+__attribute__((used)) void* use65025 = (void*)&foo65025;
+__attribute__((used)) void* use65026 = (void*)&foo65026;
+__attribute__((used)) void* use65027 = (void*)&foo65027;
+__attribute__((used)) void* use65028 = (void*)&foo65028;
+__attribute__((used)) void* use65029 = (void*)&foo65029;
+__attribute__((used)) void* use65030 = (void*)&foo65030;
+__attribute__((used)) void* use65031 = (void*)&foo65031;
+__attribute__((used)) void* use65032 = (void*)&foo65032;
+__attribute__((used)) void* use65033 = (void*)&foo65033;
+__attribute__((used)) void* use65034 = (void*)&foo65034;
+__attribute__((used)) void* use65035 = (void*)&foo65035;
+__attribute__((used)) void* use65036 = (void*)&foo65036;
+__attribute__((used)) void* use65037 = (void*)&foo65037;
+__attribute__((used)) void* use65038 = (void*)&foo65038;
+__attribute__((used)) void* use65039 = (void*)&foo65039;
+__attribute__((used)) void* use65040 = (void*)&foo65040;
+__attribute__((used)) void* use65041 = (void*)&foo65041;
+__attribute__((used)) void* use65042 = (void*)&foo65042;
+__attribute__((used)) void* use65043 = (void*)&foo65043;
+__attribute__((used)) void* use65044 = (void*)&foo65044;
+__attribute__((used)) void* use65045 = (void*)&foo65045;
+__attribute__((used)) void* use65046 = (void*)&foo65046;
+__attribute__((used)) void* use65047 = (void*)&foo65047;
+__attribute__((used)) void* use65048 = (void*)&foo65048;
+__attribute__((used)) void* use65049 = (void*)&foo65049;
+__attribute__((used)) void* use65050 = (void*)&foo65050;
+__attribute__((used)) void* use65051 = (void*)&foo65051;
+__attribute__((used)) void* use65052 = (void*)&foo65052;
+__attribute__((used)) void* use65053 = (void*)&foo65053;
+__attribute__((used)) void* use65054 = (void*)&foo65054;
+__attribute__((used)) void* use65055 = (void*)&foo65055;
+__attribute__((used)) void* use65056 = (void*)&foo65056;
+__attribute__((used)) void* use65057 = (void*)&foo65057;
+__attribute__((used)) void* use65058 = (void*)&foo65058;
+__attribute__((used)) void* use65059 = (void*)&foo65059;
+__attribute__((used)) void* use65060 = (void*)&foo65060;
+__attribute__((used)) void* use65061 = (void*)&foo65061;
+__attribute__((used)) void* use65062 = (void*)&foo65062;
+__attribute__((used)) void* use65063 = (void*)&foo65063;
+__attribute__((used)) void* use65064 = (void*)&foo65064;
+__attribute__((used)) void* use65065 = (void*)&foo65065;
+__attribute__((used)) void* use65066 = (void*)&foo65066;
+__attribute__((used)) void* use65067 = (void*)&foo65067;
+__attribute__((used)) void* use65068 = (void*)&foo65068;
+__attribute__((used)) void* use65069 = (void*)&foo65069;
+__attribute__((used)) void* use65070 = (void*)&foo65070;
+__attribute__((used)) void* use65071 = (void*)&foo65071;
+__attribute__((used)) void* use65072 = (void*)&foo65072;
+__attribute__((used)) void* use65073 = (void*)&foo65073;
+__attribute__((used)) void* use65074 = (void*)&foo65074;
+__attribute__((used)) void* use65075 = (void*)&foo65075;
+__attribute__((used)) void* use65076 = (void*)&foo65076;
+__attribute__((used)) void* use65077 = (void*)&foo65077;
+__attribute__((used)) void* use65078 = (void*)&foo65078;
+__attribute__((used)) void* use65079 = (void*)&foo65079;
+__attribute__((used)) void* use65080 = (void*)&foo65080;
+__attribute__((used)) void* use65081 = (void*)&foo65081;
+__attribute__((used)) void* use65082 = (void*)&foo65082;
+__attribute__((used)) void* use65083 = (void*)&foo65083;
+__attribute__((used)) void* use65084 = (void*)&foo65084;
+__attribute__((used)) void* use65085 = (void*)&foo65085;
+__attribute__((used)) void* use65086 = (void*)&foo65086;
+__attribute__((used)) void* use65087 = (void*)&foo65087;
+__attribute__((used)) void* use65088 = (void*)&foo65088;
+__attribute__((used)) void* use65089 = (void*)&foo65089;
+__attribute__((used)) void* use65090 = (void*)&foo65090;
+__attribute__((used)) void* use65091 = (void*)&foo65091;
+__attribute__((used)) void* use65092 = (void*)&foo65092;
+__attribute__((used)) void* use65093 = (void*)&foo65093;
+__attribute__((used)) void* use65094 = (void*)&foo65094;
+__attribute__((used)) void* use65095 = (void*)&foo65095;
+__attribute__((used)) void* use65096 = (void*)&foo65096;
+__attribute__((used)) void* use65097 = (void*)&foo65097;
+__attribute__((used)) void* use65098 = (void*)&foo65098;
+__attribute__((used)) void* use65099 = (void*)&foo65099;
+__attribute__((used)) void* use65100 = (void*)&foo65100;
+__attribute__((used)) void* use65101 = (void*)&foo65101;
+__attribute__((used)) void* use65102 = (void*)&foo65102;
+__attribute__((used)) void* use65103 = (void*)&foo65103;
+__attribute__((used)) void* use65104 = (void*)&foo65104;
+__attribute__((used)) void* use65105 = (void*)&foo65105;
+__attribute__((used)) void* use65106 = (void*)&foo65106;
+__attribute__((used)) void* use65107 = (void*)&foo65107;
+__attribute__((used)) void* use65108 = (void*)&foo65108;
+__attribute__((used)) void* use65109 = (void*)&foo65109;
+__attribute__((used)) void* use65110 = (void*)&foo65110;
+__attribute__((used)) void* use65111 = (void*)&foo65111;
+__attribute__((used)) void* use65112 = (void*)&foo65112;
+__attribute__((used)) void* use65113 = (void*)&foo65113;
+__attribute__((used)) void* use65114 = (void*)&foo65114;
+__attribute__((used)) void* use65115 = (void*)&foo65115;
+__attribute__((used)) void* use65116 = (void*)&foo65116;
+__attribute__((used)) void* use65117 = (void*)&foo65117;
+__attribute__((used)) void* use65118 = (void*)&foo65118;
+__attribute__((used)) void* use65119 = (void*)&foo65119;
+__attribute__((used)) void* use65120 = (void*)&foo65120;
+__attribute__((used)) void* use65121 = (void*)&foo65121;
+__attribute__((used)) void* use65122 = (void*)&foo65122;
+__attribute__((used)) void* use65123 = (void*)&foo65123;
+__attribute__((used)) void* use65124 = (void*)&foo65124;
+__attribute__((used)) void* use65125 = (void*)&foo65125;
+__attribute__((used)) void* use65126 = (void*)&foo65126;
+__attribute__((used)) void* use65127 = (void*)&foo65127;
+__attribute__((used)) void* use65128 = (void*)&foo65128;
+__attribute__((used)) void* use65129 = (void*)&foo65129;
+__attribute__((used)) void* use65130 = (void*)&foo65130;
+__attribute__((used)) void* use65131 = (void*)&foo65131;
+__attribute__((used)) void* use65132 = (void*)&foo65132;
+__attribute__((used)) void* use65133 = (void*)&foo65133;
+__attribute__((used)) void* use65134 = (void*)&foo65134;
+__attribute__((used)) void* use65135 = (void*)&foo65135;
+__attribute__((used)) void* use65136 = (void*)&foo65136;
+__attribute__((used)) void* use65137 = (void*)&foo65137;
+__attribute__((used)) void* use65138 = (void*)&foo65138;
+__attribute__((used)) void* use65139 = (void*)&foo65139;
+__attribute__((used)) void* use65140 = (void*)&foo65140;
+__attribute__((used)) void* use65141 = (void*)&foo65141;
+__attribute__((used)) void* use65142 = (void*)&foo65142;
+__attribute__((used)) void* use65143 = (void*)&foo65143;
+__attribute__((used)) void* use65144 = (void*)&foo65144;
+__attribute__((used)) void* use65145 = (void*)&foo65145;
+__attribute__((used)) void* use65146 = (void*)&foo65146;
+__attribute__((used)) void* use65147 = (void*)&foo65147;
+__attribute__((used)) void* use65148 = (void*)&foo65148;
+__attribute__((used)) void* use65149 = (void*)&foo65149;
+__attribute__((used)) void* use65150 = (void*)&foo65150;
+__attribute__((used)) void* use65151 = (void*)&foo65151;
+__attribute__((used)) void* use65152 = (void*)&foo65152;
+__attribute__((used)) void* use65153 = (void*)&foo65153;
+__attribute__((used)) void* use65154 = (void*)&foo65154;
+__attribute__((used)) void* use65155 = (void*)&foo65155;
+__attribute__((used)) void* use65156 = (void*)&foo65156;
+__attribute__((used)) void* use65157 = (void*)&foo65157;
+__attribute__((used)) void* use65158 = (void*)&foo65158;
+__attribute__((used)) void* use65159 = (void*)&foo65159;
+__attribute__((used)) void* use65160 = (void*)&foo65160;
+__attribute__((used)) void* use65161 = (void*)&foo65161;
+__attribute__((used)) void* use65162 = (void*)&foo65162;
+__attribute__((used)) void* use65163 = (void*)&foo65163;
+__attribute__((used)) void* use65164 = (void*)&foo65164;
+__attribute__((used)) void* use65165 = (void*)&foo65165;
+__attribute__((used)) void* use65166 = (void*)&foo65166;
+__attribute__((used)) void* use65167 = (void*)&foo65167;
+__attribute__((used)) void* use65168 = (void*)&foo65168;
+__attribute__((used)) void* use65169 = (void*)&foo65169;
+__attribute__((used)) void* use65170 = (void*)&foo65170;
+__attribute__((used)) void* use65171 = (void*)&foo65171;
+__attribute__((used)) void* use65172 = (void*)&foo65172;
+__attribute__((used)) void* use65173 = (void*)&foo65173;
+__attribute__((used)) void* use65174 = (void*)&foo65174;
+__attribute__((used)) void* use65175 = (void*)&foo65175;
+__attribute__((used)) void* use65176 = (void*)&foo65176;
+__attribute__((used)) void* use65177 = (void*)&foo65177;
+__attribute__((used)) void* use65178 = (void*)&foo65178;
+__attribute__((used)) void* use65179 = (void*)&foo65179;
+__attribute__((used)) void* use65180 = (void*)&foo65180;
+__attribute__((used)) void* use65181 = (void*)&foo65181;
+__attribute__((used)) void* use65182 = (void*)&foo65182;
+__attribute__((used)) void* use65183 = (void*)&foo65183;
+__attribute__((used)) void* use65184 = (void*)&foo65184;
+__attribute__((used)) void* use65185 = (void*)&foo65185;
+__attribute__((used)) void* use65186 = (void*)&foo65186;
+__attribute__((used)) void* use65187 = (void*)&foo65187;
+__attribute__((used)) void* use65188 = (void*)&foo65188;
+__attribute__((used)) void* use65189 = (void*)&foo65189;
+__attribute__((used)) void* use65190 = (void*)&foo65190;
+__attribute__((used)) void* use65191 = (void*)&foo65191;
+__attribute__((used)) void* use65192 = (void*)&foo65192;
+__attribute__((used)) void* use65193 = (void*)&foo65193;
+__attribute__((used)) void* use65194 = (void*)&foo65194;
+__attribute__((used)) void* use65195 = (void*)&foo65195;
+__attribute__((used)) void* use65196 = (void*)&foo65196;
+__attribute__((used)) void* use65197 = (void*)&foo65197;
+__attribute__((used)) void* use65198 = (void*)&foo65198;
+__attribute__((used)) void* use65199 = (void*)&foo65199;
+__attribute__((used)) void* use65200 = (void*)&foo65200;
+__attribute__((used)) void* use65201 = (void*)&foo65201;
+__attribute__((used)) void* use65202 = (void*)&foo65202;
+__attribute__((used)) void* use65203 = (void*)&foo65203;
+__attribute__((used)) void* use65204 = (void*)&foo65204;
+__attribute__((used)) void* use65205 = (void*)&foo65205;
+__attribute__((used)) void* use65206 = (void*)&foo65206;
+__attribute__((used)) void* use65207 = (void*)&foo65207;
+__attribute__((used)) void* use65208 = (void*)&foo65208;
+__attribute__((used)) void* use65209 = (void*)&foo65209;
+__attribute__((used)) void* use65210 = (void*)&foo65210;
+__attribute__((used)) void* use65211 = (void*)&foo65211;
+__attribute__((used)) void* use65212 = (void*)&foo65212;
+__attribute__((used)) void* use65213 = (void*)&foo65213;
+__attribute__((used)) void* use65214 = (void*)&foo65214;
+__attribute__((used)) void* use65215 = (void*)&foo65215;
+__attribute__((used)) void* use65216 = (void*)&foo65216;
+__attribute__((used)) void* use65217 = (void*)&foo65217;
+__attribute__((used)) void* use65218 = (void*)&foo65218;
+__attribute__((used)) void* use65219 = (void*)&foo65219;
+__attribute__((used)) void* use65220 = (void*)&foo65220;
+__attribute__((used)) void* use65221 = (void*)&foo65221;
+__attribute__((used)) void* use65222 = (void*)&foo65222;
+__attribute__((used)) void* use65223 = (void*)&foo65223;
+__attribute__((used)) void* use65224 = (void*)&foo65224;
+__attribute__((used)) void* use65225 = (void*)&foo65225;
+__attribute__((used)) void* use65226 = (void*)&foo65226;
+__attribute__((used)) void* use65227 = (void*)&foo65227;
+__attribute__((used)) void* use65228 = (void*)&foo65228;
+__attribute__((used)) void* use65229 = (void*)&foo65229;
+__attribute__((used)) void* use65230 = (void*)&foo65230;
+__attribute__((used)) void* use65231 = (void*)&foo65231;
+__attribute__((used)) void* use65232 = (void*)&foo65232;
+__attribute__((used)) void* use65233 = (void*)&foo65233;
+__attribute__((used)) void* use65234 = (void*)&foo65234;
+__attribute__((used)) void* use65235 = (void*)&foo65235;
+__attribute__((used)) void* use65236 = (void*)&foo65236;
+__attribute__((used)) void* use65237 = (void*)&foo65237;
+__attribute__((used)) void* use65238 = (void*)&foo65238;
+__attribute__((used)) void* use65239 = (void*)&foo65239;
+__attribute__((used)) void* use65240 = (void*)&foo65240;
+__attribute__((used)) void* use65241 = (void*)&foo65241;
+__attribute__((used)) void* use65242 = (void*)&foo65242;
+__attribute__((used)) void* use65243 = (void*)&foo65243;
+__attribute__((used)) void* use65244 = (void*)&foo65244;
+__attribute__((used)) void* use65245 = (void*)&foo65245;
+__attribute__((used)) void* use65246 = (void*)&foo65246;
+__attribute__((used)) void* use65247 = (void*)&foo65247;
+__attribute__((used)) void* use65248 = (void*)&foo65248;
+__attribute__((used)) void* use65249 = (void*)&foo65249;
+__attribute__((used)) void* use65250 = (void*)&foo65250;
+__attribute__((used)) void* use65251 = (void*)&foo65251;
+__attribute__((used)) void* use65252 = (void*)&foo65252;
+__attribute__((used)) void* use65253 = (void*)&foo65253;
+__attribute__((used)) void* use65254 = (void*)&foo65254;
+__attribute__((used)) void* use65255 = (void*)&foo65255;
+__attribute__((used)) void* use65256 = (void*)&foo65256;
+__attribute__((used)) void* use65257 = (void*)&foo65257;
+__attribute__((used)) void* use65258 = (void*)&foo65258;
+__attribute__((used)) void* use65259 = (void*)&foo65259;
+__attribute__((used)) void* use65260 = (void*)&foo65260;
+__attribute__((used)) void* use65261 = (void*)&foo65261;
+__attribute__((used)) void* use65262 = (void*)&foo65262;
+__attribute__((used)) void* use65263 = (void*)&foo65263;
+__attribute__((used)) void* use65264 = (void*)&foo65264;
+__attribute__((used)) void* use65265 = (void*)&foo65265;
+__attribute__((used)) void* use65266 = (void*)&foo65266;
+__attribute__((used)) void* use65267 = (void*)&foo65267;
+__attribute__((used)) void* use65268 = (void*)&foo65268;
+__attribute__((used)) void* use65269 = (void*)&foo65269;
+__attribute__((used)) void* use65270 = (void*)&foo65270;
+__attribute__((used)) void* use65271 = (void*)&foo65271;
+__attribute__((used)) void* use65272 = (void*)&foo65272;
+__attribute__((used)) void* use65273 = (void*)&foo65273;
+__attribute__((used)) void* use65274 = (void*)&foo65274;
+__attribute__((used)) void* use65275 = (void*)&foo65275;
+__attribute__((used)) void* use65276 = (void*)&foo65276;
+__attribute__((used)) void* use65277 = (void*)&foo65277;
+__attribute__((used)) void* use65278 = (void*)&foo65278;
+__attribute__((used)) void* use65279 = (void*)&foo65279;
+__attribute__((used)) void* use65280 = (void*)&foo65280;
+__attribute__((used)) void* use65281 = (void*)&foo65281;
+__attribute__((used)) void* use65282 = (void*)&foo65282;
+__attribute__((used)) void* use65283 = (void*)&foo65283;
+__attribute__((used)) void* use65284 = (void*)&foo65284;
+__attribute__((used)) void* use65285 = (void*)&foo65285;
+__attribute__((used)) void* use65286 = (void*)&foo65286;
+__attribute__((used)) void* use65287 = (void*)&foo65287;
+__attribute__((used)) void* use65288 = (void*)&foo65288;
+__attribute__((used)) void* use65289 = (void*)&foo65289;
+__attribute__((used)) void* use65290 = (void*)&foo65290;
+__attribute__((used)) void* use65291 = (void*)&foo65291;
+__attribute__((used)) void* use65292 = (void*)&foo65292;
+__attribute__((used)) void* use65293 = (void*)&foo65293;
+__attribute__((used)) void* use65294 = (void*)&foo65294;
+__attribute__((used)) void* use65295 = (void*)&foo65295;
+__attribute__((used)) void* use65296 = (void*)&foo65296;
+__attribute__((used)) void* use65297 = (void*)&foo65297;
+__attribute__((used)) void* use65298 = (void*)&foo65298;
+__attribute__((used)) void* use65299 = (void*)&foo65299;
+__attribute__((used)) void* use65300 = (void*)&foo65300;
+__attribute__((used)) void* use65301 = (void*)&foo65301;
+__attribute__((used)) void* use65302 = (void*)&foo65302;
+__attribute__((used)) void* use65303 = (void*)&foo65303;
+__attribute__((used)) void* use65304 = (void*)&foo65304;
+__attribute__((used)) void* use65305 = (void*)&foo65305;
+__attribute__((used)) void* use65306 = (void*)&foo65306;
+__attribute__((used)) void* use65307 = (void*)&foo65307;
+__attribute__((used)) void* use65308 = (void*)&foo65308;
+__attribute__((used)) void* use65309 = (void*)&foo65309;
+__attribute__((used)) void* use65310 = (void*)&foo65310;
+__attribute__((used)) void* use65311 = (void*)&foo65311;
+__attribute__((used)) void* use65312 = (void*)&foo65312;
+__attribute__((used)) void* use65313 = (void*)&foo65313;
+__attribute__((used)) void* use65314 = (void*)&foo65314;
+__attribute__((used)) void* use65315 = (void*)&foo65315;
+__attribute__((used)) void* use65316 = (void*)&foo65316;
+__attribute__((used)) void* use65317 = (void*)&foo65317;
+__attribute__((used)) void* use65318 = (void*)&foo65318;
+__attribute__((used)) void* use65319 = (void*)&foo65319;
+__attribute__((used)) void* use65320 = (void*)&foo65320;
+__attribute__((used)) void* use65321 = (void*)&foo65321;
+__attribute__((used)) void* use65322 = (void*)&foo65322;
+__attribute__((used)) void* use65323 = (void*)&foo65323;
+__attribute__((used)) void* use65324 = (void*)&foo65324;
+__attribute__((used)) void* use65325 = (void*)&foo65325;
+__attribute__((used)) void* use65326 = (void*)&foo65326;
+__attribute__((used)) void* use65327 = (void*)&foo65327;
+__attribute__((used)) void* use65328 = (void*)&foo65328;
+__attribute__((used)) void* use65329 = (void*)&foo65329;
+__attribute__((used)) void* use65330 = (void*)&foo65330;
+__attribute__((used)) void* use65331 = (void*)&foo65331;
+__attribute__((used)) void* use65332 = (void*)&foo65332;
+__attribute__((used)) void* use65333 = (void*)&foo65333;
+__attribute__((used)) void* use65334 = (void*)&foo65334;
+__attribute__((used)) void* use65335 = (void*)&foo65335;
+__attribute__((used)) void* use65336 = (void*)&foo65336;
+__attribute__((used)) void* use65337 = (void*)&foo65337;
+__attribute__((used)) void* use65338 = (void*)&foo65338;
+__attribute__((used)) void* use65339 = (void*)&foo65339;
+__attribute__((used)) void* use65340 = (void*)&foo65340;
+__attribute__((used)) void* use65341 = (void*)&foo65341;
+__attribute__((used)) void* use65342 = (void*)&foo65342;
+__attribute__((used)) void* use65343 = (void*)&foo65343;
+__attribute__((used)) void* use65344 = (void*)&foo65344;
+__attribute__((used)) void* use65345 = (void*)&foo65345;
+__attribute__((used)) void* use65346 = (void*)&foo65346;
+__attribute__((used)) void* use65347 = (void*)&foo65347;
+__attribute__((used)) void* use65348 = (void*)&foo65348;
+__attribute__((used)) void* use65349 = (void*)&foo65349;
+__attribute__((used)) void* use65350 = (void*)&foo65350;
+__attribute__((used)) void* use65351 = (void*)&foo65351;
+__attribute__((used)) void* use65352 = (void*)&foo65352;
+__attribute__((used)) void* use65353 = (void*)&foo65353;
+__attribute__((used)) void* use65354 = (void*)&foo65354;
+__attribute__((used)) void* use65355 = (void*)&foo65355;
+__attribute__((used)) void* use65356 = (void*)&foo65356;
+__attribute__((used)) void* use65357 = (void*)&foo65357;
+__attribute__((used)) void* use65358 = (void*)&foo65358;
+__attribute__((used)) void* use65359 = (void*)&foo65359;
+__attribute__((used)) void* use65360 = (void*)&foo65360;
+__attribute__((used)) void* use65361 = (void*)&foo65361;
+__attribute__((used)) void* use65362 = (void*)&foo65362;
+__attribute__((used)) void* use65363 = (void*)&foo65363;
+__attribute__((used)) void* use65364 = (void*)&foo65364;
+__attribute__((used)) void* use65365 = (void*)&foo65365;
+__attribute__((used)) void* use65366 = (void*)&foo65366;
+__attribute__((used)) void* use65367 = (void*)&foo65367;
+__attribute__((used)) void* use65368 = (void*)&foo65368;
+__attribute__((used)) void* use65369 = (void*)&foo65369;
+__attribute__((used)) void* use65370 = (void*)&foo65370;
+__attribute__((used)) void* use65371 = (void*)&foo65371;
+__attribute__((used)) void* use65372 = (void*)&foo65372;
+__attribute__((used)) void* use65373 = (void*)&foo65373;
+__attribute__((used)) void* use65374 = (void*)&foo65374;
+__attribute__((used)) void* use65375 = (void*)&foo65375;
+__attribute__((used)) void* use65376 = (void*)&foo65376;
+__attribute__((used)) void* use65377 = (void*)&foo65377;
+__attribute__((used)) void* use65378 = (void*)&foo65378;
+__attribute__((used)) void* use65379 = (void*)&foo65379;
+__attribute__((used)) void* use65380 = (void*)&foo65380;
+__attribute__((used)) void* use65381 = (void*)&foo65381;
+__attribute__((used)) void* use65382 = (void*)&foo65382;
+__attribute__((used)) void* use65383 = (void*)&foo65383;
+__attribute__((used)) void* use65384 = (void*)&foo65384;
+__attribute__((used)) void* use65385 = (void*)&foo65385;
+__attribute__((used)) void* use65386 = (void*)&foo65386;
+__attribute__((used)) void* use65387 = (void*)&foo65387;
+__attribute__((used)) void* use65388 = (void*)&foo65388;
+__attribute__((used)) void* use65389 = (void*)&foo65389;
+__attribute__((used)) void* use65390 = (void*)&foo65390;
+__attribute__((used)) void* use65391 = (void*)&foo65391;
+__attribute__((used)) void* use65392 = (void*)&foo65392;
+__attribute__((used)) void* use65393 = (void*)&foo65393;
+__attribute__((used)) void* use65394 = (void*)&foo65394;
+__attribute__((used)) void* use65395 = (void*)&foo65395;
+__attribute__((used)) void* use65396 = (void*)&foo65396;
+__attribute__((used)) void* use65397 = (void*)&foo65397;
+__attribute__((used)) void* use65398 = (void*)&foo65398;
+__attribute__((used)) void* use65399 = (void*)&foo65399;
+__attribute__((used)) void* use65400 = (void*)&foo65400;
+__attribute__((used)) void* use65401 = (void*)&foo65401;
+__attribute__((used)) void* use65402 = (void*)&foo65402;
+__attribute__((used)) void* use65403 = (void*)&foo65403;
+__attribute__((used)) void* use65404 = (void*)&foo65404;
+__attribute__((used)) void* use65405 = (void*)&foo65405;
+__attribute__((used)) void* use65406 = (void*)&foo65406;
+__attribute__((used)) void* use65407 = (void*)&foo65407;
+__attribute__((used)) void* use65408 = (void*)&foo65408;
+__attribute__((used)) void* use65409 = (void*)&foo65409;
+__attribute__((used)) void* use65410 = (void*)&foo65410;
+__attribute__((used)) void* use65411 = (void*)&foo65411;
+__attribute__((used)) void* use65412 = (void*)&foo65412;
+__attribute__((used)) void* use65413 = (void*)&foo65413;
+__attribute__((used)) void* use65414 = (void*)&foo65414;
+__attribute__((used)) void* use65415 = (void*)&foo65415;
+__attribute__((used)) void* use65416 = (void*)&foo65416;
+__attribute__((used)) void* use65417 = (void*)&foo65417;
+__attribute__((used)) void* use65418 = (void*)&foo65418;
+__attribute__((used)) void* use65419 = (void*)&foo65419;
+__attribute__((used)) void* use65420 = (void*)&foo65420;
+__attribute__((used)) void* use65421 = (void*)&foo65421;
+__attribute__((used)) void* use65422 = (void*)&foo65422;
+__attribute__((used)) void* use65423 = (void*)&foo65423;
+__attribute__((used)) void* use65424 = (void*)&foo65424;
+__attribute__((used)) void* use65425 = (void*)&foo65425;
+__attribute__((used)) void* use65426 = (void*)&foo65426;
+__attribute__((used)) void* use65427 = (void*)&foo65427;
+__attribute__((used)) void* use65428 = (void*)&foo65428;
+__attribute__((used)) void* use65429 = (void*)&foo65429;
+__attribute__((used)) void* use65430 = (void*)&foo65430;
+__attribute__((used)) void* use65431 = (void*)&foo65431;
+__attribute__((used)) void* use65432 = (void*)&foo65432;
+__attribute__((used)) void* use65433 = (void*)&foo65433;
+__attribute__((used)) void* use65434 = (void*)&foo65434;
+__attribute__((used)) void* use65435 = (void*)&foo65435;
+__attribute__((used)) void* use65436 = (void*)&foo65436;
+__attribute__((used)) void* use65437 = (void*)&foo65437;
+__attribute__((used)) void* use65438 = (void*)&foo65438;
+__attribute__((used)) void* use65439 = (void*)&foo65439;
+__attribute__((used)) void* use65440 = (void*)&foo65440;
+__attribute__((used)) void* use65441 = (void*)&foo65441;
+__attribute__((used)) void* use65442 = (void*)&foo65442;
+__attribute__((used)) void* use65443 = (void*)&foo65443;
+__attribute__((used)) void* use65444 = (void*)&foo65444;
+__attribute__((used)) void* use65445 = (void*)&foo65445;
+__attribute__((used)) void* use65446 = (void*)&foo65446;
+__attribute__((used)) void* use65447 = (void*)&foo65447;
+__attribute__((used)) void* use65448 = (void*)&foo65448;
+__attribute__((used)) void* use65449 = (void*)&foo65449;
+__attribute__((used)) void* use65450 = (void*)&foo65450;
+__attribute__((used)) void* use65451 = (void*)&foo65451;
+__attribute__((used)) void* use65452 = (void*)&foo65452;
+__attribute__((used)) void* use65453 = (void*)&foo65453;
+__attribute__((used)) void* use65454 = (void*)&foo65454;
+__attribute__((used)) void* use65455 = (void*)&foo65455;
+__attribute__((used)) void* use65456 = (void*)&foo65456;
+__attribute__((used)) void* use65457 = (void*)&foo65457;
+__attribute__((used)) void* use65458 = (void*)&foo65458;
+__attribute__((used)) void* use65459 = (void*)&foo65459;
+__attribute__((used)) void* use65460 = (void*)&foo65460;
+__attribute__((used)) void* use65461 = (void*)&foo65461;
+__attribute__((used)) void* use65462 = (void*)&foo65462;
+__attribute__((used)) void* use65463 = (void*)&foo65463;
+__attribute__((used)) void* use65464 = (void*)&foo65464;
+__attribute__((used)) void* use65465 = (void*)&foo65465;
+__attribute__((used)) void* use65466 = (void*)&foo65466;
+__attribute__((used)) void* use65467 = (void*)&foo65467;
+__attribute__((used)) void* use65468 = (void*)&foo65468;
+__attribute__((used)) void* use65469 = (void*)&foo65469;
+__attribute__((used)) void* use65470 = (void*)&foo65470;
+__attribute__((used)) void* use65471 = (void*)&foo65471;
+__attribute__((used)) void* use65472 = (void*)&foo65472;
+__attribute__((used)) void* use65473 = (void*)&foo65473;
+__attribute__((used)) void* use65474 = (void*)&foo65474;
+__attribute__((used)) void* use65475 = (void*)&foo65475;
+__attribute__((used)) void* use65476 = (void*)&foo65476;
+__attribute__((used)) void* use65477 = (void*)&foo65477;
+__attribute__((used)) void* use65478 = (void*)&foo65478;
+__attribute__((used)) void* use65479 = (void*)&foo65479;
+__attribute__((used)) void* use65480 = (void*)&foo65480;
+__attribute__((used)) void* use65481 = (void*)&foo65481;
+__attribute__((used)) void* use65482 = (void*)&foo65482;
+__attribute__((used)) void* use65483 = (void*)&foo65483;
+__attribute__((used)) void* use65484 = (void*)&foo65484;
+__attribute__((used)) void* use65485 = (void*)&foo65485;
+__attribute__((used)) void* use65486 = (void*)&foo65486;
+__attribute__((used)) void* use65487 = (void*)&foo65487;
+__attribute__((used)) void* use65488 = (void*)&foo65488;
+__attribute__((used)) void* use65489 = (void*)&foo65489;
+__attribute__((used)) void* use65490 = (void*)&foo65490;
+__attribute__((used)) void* use65491 = (void*)&foo65491;
+__attribute__((used)) void* use65492 = (void*)&foo65492;
+__attribute__((used)) void* use65493 = (void*)&foo65493;
+__attribute__((used)) void* use65494 = (void*)&foo65494;
+__attribute__((used)) void* use65495 = (void*)&foo65495;
+__attribute__((used)) void* use65496 = (void*)&foo65496;
+__attribute__((used)) void* use65497 = (void*)&foo65497;
+__attribute__((used)) void* use65498 = (void*)&foo65498;
+__attribute__((used)) void* use65499 = (void*)&foo65499;
+__attribute__((used)) void* use65500 = (void*)&foo65500;
+__attribute__((used)) void* use65501 = (void*)&foo65501;
+__attribute__((used)) void* use65502 = (void*)&foo65502;
+__attribute__((used)) void* use65503 = (void*)&foo65503;
+__attribute__((used)) void* use65504 = (void*)&foo65504;
+__attribute__((used)) void* use65505 = (void*)&foo65505;
+__attribute__((used)) void* use65506 = (void*)&foo65506;
+__attribute__((used)) void* use65507 = (void*)&foo65507;
+__attribute__((used)) void* use65508 = (void*)&foo65508;
+__attribute__((used)) void* use65509 = (void*)&foo65509;
+__attribute__((used)) void* use65510 = (void*)&foo65510;
+__attribute__((used)) void* use65511 = (void*)&foo65511;
+__attribute__((used)) void* use65512 = (void*)&foo65512;
+__attribute__((used)) void* use65513 = (void*)&foo65513;
+__attribute__((used)) void* use65514 = (void*)&foo65514;
+__attribute__((used)) void* use65515 = (void*)&foo65515;
+__attribute__((used)) void* use65516 = (void*)&foo65516;
+__attribute__((used)) void* use65517 = (void*)&foo65517;
+__attribute__((used)) void* use65518 = (void*)&foo65518;
+__attribute__((used)) void* use65519 = (void*)&foo65519;
+__attribute__((used)) void* use65520 = (void*)&foo65520;
+__attribute__((used)) void* use65521 = (void*)&foo65521;
+__attribute__((used)) void* use65522 = (void*)&foo65522;
+__attribute__((used)) void* use65523 = (void*)&foo65523;
+__attribute__((used)) void* use65524 = (void*)&foo65524;
+__attribute__((used)) void* use65525 = (void*)&foo65525;
+__attribute__((used)) void* use65526 = (void*)&foo65526;
+__attribute__((used)) void* use65527 = (void*)&foo65527;
+__attribute__((used)) void* use65528 = (void*)&foo65528;
+__attribute__((used)) void* use65529 = (void*)&foo65529;
+__attribute__((used)) void* use65530 = (void*)&foo65530;
+__attribute__((used)) void* use65531 = (void*)&foo65531;
+__attribute__((used)) void* use65532 = (void*)&foo65532;
+__attribute__((used)) void* use65533 = (void*)&foo65533;
+__attribute__((used)) void* use65534 = (void*)&foo65534;
+__attribute__((used)) void* use65535 = (void*)&foo65535;
+__attribute__((used)) void* use65536 = (void*)&foo65536;
+__attribute__((used)) void* use65537 = (void*)&foo65537;
+__attribute__((used)) void* use65538 = (void*)&foo65538;
+__attribute__((used)) void* use65539 = (void*)&foo65539;
+__attribute__((used)) void* use65540 = (void*)&foo65540;
+__attribute__((used)) void* use65541 = (void*)&foo65541;
+__attribute__((used)) void* use65542 = (void*)&foo65542;
+__attribute__((used)) void* use65543 = (void*)&foo65543;
+__attribute__((used)) void* use65544 = (void*)&foo65544;
+__attribute__((used)) void* use65545 = (void*)&foo65545;
+__attribute__((used)) void* use65546 = (void*)&foo65546;
+__attribute__((used)) void* use65547 = (void*)&foo65547;
+__attribute__((used)) void* use65548 = (void*)&foo65548;
+__attribute__((used)) void* use65549 = (void*)&foo65549;
+__attribute__((used)) void* use65550 = (void*)&foo65550;
+__attribute__((used)) void* use65551 = (void*)&foo65551;
+__attribute__((used)) void* use65552 = (void*)&foo65552;
+__attribute__((used)) void* use65553 = (void*)&foo65553;
+__attribute__((used)) void* use65554 = (void*)&foo65554;
+__attribute__((used)) void* use65555 = (void*)&foo65555;
+__attribute__((used)) void* use65556 = (void*)&foo65556;
+__attribute__((used)) void* use65557 = (void*)&foo65557;
+__attribute__((used)) void* use65558 = (void*)&foo65558;
+__attribute__((used)) void* use65559 = (void*)&foo65559;
+__attribute__((used)) void* use65560 = (void*)&foo65560;
+__attribute__((used)) void* use65561 = (void*)&foo65561;
+__attribute__((used)) void* use65562 = (void*)&foo65562;
+__attribute__((used)) void* use65563 = (void*)&foo65563;
+__attribute__((used)) void* use65564 = (void*)&foo65564;
+__attribute__((used)) void* use65565 = (void*)&foo65565;
+__attribute__((used)) void* use65566 = (void*)&foo65566;
+__attribute__((used)) void* use65567 = (void*)&foo65567;
+__attribute__((used)) void* use65568 = (void*)&foo65568;
+__attribute__((used)) void* use65569 = (void*)&foo65569;
+__attribute__((used)) void* use65570 = (void*)&foo65570;
+__attribute__((used)) void* use65571 = (void*)&foo65571;
+__attribute__((used)) void* use65572 = (void*)&foo65572;
+__attribute__((used)) void* use65573 = (void*)&foo65573;
+__attribute__((used)) void* use65574 = (void*)&foo65574;
+__attribute__((used)) void* use65575 = (void*)&foo65575;
+__attribute__((used)) void* use65576 = (void*)&foo65576;
+__attribute__((used)) void* use65577 = (void*)&foo65577;
+__attribute__((used)) void* use65578 = (void*)&foo65578;
+__attribute__((used)) void* use65579 = (void*)&foo65579;
+__attribute__((used)) void* use65580 = (void*)&foo65580;
+__attribute__((used)) void* use65581 = (void*)&foo65581;
+__attribute__((used)) void* use65582 = (void*)&foo65582;
+__attribute__((used)) void* use65583 = (void*)&foo65583;
+__attribute__((used)) void* use65584 = (void*)&foo65584;
+__attribute__((used)) void* use65585 = (void*)&foo65585;
+__attribute__((used)) void* use65586 = (void*)&foo65586;
+__attribute__((used)) void* use65587 = (void*)&foo65587;
+__attribute__((used)) void* use65588 = (void*)&foo65588;
+__attribute__((used)) void* use65589 = (void*)&foo65589;
+__attribute__((used)) void* use65590 = (void*)&foo65590;
+__attribute__((used)) void* use65591 = (void*)&foo65591;
+__attribute__((used)) void* use65592 = (void*)&foo65592;
+__attribute__((used)) void* use65593 = (void*)&foo65593;
+__attribute__((used)) void* use65594 = (void*)&foo65594;
+__attribute__((used)) void* use65595 = (void*)&foo65595;
+__attribute__((used)) void* use65596 = (void*)&foo65596;
+__attribute__((used)) void* use65597 = (void*)&foo65597;
+__attribute__((used)) void* use65598 = (void*)&foo65598;
+__attribute__((used)) void* use65599 = (void*)&foo65599;
+__attribute__((used)) void* use65600 = (void*)&foo65600;
+__attribute__((used)) void* use65601 = (void*)&foo65601;
+__attribute__((used)) void* use65602 = (void*)&foo65602;
+__attribute__((used)) void* use65603 = (void*)&foo65603;
+__attribute__((used)) void* use65604 = (void*)&foo65604;
+__attribute__((used)) void* use65605 = (void*)&foo65605;
+__attribute__((used)) void* use65606 = (void*)&foo65606;
+__attribute__((used)) void* use65607 = (void*)&foo65607;
+__attribute__((used)) void* use65608 = (void*)&foo65608;
+__attribute__((used)) void* use65609 = (void*)&foo65609;
+__attribute__((used)) void* use65610 = (void*)&foo65610;
+__attribute__((used)) void* use65611 = (void*)&foo65611;
+__attribute__((used)) void* use65612 = (void*)&foo65612;
+__attribute__((used)) void* use65613 = (void*)&foo65613;
+__attribute__((used)) void* use65614 = (void*)&foo65614;
+__attribute__((used)) void* use65615 = (void*)&foo65615;
+__attribute__((used)) void* use65616 = (void*)&foo65616;
+__attribute__((used)) void* use65617 = (void*)&foo65617;
+__attribute__((used)) void* use65618 = (void*)&foo65618;
+__attribute__((used)) void* use65619 = (void*)&foo65619;
+__attribute__((used)) void* use65620 = (void*)&foo65620;
+__attribute__((used)) void* use65621 = (void*)&foo65621;
+__attribute__((used)) void* use65622 = (void*)&foo65622;
+__attribute__((used)) void* use65623 = (void*)&foo65623;
+__attribute__((used)) void* use65624 = (void*)&foo65624;
+__attribute__((used)) void* use65625 = (void*)&foo65625;
+__attribute__((used)) void* use65626 = (void*)&foo65626;
+__attribute__((used)) void* use65627 = (void*)&foo65627;
+__attribute__((used)) void* use65628 = (void*)&foo65628;
+__attribute__((used)) void* use65629 = (void*)&foo65629;
+__attribute__((used)) void* use65630 = (void*)&foo65630;
+__attribute__((used)) void* use65631 = (void*)&foo65631;
+__attribute__((used)) void* use65632 = (void*)&foo65632;
+__attribute__((used)) void* use65633 = (void*)&foo65633;
+__attribute__((used)) void* use65634 = (void*)&foo65634;
+__attribute__((used)) void* use65635 = (void*)&foo65635;
+__attribute__((used)) void* use65636 = (void*)&foo65636;
+__attribute__((used)) void* use65637 = (void*)&foo65637;
+__attribute__((used)) void* use65638 = (void*)&foo65638;
+__attribute__((used)) void* use65639 = (void*)&foo65639;
+__attribute__((used)) void* use65640 = (void*)&foo65640;
+__attribute__((used)) void* use65641 = (void*)&foo65641;
+__attribute__((used)) void* use65642 = (void*)&foo65642;
+__attribute__((used)) void* use65643 = (void*)&foo65643;
+__attribute__((used)) void* use65644 = (void*)&foo65644;
+__attribute__((used)) void* use65645 = (void*)&foo65645;
+__attribute__((used)) void* use65646 = (void*)&foo65646;
+__attribute__((used)) void* use65647 = (void*)&foo65647;
+__attribute__((used)) void* use65648 = (void*)&foo65648;
+__attribute__((used)) void* use65649 = (void*)&foo65649;
+__attribute__((used)) void* use65650 = (void*)&foo65650;
+__attribute__((used)) void* use65651 = (void*)&foo65651;
+__attribute__((used)) void* use65652 = (void*)&foo65652;
+__attribute__((used)) void* use65653 = (void*)&foo65653;
+__attribute__((used)) void* use65654 = (void*)&foo65654;
+__attribute__((used)) void* use65655 = (void*)&foo65655;
+__attribute__((used)) void* use65656 = (void*)&foo65656;
+__attribute__((used)) void* use65657 = (void*)&foo65657;
+__attribute__((used)) void* use65658 = (void*)&foo65658;
+__attribute__((used)) void* use65659 = (void*)&foo65659;
+__attribute__((used)) void* use65660 = (void*)&foo65660;
+__attribute__((used)) void* use65661 = (void*)&foo65661;
+__attribute__((used)) void* use65662 = (void*)&foo65662;
+__attribute__((used)) void* use65663 = (void*)&foo65663;
+__attribute__((used)) void* use65664 = (void*)&foo65664;
+__attribute__((used)) void* use65665 = (void*)&foo65665;
+__attribute__((used)) void* use65666 = (void*)&foo65666;
+__attribute__((used)) void* use65667 = (void*)&foo65667;
+__attribute__((used)) void* use65668 = (void*)&foo65668;
+__attribute__((used)) void* use65669 = (void*)&foo65669;
+__attribute__((used)) void* use65670 = (void*)&foo65670;
+__attribute__((used)) void* use65671 = (void*)&foo65671;
+__attribute__((used)) void* use65672 = (void*)&foo65672;
+__attribute__((used)) void* use65673 = (void*)&foo65673;
+__attribute__((used)) void* use65674 = (void*)&foo65674;
+__attribute__((used)) void* use65675 = (void*)&foo65675;
+__attribute__((used)) void* use65676 = (void*)&foo65676;
+__attribute__((used)) void* use65677 = (void*)&foo65677;
+__attribute__((used)) void* use65678 = (void*)&foo65678;
+__attribute__((used)) void* use65679 = (void*)&foo65679;
+__attribute__((used)) void* use65680 = (void*)&foo65680;
+__attribute__((used)) void* use65681 = (void*)&foo65681;
+__attribute__((used)) void* use65682 = (void*)&foo65682;
+__attribute__((used)) void* use65683 = (void*)&foo65683;
+__attribute__((used)) void* use65684 = (void*)&foo65684;
+__attribute__((used)) void* use65685 = (void*)&foo65685;
+__attribute__((used)) void* use65686 = (void*)&foo65686;
+__attribute__((used)) void* use65687 = (void*)&foo65687;
+__attribute__((used)) void* use65688 = (void*)&foo65688;
+__attribute__((used)) void* use65689 = (void*)&foo65689;
+__attribute__((used)) void* use65690 = (void*)&foo65690;
+__attribute__((used)) void* use65691 = (void*)&foo65691;
+__attribute__((used)) void* use65692 = (void*)&foo65692;
+__attribute__((used)) void* use65693 = (void*)&foo65693;
+__attribute__((used)) void* use65694 = (void*)&foo65694;
+__attribute__((used)) void* use65695 = (void*)&foo65695;
+__attribute__((used)) void* use65696 = (void*)&foo65696;
+__attribute__((used)) void* use65697 = (void*)&foo65697;
+__attribute__((used)) void* use65698 = (void*)&foo65698;
+__attribute__((used)) void* use65699 = (void*)&foo65699;
+__attribute__((used)) void* use65700 = (void*)&foo65700;
+__attribute__((used)) void* use65701 = (void*)&foo65701;
+__attribute__((used)) void* use65702 = (void*)&foo65702;
+__attribute__((used)) void* use65703 = (void*)&foo65703;
+__attribute__((used)) void* use65704 = (void*)&foo65704;
+__attribute__((used)) void* use65705 = (void*)&foo65705;
+__attribute__((used)) void* use65706 = (void*)&foo65706;
+__attribute__((used)) void* use65707 = (void*)&foo65707;
+__attribute__((used)) void* use65708 = (void*)&foo65708;
+__attribute__((used)) void* use65709 = (void*)&foo65709;
+__attribute__((used)) void* use65710 = (void*)&foo65710;
+__attribute__((used)) void* use65711 = (void*)&foo65711;
+__attribute__((used)) void* use65712 = (void*)&foo65712;
+__attribute__((used)) void* use65713 = (void*)&foo65713;
+__attribute__((used)) void* use65714 = (void*)&foo65714;
+__attribute__((used)) void* use65715 = (void*)&foo65715;
+__attribute__((used)) void* use65716 = (void*)&foo65716;
+__attribute__((used)) void* use65717 = (void*)&foo65717;
+__attribute__((used)) void* use65718 = (void*)&foo65718;
+__attribute__((used)) void* use65719 = (void*)&foo65719;
+__attribute__((used)) void* use65720 = (void*)&foo65720;
+__attribute__((used)) void* use65721 = (void*)&foo65721;
+__attribute__((used)) void* use65722 = (void*)&foo65722;
+__attribute__((used)) void* use65723 = (void*)&foo65723;
+__attribute__((used)) void* use65724 = (void*)&foo65724;
+__attribute__((used)) void* use65725 = (void*)&foo65725;
+__attribute__((used)) void* use65726 = (void*)&foo65726;
+__attribute__((used)) void* use65727 = (void*)&foo65727;
+__attribute__((used)) void* use65728 = (void*)&foo65728;
+__attribute__((used)) void* use65729 = (void*)&foo65729;
+__attribute__((used)) void* use65730 = (void*)&foo65730;
+__attribute__((used)) void* use65731 = (void*)&foo65731;
+__attribute__((used)) void* use65732 = (void*)&foo65732;
+__attribute__((used)) void* use65733 = (void*)&foo65733;
+__attribute__((used)) void* use65734 = (void*)&foo65734;
+__attribute__((used)) void* use65735 = (void*)&foo65735;
+__attribute__((used)) void* use65736 = (void*)&foo65736;
+__attribute__((used)) void* use65737 = (void*)&foo65737;
+__attribute__((used)) void* use65738 = (void*)&foo65738;
+__attribute__((used)) void* use65739 = (void*)&foo65739;
+__attribute__((used)) void* use65740 = (void*)&foo65740;
+__attribute__((used)) void* use65741 = (void*)&foo65741;
+__attribute__((used)) void* use65742 = (void*)&foo65742;
+__attribute__((used)) void* use65743 = (void*)&foo65743;
+__attribute__((used)) void* use65744 = (void*)&foo65744;
+__attribute__((used)) void* use65745 = (void*)&foo65745;
+__attribute__((used)) void* use65746 = (void*)&foo65746;
+__attribute__((used)) void* use65747 = (void*)&foo65747;
+__attribute__((used)) void* use65748 = (void*)&foo65748;
+__attribute__((used)) void* use65749 = (void*)&foo65749;
+__attribute__((used)) void* use65750 = (void*)&foo65750;
+__attribute__((used)) void* use65751 = (void*)&foo65751;
+__attribute__((used)) void* use65752 = (void*)&foo65752;
+__attribute__((used)) void* use65753 = (void*)&foo65753;
+__attribute__((used)) void* use65754 = (void*)&foo65754;
+__attribute__((used)) void* use65755 = (void*)&foo65755;
+__attribute__((used)) void* use65756 = (void*)&foo65756;
+__attribute__((used)) void* use65757 = (void*)&foo65757;
+__attribute__((used)) void* use65758 = (void*)&foo65758;
+__attribute__((used)) void* use65759 = (void*)&foo65759;
+__attribute__((used)) void* use65760 = (void*)&foo65760;
+__attribute__((used)) void* use65761 = (void*)&foo65761;
+__attribute__((used)) void* use65762 = (void*)&foo65762;
+__attribute__((used)) void* use65763 = (void*)&foo65763;
+__attribute__((used)) void* use65764 = (void*)&foo65764;
+__attribute__((used)) void* use65765 = (void*)&foo65765;
+__attribute__((used)) void* use65766 = (void*)&foo65766;
+__attribute__((used)) void* use65767 = (void*)&foo65767;
+__attribute__((used)) void* use65768 = (void*)&foo65768;
+__attribute__((used)) void* use65769 = (void*)&foo65769;
+__attribute__((used)) void* use65770 = (void*)&foo65770;
+__attribute__((used)) void* use65771 = (void*)&foo65771;
+__attribute__((used)) void* use65772 = (void*)&foo65772;
+__attribute__((used)) void* use65773 = (void*)&foo65773;
+__attribute__((used)) void* use65774 = (void*)&foo65774;
+__attribute__((used)) void* use65775 = (void*)&foo65775;
+__attribute__((used)) void* use65776 = (void*)&foo65776;
+__attribute__((used)) void* use65777 = (void*)&foo65777;
+__attribute__((used)) void* use65778 = (void*)&foo65778;
+__attribute__((used)) void* use65779 = (void*)&foo65779;
+__attribute__((used)) void* use65780 = (void*)&foo65780;
+__attribute__((used)) void* use65781 = (void*)&foo65781;
+__attribute__((used)) void* use65782 = (void*)&foo65782;
+__attribute__((used)) void* use65783 = (void*)&foo65783;
+__attribute__((used)) void* use65784 = (void*)&foo65784;
+__attribute__((used)) void* use65785 = (void*)&foo65785;
+__attribute__((used)) void* use65786 = (void*)&foo65786;
+__attribute__((used)) void* use65787 = (void*)&foo65787;
+__attribute__((used)) void* use65788 = (void*)&foo65788;
+__attribute__((used)) void* use65789 = (void*)&foo65789;
+__attribute__((used)) void* use65790 = (void*)&foo65790;
+__attribute__((used)) void* use65791 = (void*)&foo65791;
+__attribute__((used)) void* use65792 = (void*)&foo65792;
+__attribute__((used)) void* use65793 = (void*)&foo65793;
+__attribute__((used)) void* use65794 = (void*)&foo65794;
+__attribute__((used)) void* use65795 = (void*)&foo65795;
+__attribute__((used)) void* use65796 = (void*)&foo65796;
+__attribute__((used)) void* use65797 = (void*)&foo65797;
+__attribute__((used)) void* use65798 = (void*)&foo65798;
+__attribute__((used)) void* use65799 = (void*)&foo65799;
+__attribute__((used)) void* use65800 = (void*)&foo65800;
+__attribute__((used)) void* use65801 = (void*)&foo65801;
+__attribute__((used)) void* use65802 = (void*)&foo65802;
+__attribute__((used)) void* use65803 = (void*)&foo65803;
+__attribute__((used)) void* use65804 = (void*)&foo65804;
+__attribute__((used)) void* use65805 = (void*)&foo65805;
+__attribute__((used)) void* use65806 = (void*)&foo65806;
+__attribute__((used)) void* use65807 = (void*)&foo65807;
+__attribute__((used)) void* use65808 = (void*)&foo65808;
+__attribute__((used)) void* use65809 = (void*)&foo65809;
+__attribute__((used)) void* use65810 = (void*)&foo65810;
+__attribute__((used)) void* use65811 = (void*)&foo65811;
+__attribute__((used)) void* use65812 = (void*)&foo65812;
+__attribute__((used)) void* use65813 = (void*)&foo65813;
+__attribute__((used)) void* use65814 = (void*)&foo65814;
+__attribute__((used)) void* use65815 = (void*)&foo65815;
+__attribute__((used)) void* use65816 = (void*)&foo65816;
+__attribute__((used)) void* use65817 = (void*)&foo65817;
+__attribute__((used)) void* use65818 = (void*)&foo65818;
+__attribute__((used)) void* use65819 = (void*)&foo65819;
+__attribute__((used)) void* use65820 = (void*)&foo65820;
+__attribute__((used)) void* use65821 = (void*)&foo65821;
+__attribute__((used)) void* use65822 = (void*)&foo65822;
+__attribute__((used)) void* use65823 = (void*)&foo65823;
+__attribute__((used)) void* use65824 = (void*)&foo65824;
+__attribute__((used)) void* use65825 = (void*)&foo65825;
+__attribute__((used)) void* use65826 = (void*)&foo65826;
+__attribute__((used)) void* use65827 = (void*)&foo65827;
+__attribute__((used)) void* use65828 = (void*)&foo65828;
+__attribute__((used)) void* use65829 = (void*)&foo65829;
+__attribute__((used)) void* use65830 = (void*)&foo65830;
+__attribute__((used)) void* use65831 = (void*)&foo65831;
+__attribute__((used)) void* use65832 = (void*)&foo65832;
+__attribute__((used)) void* use65833 = (void*)&foo65833;
+__attribute__((used)) void* use65834 = (void*)&foo65834;
+__attribute__((used)) void* use65835 = (void*)&foo65835;
+__attribute__((used)) void* use65836 = (void*)&foo65836;
+__attribute__((used)) void* use65837 = (void*)&foo65837;
+__attribute__((used)) void* use65838 = (void*)&foo65838;
+__attribute__((used)) void* use65839 = (void*)&foo65839;
+__attribute__((used)) void* use65840 = (void*)&foo65840;
+__attribute__((used)) void* use65841 = (void*)&foo65841;
+__attribute__((used)) void* use65842 = (void*)&foo65842;
+__attribute__((used)) void* use65843 = (void*)&foo65843;
+__attribute__((used)) void* use65844 = (void*)&foo65844;
+__attribute__((used)) void* use65845 = (void*)&foo65845;
+__attribute__((used)) void* use65846 = (void*)&foo65846;
+__attribute__((used)) void* use65847 = (void*)&foo65847;
+__attribute__((used)) void* use65848 = (void*)&foo65848;
+__attribute__((used)) void* use65849 = (void*)&foo65849;
+__attribute__((used)) void* use65850 = (void*)&foo65850;
+__attribute__((used)) void* use65851 = (void*)&foo65851;
+__attribute__((used)) void* use65852 = (void*)&foo65852;
+__attribute__((used)) void* use65853 = (void*)&foo65853;
+__attribute__((used)) void* use65854 = (void*)&foo65854;
+__attribute__((used)) void* use65855 = (void*)&foo65855;
+__attribute__((used)) void* use65856 = (void*)&foo65856;
+__attribute__((used)) void* use65857 = (void*)&foo65857;
+__attribute__((used)) void* use65858 = (void*)&foo65858;
+__attribute__((used)) void* use65859 = (void*)&foo65859;
+__attribute__((used)) void* use65860 = (void*)&foo65860;
+__attribute__((used)) void* use65861 = (void*)&foo65861;
+__attribute__((used)) void* use65862 = (void*)&foo65862;
+__attribute__((used)) void* use65863 = (void*)&foo65863;
+__attribute__((used)) void* use65864 = (void*)&foo65864;
+__attribute__((used)) void* use65865 = (void*)&foo65865;
+__attribute__((used)) void* use65866 = (void*)&foo65866;
+__attribute__((used)) void* use65867 = (void*)&foo65867;
+__attribute__((used)) void* use65868 = (void*)&foo65868;
+__attribute__((used)) void* use65869 = (void*)&foo65869;
+__attribute__((used)) void* use65870 = (void*)&foo65870;
+__attribute__((used)) void* use65871 = (void*)&foo65871;
+__attribute__((used)) void* use65872 = (void*)&foo65872;
+__attribute__((used)) void* use65873 = (void*)&foo65873;
+__attribute__((used)) void* use65874 = (void*)&foo65874;
+__attribute__((used)) void* use65875 = (void*)&foo65875;
+__attribute__((used)) void* use65876 = (void*)&foo65876;
+__attribute__((used)) void* use65877 = (void*)&foo65877;
+__attribute__((used)) void* use65878 = (void*)&foo65878;
+__attribute__((used)) void* use65879 = (void*)&foo65879;
+__attribute__((used)) void* use65880 = (void*)&foo65880;
+__attribute__((used)) void* use65881 = (void*)&foo65881;
+__attribute__((used)) void* use65882 = (void*)&foo65882;
+__attribute__((used)) void* use65883 = (void*)&foo65883;
+__attribute__((used)) void* use65884 = (void*)&foo65884;
+__attribute__((used)) void* use65885 = (void*)&foo65885;
+__attribute__((used)) void* use65886 = (void*)&foo65886;
+__attribute__((used)) void* use65887 = (void*)&foo65887;
+__attribute__((used)) void* use65888 = (void*)&foo65888;
+__attribute__((used)) void* use65889 = (void*)&foo65889;
+__attribute__((used)) void* use65890 = (void*)&foo65890;
+__attribute__((used)) void* use65891 = (void*)&foo65891;
+__attribute__((used)) void* use65892 = (void*)&foo65892;
+__attribute__((used)) void* use65893 = (void*)&foo65893;
+__attribute__((used)) void* use65894 = (void*)&foo65894;
+__attribute__((used)) void* use65895 = (void*)&foo65895;
+__attribute__((used)) void* use65896 = (void*)&foo65896;
+__attribute__((used)) void* use65897 = (void*)&foo65897;
+__attribute__((used)) void* use65898 = (void*)&foo65898;
+__attribute__((used)) void* use65899 = (void*)&foo65899;
+__attribute__((used)) void* use65900 = (void*)&foo65900;
+__attribute__((used)) void* use65901 = (void*)&foo65901;
+__attribute__((used)) void* use65902 = (void*)&foo65902;
+__attribute__((used)) void* use65903 = (void*)&foo65903;
+__attribute__((used)) void* use65904 = (void*)&foo65904;
+__attribute__((used)) void* use65905 = (void*)&foo65905;
+__attribute__((used)) void* use65906 = (void*)&foo65906;
+__attribute__((used)) void* use65907 = (void*)&foo65907;
+__attribute__((used)) void* use65908 = (void*)&foo65908;
+__attribute__((used)) void* use65909 = (void*)&foo65909;
+__attribute__((used)) void* use65910 = (void*)&foo65910;
+__attribute__((used)) void* use65911 = (void*)&foo65911;
+__attribute__((used)) void* use65912 = (void*)&foo65912;
+__attribute__((used)) void* use65913 = (void*)&foo65913;
+__attribute__((used)) void* use65914 = (void*)&foo65914;
+__attribute__((used)) void* use65915 = (void*)&foo65915;
+__attribute__((used)) void* use65916 = (void*)&foo65916;
+__attribute__((used)) void* use65917 = (void*)&foo65917;
+__attribute__((used)) void* use65918 = (void*)&foo65918;
+__attribute__((used)) void* use65919 = (void*)&foo65919;
+__attribute__((used)) void* use65920 = (void*)&foo65920;
+__attribute__((used)) void* use65921 = (void*)&foo65921;
+__attribute__((used)) void* use65922 = (void*)&foo65922;
+__attribute__((used)) void* use65923 = (void*)&foo65923;
+__attribute__((used)) void* use65924 = (void*)&foo65924;
+__attribute__((used)) void* use65925 = (void*)&foo65925;
+__attribute__((used)) void* use65926 = (void*)&foo65926;
+__attribute__((used)) void* use65927 = (void*)&foo65927;
+__attribute__((used)) void* use65928 = (void*)&foo65928;
+__attribute__((used)) void* use65929 = (void*)&foo65929;
+__attribute__((used)) void* use65930 = (void*)&foo65930;
+__attribute__((used)) void* use65931 = (void*)&foo65931;
+__attribute__((used)) void* use65932 = (void*)&foo65932;
+__attribute__((used)) void* use65933 = (void*)&foo65933;
+__attribute__((used)) void* use65934 = (void*)&foo65934;
+__attribute__((used)) void* use65935 = (void*)&foo65935;
+__attribute__((used)) void* use65936 = (void*)&foo65936;
+__attribute__((used)) void* use65937 = (void*)&foo65937;
+__attribute__((used)) void* use65938 = (void*)&foo65938;
+__attribute__((used)) void* use65939 = (void*)&foo65939;
+__attribute__((used)) void* use65940 = (void*)&foo65940;
+__attribute__((used)) void* use65941 = (void*)&foo65941;
+__attribute__((used)) void* use65942 = (void*)&foo65942;
+__attribute__((used)) void* use65943 = (void*)&foo65943;
+__attribute__((used)) void* use65944 = (void*)&foo65944;
+__attribute__((used)) void* use65945 = (void*)&foo65945;
+__attribute__((used)) void* use65946 = (void*)&foo65946;
+__attribute__((used)) void* use65947 = (void*)&foo65947;
+__attribute__((used)) void* use65948 = (void*)&foo65948;
+__attribute__((used)) void* use65949 = (void*)&foo65949;
+__attribute__((used)) void* use65950 = (void*)&foo65950;
+__attribute__((used)) void* use65951 = (void*)&foo65951;
+__attribute__((used)) void* use65952 = (void*)&foo65952;
+__attribute__((used)) void* use65953 = (void*)&foo65953;
+__attribute__((used)) void* use65954 = (void*)&foo65954;
+__attribute__((used)) void* use65955 = (void*)&foo65955;
+__attribute__((used)) void* use65956 = (void*)&foo65956;
+__attribute__((used)) void* use65957 = (void*)&foo65957;
+__attribute__((used)) void* use65958 = (void*)&foo65958;
+__attribute__((used)) void* use65959 = (void*)&foo65959;
+__attribute__((used)) void* use65960 = (void*)&foo65960;
+__attribute__((used)) void* use65961 = (void*)&foo65961;
+__attribute__((used)) void* use65962 = (void*)&foo65962;
+__attribute__((used)) void* use65963 = (void*)&foo65963;
+__attribute__((used)) void* use65964 = (void*)&foo65964;
+__attribute__((used)) void* use65965 = (void*)&foo65965;
+__attribute__((used)) void* use65966 = (void*)&foo65966;
+__attribute__((used)) void* use65967 = (void*)&foo65967;
+__attribute__((used)) void* use65968 = (void*)&foo65968;
+__attribute__((used)) void* use65969 = (void*)&foo65969;
+__attribute__((used)) void* use65970 = (void*)&foo65970;
+__attribute__((used)) void* use65971 = (void*)&foo65971;
+__attribute__((used)) void* use65972 = (void*)&foo65972;
+__attribute__((used)) void* use65973 = (void*)&foo65973;
+__attribute__((used)) void* use65974 = (void*)&foo65974;
+__attribute__((used)) void* use65975 = (void*)&foo65975;
+__attribute__((used)) void* use65976 = (void*)&foo65976;
+__attribute__((used)) void* use65977 = (void*)&foo65977;
+__attribute__((used)) void* use65978 = (void*)&foo65978;
+__attribute__((used)) void* use65979 = (void*)&foo65979;
+__attribute__((used)) void* use65980 = (void*)&foo65980;
+__attribute__((used)) void* use65981 = (void*)&foo65981;
+__attribute__((used)) void* use65982 = (void*)&foo65982;
+__attribute__((used)) void* use65983 = (void*)&foo65983;
+__attribute__((used)) void* use65984 = (void*)&foo65984;
+__attribute__((used)) void* use65985 = (void*)&foo65985;
+__attribute__((used)) void* use65986 = (void*)&foo65986;
+__attribute__((used)) void* use65987 = (void*)&foo65987;
+__attribute__((used)) void* use65988 = (void*)&foo65988;
+__attribute__((used)) void* use65989 = (void*)&foo65989;
+__attribute__((used)) void* use65990 = (void*)&foo65990;
+__attribute__((used)) void* use65991 = (void*)&foo65991;
+__attribute__((used)) void* use65992 = (void*)&foo65992;
+__attribute__((used)) void* use65993 = (void*)&foo65993;
+__attribute__((used)) void* use65994 = (void*)&foo65994;
+__attribute__((used)) void* use65995 = (void*)&foo65995;
+__attribute__((used)) void* use65996 = (void*)&foo65996;
+__attribute__((used)) void* use65997 = (void*)&foo65997;
+__attribute__((used)) void* use65998 = (void*)&foo65998;
+__attribute__((used)) void* use65999 = (void*)&foo65999;
+__attribute__((used)) void* use66000 = (void*)&foo66000;
+__attribute__((used)) void* use66001 = (void*)&foo66001;
+__attribute__((used)) void* use66002 = (void*)&foo66002;
+__attribute__((used)) void* use66003 = (void*)&foo66003;
+__attribute__((used)) void* use66004 = (void*)&foo66004;
+__attribute__((used)) void* use66005 = (void*)&foo66005;
+__attribute__((used)) void* use66006 = (void*)&foo66006;
+__attribute__((used)) void* use66007 = (void*)&foo66007;
+__attribute__((used)) void* use66008 = (void*)&foo66008;
+__attribute__((used)) void* use66009 = (void*)&foo66009;
+__attribute__((used)) void* use66010 = (void*)&foo66010;
+__attribute__((used)) void* use66011 = (void*)&foo66011;
+__attribute__((used)) void* use66012 = (void*)&foo66012;
+__attribute__((used)) void* use66013 = (void*)&foo66013;
+__attribute__((used)) void* use66014 = (void*)&foo66014;
+__attribute__((used)) void* use66015 = (void*)&foo66015;
+__attribute__((used)) void* use66016 = (void*)&foo66016;
+__attribute__((used)) void* use66017 = (void*)&foo66017;
+__attribute__((used)) void* use66018 = (void*)&foo66018;
+__attribute__((used)) void* use66019 = (void*)&foo66019;
+__attribute__((used)) void* use66020 = (void*)&foo66020;
+__attribute__((used)) void* use66021 = (void*)&foo66021;
+__attribute__((used)) void* use66022 = (void*)&foo66022;
+__attribute__((used)) void* use66023 = (void*)&foo66023;
+__attribute__((used)) void* use66024 = (void*)&foo66024;
+__attribute__((used)) void* use66025 = (void*)&foo66025;
+__attribute__((used)) void* use66026 = (void*)&foo66026;
+__attribute__((used)) void* use66027 = (void*)&foo66027;
+__attribute__((used)) void* use66028 = (void*)&foo66028;
+__attribute__((used)) void* use66029 = (void*)&foo66029;
+__attribute__((used)) void* use66030 = (void*)&foo66030;
+__attribute__((used)) void* use66031 = (void*)&foo66031;
+__attribute__((used)) void* use66032 = (void*)&foo66032;
+__attribute__((used)) void* use66033 = (void*)&foo66033;
+__attribute__((used)) void* use66034 = (void*)&foo66034;
+__attribute__((used)) void* use66035 = (void*)&foo66035;
+__attribute__((used)) void* use66036 = (void*)&foo66036;
+__attribute__((used)) void* use66037 = (void*)&foo66037;
+__attribute__((used)) void* use66038 = (void*)&foo66038;
+__attribute__((used)) void* use66039 = (void*)&foo66039;
+__attribute__((used)) void* use66040 = (void*)&foo66040;
+__attribute__((used)) void* use66041 = (void*)&foo66041;
+__attribute__((used)) void* use66042 = (void*)&foo66042;
+__attribute__((used)) void* use66043 = (void*)&foo66043;
+__attribute__((used)) void* use66044 = (void*)&foo66044;
+__attribute__((used)) void* use66045 = (void*)&foo66045;
+__attribute__((used)) void* use66046 = (void*)&foo66046;
+__attribute__((used)) void* use66047 = (void*)&foo66047;
+__attribute__((used)) void* use66048 = (void*)&foo66048;
+__attribute__((used)) void* use66049 = (void*)&foo66049;
+__attribute__((used)) void* use66050 = (void*)&foo66050;
+__attribute__((used)) void* use66051 = (void*)&foo66051;
+__attribute__((used)) void* use66052 = (void*)&foo66052;
+__attribute__((used)) void* use66053 = (void*)&foo66053;
+__attribute__((used)) void* use66054 = (void*)&foo66054;
+__attribute__((used)) void* use66055 = (void*)&foo66055;
+__attribute__((used)) void* use66056 = (void*)&foo66056;
+__attribute__((used)) void* use66057 = (void*)&foo66057;
+__attribute__((used)) void* use66058 = (void*)&foo66058;
+__attribute__((used)) void* use66059 = (void*)&foo66059;
+__attribute__((used)) void* use66060 = (void*)&foo66060;
+__attribute__((used)) void* use66061 = (void*)&foo66061;
+__attribute__((used)) void* use66062 = (void*)&foo66062;
+__attribute__((used)) void* use66063 = (void*)&foo66063;
+__attribute__((used)) void* use66064 = (void*)&foo66064;
+__attribute__((used)) void* use66065 = (void*)&foo66065;
+__attribute__((used)) void* use66066 = (void*)&foo66066;
+__attribute__((used)) void* use66067 = (void*)&foo66067;
+__attribute__((used)) void* use66068 = (void*)&foo66068;
+__attribute__((used)) void* use66069 = (void*)&foo66069;
+__attribute__((used)) void* use66070 = (void*)&foo66070;
+__attribute__((used)) void* use66071 = (void*)&foo66071;
+__attribute__((used)) void* use66072 = (void*)&foo66072;
+__attribute__((used)) void* use66073 = (void*)&foo66073;
+__attribute__((used)) void* use66074 = (void*)&foo66074;
+__attribute__((used)) void* use66075 = (void*)&foo66075;
+__attribute__((used)) void* use66076 = (void*)&foo66076;
+__attribute__((used)) void* use66077 = (void*)&foo66077;
+__attribute__((used)) void* use66078 = (void*)&foo66078;
+__attribute__((used)) void* use66079 = (void*)&foo66079;
+__attribute__((used)) void* use66080 = (void*)&foo66080;
+__attribute__((used)) void* use66081 = (void*)&foo66081;
+__attribute__((used)) void* use66082 = (void*)&foo66082;
+__attribute__((used)) void* use66083 = (void*)&foo66083;
+__attribute__((used)) void* use66084 = (void*)&foo66084;
+__attribute__((used)) void* use66085 = (void*)&foo66085;
+__attribute__((used)) void* use66086 = (void*)&foo66086;
+__attribute__((used)) void* use66087 = (void*)&foo66087;
+__attribute__((used)) void* use66088 = (void*)&foo66088;
+__attribute__((used)) void* use66089 = (void*)&foo66089;
+__attribute__((used)) void* use66090 = (void*)&foo66090;
+__attribute__((used)) void* use66091 = (void*)&foo66091;
+__attribute__((used)) void* use66092 = (void*)&foo66092;
+__attribute__((used)) void* use66093 = (void*)&foo66093;
+__attribute__((used)) void* use66094 = (void*)&foo66094;
+__attribute__((used)) void* use66095 = (void*)&foo66095;
+__attribute__((used)) void* use66096 = (void*)&foo66096;
+__attribute__((used)) void* use66097 = (void*)&foo66097;
+__attribute__((used)) void* use66098 = (void*)&foo66098;
+__attribute__((used)) void* use66099 = (void*)&foo66099;
+__attribute__((used)) void* use66100 = (void*)&foo66100;
+__attribute__((used)) void* use66101 = (void*)&foo66101;
+__attribute__((used)) void* use66102 = (void*)&foo66102;
+__attribute__((used)) void* use66103 = (void*)&foo66103;
+__attribute__((used)) void* use66104 = (void*)&foo66104;
+__attribute__((used)) void* use66105 = (void*)&foo66105;
+__attribute__((used)) void* use66106 = (void*)&foo66106;
+__attribute__((used)) void* use66107 = (void*)&foo66107;
+__attribute__((used)) void* use66108 = (void*)&foo66108;
+__attribute__((used)) void* use66109 = (void*)&foo66109;
+__attribute__((used)) void* use66110 = (void*)&foo66110;
+__attribute__((used)) void* use66111 = (void*)&foo66111;
+__attribute__((used)) void* use66112 = (void*)&foo66112;
+__attribute__((used)) void* use66113 = (void*)&foo66113;
+__attribute__((used)) void* use66114 = (void*)&foo66114;
+__attribute__((used)) void* use66115 = (void*)&foo66115;
+__attribute__((used)) void* use66116 = (void*)&foo66116;
+__attribute__((used)) void* use66117 = (void*)&foo66117;
+__attribute__((used)) void* use66118 = (void*)&foo66118;
+__attribute__((used)) void* use66119 = (void*)&foo66119;
+__attribute__((used)) void* use66120 = (void*)&foo66120;
+__attribute__((used)) void* use66121 = (void*)&foo66121;
+__attribute__((used)) void* use66122 = (void*)&foo66122;
+__attribute__((used)) void* use66123 = (void*)&foo66123;
+__attribute__((used)) void* use66124 = (void*)&foo66124;
+__attribute__((used)) void* use66125 = (void*)&foo66125;
+__attribute__((used)) void* use66126 = (void*)&foo66126;
+__attribute__((used)) void* use66127 = (void*)&foo66127;
+__attribute__((used)) void* use66128 = (void*)&foo66128;
+__attribute__((used)) void* use66129 = (void*)&foo66129;
+__attribute__((used)) void* use66130 = (void*)&foo66130;
+__attribute__((used)) void* use66131 = (void*)&foo66131;
+__attribute__((used)) void* use66132 = (void*)&foo66132;
+__attribute__((used)) void* use66133 = (void*)&foo66133;
+__attribute__((used)) void* use66134 = (void*)&foo66134;
+__attribute__((used)) void* use66135 = (void*)&foo66135;
+__attribute__((used)) void* use66136 = (void*)&foo66136;
+__attribute__((used)) void* use66137 = (void*)&foo66137;
+__attribute__((used)) void* use66138 = (void*)&foo66138;
+__attribute__((used)) void* use66139 = (void*)&foo66139;
+__attribute__((used)) void* use66140 = (void*)&foo66140;
+__attribute__((used)) void* use66141 = (void*)&foo66141;
+__attribute__((used)) void* use66142 = (void*)&foo66142;
+__attribute__((used)) void* use66143 = (void*)&foo66143;
+__attribute__((used)) void* use66144 = (void*)&foo66144;
+__attribute__((used)) void* use66145 = (void*)&foo66145;
+__attribute__((used)) void* use66146 = (void*)&foo66146;
+__attribute__((used)) void* use66147 = (void*)&foo66147;
+__attribute__((used)) void* use66148 = (void*)&foo66148;
+__attribute__((used)) void* use66149 = (void*)&foo66149;
+__attribute__((used)) void* use66150 = (void*)&foo66150;
+__attribute__((used)) void* use66151 = (void*)&foo66151;
+__attribute__((used)) void* use66152 = (void*)&foo66152;
+__attribute__((used)) void* use66153 = (void*)&foo66153;
+__attribute__((used)) void* use66154 = (void*)&foo66154;
+__attribute__((used)) void* use66155 = (void*)&foo66155;
+__attribute__((used)) void* use66156 = (void*)&foo66156;
+__attribute__((used)) void* use66157 = (void*)&foo66157;
+__attribute__((used)) void* use66158 = (void*)&foo66158;
+__attribute__((used)) void* use66159 = (void*)&foo66159;
+__attribute__((used)) void* use66160 = (void*)&foo66160;
+__attribute__((used)) void* use66161 = (void*)&foo66161;
+__attribute__((used)) void* use66162 = (void*)&foo66162;
+__attribute__((used)) void* use66163 = (void*)&foo66163;
+__attribute__((used)) void* use66164 = (void*)&foo66164;
+__attribute__((used)) void* use66165 = (void*)&foo66165;
+__attribute__((used)) void* use66166 = (void*)&foo66166;
+__attribute__((used)) void* use66167 = (void*)&foo66167;
+__attribute__((used)) void* use66168 = (void*)&foo66168;
+__attribute__((used)) void* use66169 = (void*)&foo66169;
+__attribute__((used)) void* use66170 = (void*)&foo66170;
+__attribute__((used)) void* use66171 = (void*)&foo66171;
+__attribute__((used)) void* use66172 = (void*)&foo66172;
+__attribute__((used)) void* use66173 = (void*)&foo66173;
+__attribute__((used)) void* use66174 = (void*)&foo66174;
+__attribute__((used)) void* use66175 = (void*)&foo66175;
+__attribute__((used)) void* use66176 = (void*)&foo66176;
+__attribute__((used)) void* use66177 = (void*)&foo66177;
+__attribute__((used)) void* use66178 = (void*)&foo66178;
+__attribute__((used)) void* use66179 = (void*)&foo66179;
+__attribute__((used)) void* use66180 = (void*)&foo66180;
+__attribute__((used)) void* use66181 = (void*)&foo66181;
+__attribute__((used)) void* use66182 = (void*)&foo66182;
+__attribute__((used)) void* use66183 = (void*)&foo66183;
+__attribute__((used)) void* use66184 = (void*)&foo66184;
+__attribute__((used)) void* use66185 = (void*)&foo66185;
+__attribute__((used)) void* use66186 = (void*)&foo66186;
+__attribute__((used)) void* use66187 = (void*)&foo66187;
+__attribute__((used)) void* use66188 = (void*)&foo66188;
+__attribute__((used)) void* use66189 = (void*)&foo66189;
+__attribute__((used)) void* use66190 = (void*)&foo66190;
+__attribute__((used)) void* use66191 = (void*)&foo66191;
+__attribute__((used)) void* use66192 = (void*)&foo66192;
+__attribute__((used)) void* use66193 = (void*)&foo66193;
+__attribute__((used)) void* use66194 = (void*)&foo66194;
+__attribute__((used)) void* use66195 = (void*)&foo66195;
+__attribute__((used)) void* use66196 = (void*)&foo66196;
+__attribute__((used)) void* use66197 = (void*)&foo66197;
+__attribute__((used)) void* use66198 = (void*)&foo66198;
+__attribute__((used)) void* use66199 = (void*)&foo66199;
+__attribute__((used)) void* use66200 = (void*)&foo66200;
+__attribute__((used)) void* use66201 = (void*)&foo66201;
+__attribute__((used)) void* use66202 = (void*)&foo66202;
+__attribute__((used)) void* use66203 = (void*)&foo66203;
+__attribute__((used)) void* use66204 = (void*)&foo66204;
+__attribute__((used)) void* use66205 = (void*)&foo66205;
+__attribute__((used)) void* use66206 = (void*)&foo66206;
+__attribute__((used)) void* use66207 = (void*)&foo66207;
+__attribute__((used)) void* use66208 = (void*)&foo66208;
+__attribute__((used)) void* use66209 = (void*)&foo66209;
+__attribute__((used)) void* use66210 = (void*)&foo66210;
+__attribute__((used)) void* use66211 = (void*)&foo66211;
+__attribute__((used)) void* use66212 = (void*)&foo66212;
+__attribute__((used)) void* use66213 = (void*)&foo66213;
+__attribute__((used)) void* use66214 = (void*)&foo66214;
+__attribute__((used)) void* use66215 = (void*)&foo66215;
+__attribute__((used)) void* use66216 = (void*)&foo66216;
+__attribute__((used)) void* use66217 = (void*)&foo66217;
+__attribute__((used)) void* use66218 = (void*)&foo66218;
+__attribute__((used)) void* use66219 = (void*)&foo66219;
+__attribute__((used)) void* use66220 = (void*)&foo66220;
+__attribute__((used)) void* use66221 = (void*)&foo66221;
+__attribute__((used)) void* use66222 = (void*)&foo66222;
+__attribute__((used)) void* use66223 = (void*)&foo66223;
+__attribute__((used)) void* use66224 = (void*)&foo66224;
+__attribute__((used)) void* use66225 = (void*)&foo66225;
+__attribute__((used)) void* use66226 = (void*)&foo66226;
+__attribute__((used)) void* use66227 = (void*)&foo66227;
+__attribute__((used)) void* use66228 = (void*)&foo66228;
+__attribute__((used)) void* use66229 = (void*)&foo66229;
+__attribute__((used)) void* use66230 = (void*)&foo66230;
+__attribute__((used)) void* use66231 = (void*)&foo66231;
+__attribute__((used)) void* use66232 = (void*)&foo66232;
+__attribute__((used)) void* use66233 = (void*)&foo66233;
+__attribute__((used)) void* use66234 = (void*)&foo66234;
+__attribute__((used)) void* use66235 = (void*)&foo66235;
+__attribute__((used)) void* use66236 = (void*)&foo66236;
+__attribute__((used)) void* use66237 = (void*)&foo66237;
+__attribute__((used)) void* use66238 = (void*)&foo66238;
+__attribute__((used)) void* use66239 = (void*)&foo66239;
+__attribute__((used)) void* use66240 = (void*)&foo66240;
+__attribute__((used)) void* use66241 = (void*)&foo66241;
+__attribute__((used)) void* use66242 = (void*)&foo66242;
+__attribute__((used)) void* use66243 = (void*)&foo66243;
+__attribute__((used)) void* use66244 = (void*)&foo66244;
+__attribute__((used)) void* use66245 = (void*)&foo66245;
+__attribute__((used)) void* use66246 = (void*)&foo66246;
+__attribute__((used)) void* use66247 = (void*)&foo66247;
+__attribute__((used)) void* use66248 = (void*)&foo66248;
+__attribute__((used)) void* use66249 = (void*)&foo66249;
+__attribute__((used)) void* use66250 = (void*)&foo66250;
+__attribute__((used)) void* use66251 = (void*)&foo66251;
+__attribute__((used)) void* use66252 = (void*)&foo66252;
+__attribute__((used)) void* use66253 = (void*)&foo66253;
+__attribute__((used)) void* use66254 = (void*)&foo66254;
+__attribute__((used)) void* use66255 = (void*)&foo66255;
+__attribute__((used)) void* use66256 = (void*)&foo66256;
+__attribute__((used)) void* use66257 = (void*)&foo66257;
+__attribute__((used)) void* use66258 = (void*)&foo66258;
+__attribute__((used)) void* use66259 = (void*)&foo66259;
+__attribute__((used)) void* use66260 = (void*)&foo66260;
+__attribute__((used)) void* use66261 = (void*)&foo66261;
+__attribute__((used)) void* use66262 = (void*)&foo66262;
+__attribute__((used)) void* use66263 = (void*)&foo66263;
+__attribute__((used)) void* use66264 = (void*)&foo66264;
+__attribute__((used)) void* use66265 = (void*)&foo66265;
+__attribute__((used)) void* use66266 = (void*)&foo66266;
+__attribute__((used)) void* use66267 = (void*)&foo66267;
+__attribute__((used)) void* use66268 = (void*)&foo66268;
+__attribute__((used)) void* use66269 = (void*)&foo66269;
+__attribute__((used)) void* use66270 = (void*)&foo66270;
+__attribute__((used)) void* use66271 = (void*)&foo66271;
+__attribute__((used)) void* use66272 = (void*)&foo66272;
+__attribute__((used)) void* use66273 = (void*)&foo66273;
+__attribute__((used)) void* use66274 = (void*)&foo66274;
+__attribute__((used)) void* use66275 = (void*)&foo66275;
+__attribute__((used)) void* use66276 = (void*)&foo66276;
+__attribute__((used)) void* use66277 = (void*)&foo66277;
+__attribute__((used)) void* use66278 = (void*)&foo66278;
+__attribute__((used)) void* use66279 = (void*)&foo66279;
+__attribute__((used)) void* use66280 = (void*)&foo66280;
+__attribute__((used)) void* use66281 = (void*)&foo66281;
+__attribute__((used)) void* use66282 = (void*)&foo66282;
+__attribute__((used)) void* use66283 = (void*)&foo66283;
+__attribute__((used)) void* use66284 = (void*)&foo66284;
+__attribute__((used)) void* use66285 = (void*)&foo66285;
+__attribute__((used)) void* use66286 = (void*)&foo66286;
+__attribute__((used)) void* use66287 = (void*)&foo66287;
+__attribute__((used)) void* use66288 = (void*)&foo66288;
+__attribute__((used)) void* use66289 = (void*)&foo66289;
+__attribute__((used)) void* use66290 = (void*)&foo66290;
+__attribute__((used)) void* use66291 = (void*)&foo66291;
+__attribute__((used)) void* use66292 = (void*)&foo66292;
+__attribute__((used)) void* use66293 = (void*)&foo66293;
+__attribute__((used)) void* use66294 = (void*)&foo66294;
+__attribute__((used)) void* use66295 = (void*)&foo66295;
+__attribute__((used)) void* use66296 = (void*)&foo66296;
+__attribute__((used)) void* use66297 = (void*)&foo66297;
+__attribute__((used)) void* use66298 = (void*)&foo66298;
+__attribute__((used)) void* use66299 = (void*)&foo66299;
+__attribute__((used)) void* use66300 = (void*)&foo66300;
+__attribute__((used)) void* use66301 = (void*)&foo66301;
+__attribute__((used)) void* use66302 = (void*)&foo66302;
+__attribute__((used)) void* use66303 = (void*)&foo66303;
+__attribute__((used)) void* use66304 = (void*)&foo66304;
+__attribute__((used)) void* use66305 = (void*)&foo66305;
+__attribute__((used)) void* use66306 = (void*)&foo66306;
+__attribute__((used)) void* use66307 = (void*)&foo66307;
+__attribute__((used)) void* use66308 = (void*)&foo66308;
+__attribute__((used)) void* use66309 = (void*)&foo66309;
+__attribute__((used)) void* use66310 = (void*)&foo66310;
+__attribute__((used)) void* use66311 = (void*)&foo66311;
+__attribute__((used)) void* use66312 = (void*)&foo66312;
+__attribute__((used)) void* use66313 = (void*)&foo66313;
+__attribute__((used)) void* use66314 = (void*)&foo66314;
+__attribute__((used)) void* use66315 = (void*)&foo66315;
+__attribute__((used)) void* use66316 = (void*)&foo66316;
+__attribute__((used)) void* use66317 = (void*)&foo66317;
+__attribute__((used)) void* use66318 = (void*)&foo66318;
+__attribute__((used)) void* use66319 = (void*)&foo66319;
+__attribute__((used)) void* use66320 = (void*)&foo66320;
+__attribute__((used)) void* use66321 = (void*)&foo66321;
+__attribute__((used)) void* use66322 = (void*)&foo66322;
+__attribute__((used)) void* use66323 = (void*)&foo66323;
+__attribute__((used)) void* use66324 = (void*)&foo66324;
+__attribute__((used)) void* use66325 = (void*)&foo66325;
+__attribute__((used)) void* use66326 = (void*)&foo66326;
+__attribute__((used)) void* use66327 = (void*)&foo66327;
+__attribute__((used)) void* use66328 = (void*)&foo66328;
+__attribute__((used)) void* use66329 = (void*)&foo66329;
+__attribute__((used)) void* use66330 = (void*)&foo66330;
+__attribute__((used)) void* use66331 = (void*)&foo66331;
+__attribute__((used)) void* use66332 = (void*)&foo66332;
+__attribute__((used)) void* use66333 = (void*)&foo66333;
+__attribute__((used)) void* use66334 = (void*)&foo66334;
+__attribute__((used)) void* use66335 = (void*)&foo66335;
+__attribute__((used)) void* use66336 = (void*)&foo66336;
+__attribute__((used)) void* use66337 = (void*)&foo66337;
+__attribute__((used)) void* use66338 = (void*)&foo66338;
+__attribute__((used)) void* use66339 = (void*)&foo66339;
+__attribute__((used)) void* use66340 = (void*)&foo66340;
+__attribute__((used)) void* use66341 = (void*)&foo66341;
+__attribute__((used)) void* use66342 = (void*)&foo66342;
+__attribute__((used)) void* use66343 = (void*)&foo66343;
+__attribute__((used)) void* use66344 = (void*)&foo66344;
+__attribute__((used)) void* use66345 = (void*)&foo66345;
+__attribute__((used)) void* use66346 = (void*)&foo66346;
+__attribute__((used)) void* use66347 = (void*)&foo66347;
+__attribute__((used)) void* use66348 = (void*)&foo66348;
+__attribute__((used)) void* use66349 = (void*)&foo66349;
+__attribute__((used)) void* use66350 = (void*)&foo66350;
+__attribute__((used)) void* use66351 = (void*)&foo66351;
+__attribute__((used)) void* use66352 = (void*)&foo66352;
+__attribute__((used)) void* use66353 = (void*)&foo66353;
+__attribute__((used)) void* use66354 = (void*)&foo66354;
+__attribute__((used)) void* use66355 = (void*)&foo66355;
+__attribute__((used)) void* use66356 = (void*)&foo66356;
+__attribute__((used)) void* use66357 = (void*)&foo66357;
+__attribute__((used)) void* use66358 = (void*)&foo66358;
+__attribute__((used)) void* use66359 = (void*)&foo66359;
+__attribute__((used)) void* use66360 = (void*)&foo66360;
+__attribute__((used)) void* use66361 = (void*)&foo66361;
+__attribute__((used)) void* use66362 = (void*)&foo66362;
+__attribute__((used)) void* use66363 = (void*)&foo66363;
+__attribute__((used)) void* use66364 = (void*)&foo66364;
+__attribute__((used)) void* use66365 = (void*)&foo66365;
+__attribute__((used)) void* use66366 = (void*)&foo66366;
+__attribute__((used)) void* use66367 = (void*)&foo66367;
+__attribute__((used)) void* use66368 = (void*)&foo66368;
+__attribute__((used)) void* use66369 = (void*)&foo66369;
+__attribute__((used)) void* use66370 = (void*)&foo66370;
+__attribute__((used)) void* use66371 = (void*)&foo66371;
+__attribute__((used)) void* use66372 = (void*)&foo66372;
+__attribute__((used)) void* use66373 = (void*)&foo66373;
+__attribute__((used)) void* use66374 = (void*)&foo66374;
+__attribute__((used)) void* use66375 = (void*)&foo66375;
+__attribute__((used)) void* use66376 = (void*)&foo66376;
+__attribute__((used)) void* use66377 = (void*)&foo66377;
+__attribute__((used)) void* use66378 = (void*)&foo66378;
+__attribute__((used)) void* use66379 = (void*)&foo66379;
+__attribute__((used)) void* use66380 = (void*)&foo66380;
+__attribute__((used)) void* use66381 = (void*)&foo66381;
+__attribute__((used)) void* use66382 = (void*)&foo66382;
+__attribute__((used)) void* use66383 = (void*)&foo66383;
+__attribute__((used)) void* use66384 = (void*)&foo66384;
+__attribute__((used)) void* use66385 = (void*)&foo66385;
+__attribute__((used)) void* use66386 = (void*)&foo66386;
+__attribute__((used)) void* use66387 = (void*)&foo66387;
+__attribute__((used)) void* use66388 = (void*)&foo66388;
+__attribute__((used)) void* use66389 = (void*)&foo66389;
+__attribute__((used)) void* use66390 = (void*)&foo66390;
+__attribute__((used)) void* use66391 = (void*)&foo66391;
+__attribute__((used)) void* use66392 = (void*)&foo66392;
+__attribute__((used)) void* use66393 = (void*)&foo66393;
+__attribute__((used)) void* use66394 = (void*)&foo66394;
+__attribute__((used)) void* use66395 = (void*)&foo66395;
+__attribute__((used)) void* use66396 = (void*)&foo66396;
+__attribute__((used)) void* use66397 = (void*)&foo66397;
+__attribute__((used)) void* use66398 = (void*)&foo66398;
+__attribute__((used)) void* use66399 = (void*)&foo66399;
+__attribute__((used)) void* use66400 = (void*)&foo66400;
+__attribute__((used)) void* use66401 = (void*)&foo66401;
+__attribute__((used)) void* use66402 = (void*)&foo66402;
+__attribute__((used)) void* use66403 = (void*)&foo66403;
+__attribute__((used)) void* use66404 = (void*)&foo66404;
+__attribute__((used)) void* use66405 = (void*)&foo66405;
+__attribute__((used)) void* use66406 = (void*)&foo66406;
+__attribute__((used)) void* use66407 = (void*)&foo66407;
+__attribute__((used)) void* use66408 = (void*)&foo66408;
+__attribute__((used)) void* use66409 = (void*)&foo66409;
+__attribute__((used)) void* use66410 = (void*)&foo66410;
+__attribute__((used)) void* use66411 = (void*)&foo66411;
+__attribute__((used)) void* use66412 = (void*)&foo66412;
+__attribute__((used)) void* use66413 = (void*)&foo66413;
+__attribute__((used)) void* use66414 = (void*)&foo66414;
+__attribute__((used)) void* use66415 = (void*)&foo66415;
+__attribute__((used)) void* use66416 = (void*)&foo66416;
+__attribute__((used)) void* use66417 = (void*)&foo66417;
+__attribute__((used)) void* use66418 = (void*)&foo66418;
+__attribute__((used)) void* use66419 = (void*)&foo66419;
+__attribute__((used)) void* use66420 = (void*)&foo66420;
+__attribute__((used)) void* use66421 = (void*)&foo66421;
+__attribute__((used)) void* use66422 = (void*)&foo66422;
+__attribute__((used)) void* use66423 = (void*)&foo66423;
+__attribute__((used)) void* use66424 = (void*)&foo66424;
+__attribute__((used)) void* use66425 = (void*)&foo66425;
+__attribute__((used)) void* use66426 = (void*)&foo66426;
+__attribute__((used)) void* use66427 = (void*)&foo66427;
+__attribute__((used)) void* use66428 = (void*)&foo66428;
+__attribute__((used)) void* use66429 = (void*)&foo66429;
+__attribute__((used)) void* use66430 = (void*)&foo66430;
+__attribute__((used)) void* use66431 = (void*)&foo66431;
+__attribute__((used)) void* use66432 = (void*)&foo66432;
+__attribute__((used)) void* use66433 = (void*)&foo66433;
+__attribute__((used)) void* use66434 = (void*)&foo66434;
+__attribute__((used)) void* use66435 = (void*)&foo66435;
+__attribute__((used)) void* use66436 = (void*)&foo66436;
+__attribute__((used)) void* use66437 = (void*)&foo66437;
+__attribute__((used)) void* use66438 = (void*)&foo66438;
+__attribute__((used)) void* use66439 = (void*)&foo66439;
+__attribute__((used)) void* use66440 = (void*)&foo66440;
+__attribute__((used)) void* use66441 = (void*)&foo66441;
+__attribute__((used)) void* use66442 = (void*)&foo66442;
+__attribute__((used)) void* use66443 = (void*)&foo66443;
+__attribute__((used)) void* use66444 = (void*)&foo66444;
+__attribute__((used)) void* use66445 = (void*)&foo66445;
+__attribute__((used)) void* use66446 = (void*)&foo66446;
+__attribute__((used)) void* use66447 = (void*)&foo66447;
+__attribute__((used)) void* use66448 = (void*)&foo66448;
+__attribute__((used)) void* use66449 = (void*)&foo66449;
+__attribute__((used)) void* use66450 = (void*)&foo66450;
+__attribute__((used)) void* use66451 = (void*)&foo66451;
+__attribute__((used)) void* use66452 = (void*)&foo66452;
+__attribute__((used)) void* use66453 = (void*)&foo66453;
+__attribute__((used)) void* use66454 = (void*)&foo66454;
+__attribute__((used)) void* use66455 = (void*)&foo66455;
+__attribute__((used)) void* use66456 = (void*)&foo66456;
+__attribute__((used)) void* use66457 = (void*)&foo66457;
+__attribute__((used)) void* use66458 = (void*)&foo66458;
+__attribute__((used)) void* use66459 = (void*)&foo66459;
+__attribute__((used)) void* use66460 = (void*)&foo66460;
+__attribute__((used)) void* use66461 = (void*)&foo66461;
+__attribute__((used)) void* use66462 = (void*)&foo66462;
+__attribute__((used)) void* use66463 = (void*)&foo66463;
+__attribute__((used)) void* use66464 = (void*)&foo66464;
+__attribute__((used)) void* use66465 = (void*)&foo66465;
+__attribute__((used)) void* use66466 = (void*)&foo66466;
+__attribute__((used)) void* use66467 = (void*)&foo66467;
+__attribute__((used)) void* use66468 = (void*)&foo66468;
+__attribute__((used)) void* use66469 = (void*)&foo66469;
+__attribute__((used)) void* use66470 = (void*)&foo66470;
+__attribute__((used)) void* use66471 = (void*)&foo66471;
+__attribute__((used)) void* use66472 = (void*)&foo66472;
+__attribute__((used)) void* use66473 = (void*)&foo66473;
+__attribute__((used)) void* use66474 = (void*)&foo66474;
+__attribute__((used)) void* use66475 = (void*)&foo66475;
+__attribute__((used)) void* use66476 = (void*)&foo66476;
+__attribute__((used)) void* use66477 = (void*)&foo66477;
+__attribute__((used)) void* use66478 = (void*)&foo66478;
+__attribute__((used)) void* use66479 = (void*)&foo66479;
+__attribute__((used)) void* use66480 = (void*)&foo66480;
+__attribute__((used)) void* use66481 = (void*)&foo66481;
+__attribute__((used)) void* use66482 = (void*)&foo66482;
+__attribute__((used)) void* use66483 = (void*)&foo66483;
+__attribute__((used)) void* use66484 = (void*)&foo66484;
+__attribute__((used)) void* use66485 = (void*)&foo66485;
+__attribute__((used)) void* use66486 = (void*)&foo66486;
+__attribute__((used)) void* use66487 = (void*)&foo66487;
+__attribute__((used)) void* use66488 = (void*)&foo66488;
+__attribute__((used)) void* use66489 = (void*)&foo66489;
+__attribute__((used)) void* use66490 = (void*)&foo66490;
+__attribute__((used)) void* use66491 = (void*)&foo66491;
+__attribute__((used)) void* use66492 = (void*)&foo66492;
+__attribute__((used)) void* use66493 = (void*)&foo66493;
+__attribute__((used)) void* use66494 = (void*)&foo66494;
+__attribute__((used)) void* use66495 = (void*)&foo66495;
+__attribute__((used)) void* use66496 = (void*)&foo66496;
+__attribute__((used)) void* use66497 = (void*)&foo66497;
+__attribute__((used)) void* use66498 = (void*)&foo66498;
+__attribute__((used)) void* use66499 = (void*)&foo66499;
+__attribute__((used)) void* use66500 = (void*)&foo66500;
+__attribute__((used)) void* use66501 = (void*)&foo66501;
+__attribute__((used)) void* use66502 = (void*)&foo66502;
+__attribute__((used)) void* use66503 = (void*)&foo66503;
+__attribute__((used)) void* use66504 = (void*)&foo66504;
+__attribute__((used)) void* use66505 = (void*)&foo66505;
+__attribute__((used)) void* use66506 = (void*)&foo66506;
+__attribute__((used)) void* use66507 = (void*)&foo66507;
+__attribute__((used)) void* use66508 = (void*)&foo66508;
+__attribute__((used)) void* use66509 = (void*)&foo66509;
+__attribute__((used)) void* use66510 = (void*)&foo66510;
+__attribute__((used)) void* use66511 = (void*)&foo66511;
+__attribute__((used)) void* use66512 = (void*)&foo66512;
+__attribute__((used)) void* use66513 = (void*)&foo66513;
+__attribute__((used)) void* use66514 = (void*)&foo66514;
+__attribute__((used)) void* use66515 = (void*)&foo66515;
+__attribute__((used)) void* use66516 = (void*)&foo66516;
+__attribute__((used)) void* use66517 = (void*)&foo66517;
+__attribute__((used)) void* use66518 = (void*)&foo66518;
+__attribute__((used)) void* use66519 = (void*)&foo66519;
+__attribute__((used)) void* use66520 = (void*)&foo66520;
+__attribute__((used)) void* use66521 = (void*)&foo66521;
+__attribute__((used)) void* use66522 = (void*)&foo66522;
+__attribute__((used)) void* use66523 = (void*)&foo66523;
+__attribute__((used)) void* use66524 = (void*)&foo66524;
+__attribute__((used)) void* use66525 = (void*)&foo66525;
+__attribute__((used)) void* use66526 = (void*)&foo66526;
+__attribute__((used)) void* use66527 = (void*)&foo66527;
+__attribute__((used)) void* use66528 = (void*)&foo66528;
+__attribute__((used)) void* use66529 = (void*)&foo66529;
+__attribute__((used)) void* use66530 = (void*)&foo66530;
+__attribute__((used)) void* use66531 = (void*)&foo66531;
+__attribute__((used)) void* use66532 = (void*)&foo66532;
+__attribute__((used)) void* use66533 = (void*)&foo66533;
+__attribute__((used)) void* use66534 = (void*)&foo66534;
+__attribute__((used)) void* use66535 = (void*)&foo66535;
+__attribute__((used)) void* use66536 = (void*)&foo66536;
+__attribute__((used)) void* use66537 = (void*)&foo66537;
+__attribute__((used)) void* use66538 = (void*)&foo66538;
+__attribute__((used)) void* use66539 = (void*)&foo66539;
+__attribute__((used)) void* use66540 = (void*)&foo66540;
+__attribute__((used)) void* use66541 = (void*)&foo66541;
+__attribute__((used)) void* use66542 = (void*)&foo66542;
+__attribute__((used)) void* use66543 = (void*)&foo66543;
+__attribute__((used)) void* use66544 = (void*)&foo66544;
+__attribute__((used)) void* use66545 = (void*)&foo66545;
+__attribute__((used)) void* use66546 = (void*)&foo66546;
+__attribute__((used)) void* use66547 = (void*)&foo66547;
+__attribute__((used)) void* use66548 = (void*)&foo66548;
+__attribute__((used)) void* use66549 = (void*)&foo66549;
+__attribute__((used)) void* use66550 = (void*)&foo66550;
+__attribute__((used)) void* use66551 = (void*)&foo66551;
+__attribute__((used)) void* use66552 = (void*)&foo66552;
+__attribute__((used)) void* use66553 = (void*)&foo66553;
+__attribute__((used)) void* use66554 = (void*)&foo66554;
+__attribute__((used)) void* use66555 = (void*)&foo66555;
+__attribute__((used)) void* use66556 = (void*)&foo66556;
+__attribute__((used)) void* use66557 = (void*)&foo66557;
+__attribute__((used)) void* use66558 = (void*)&foo66558;
+__attribute__((used)) void* use66559 = (void*)&foo66559;
+__attribute__((used)) void* use66560 = (void*)&foo66560;
+__attribute__((used)) void* use66561 = (void*)&foo66561;
+__attribute__((used)) void* use66562 = (void*)&foo66562;
+__attribute__((used)) void* use66563 = (void*)&foo66563;
+__attribute__((used)) void* use66564 = (void*)&foo66564;
+__attribute__((used)) void* use66565 = (void*)&foo66565;
+__attribute__((used)) void* use66566 = (void*)&foo66566;
+__attribute__((used)) void* use66567 = (void*)&foo66567;
+__attribute__((used)) void* use66568 = (void*)&foo66568;
+__attribute__((used)) void* use66569 = (void*)&foo66569;
+__attribute__((used)) void* use66570 = (void*)&foo66570;
+__attribute__((used)) void* use66571 = (void*)&foo66571;
+__attribute__((used)) void* use66572 = (void*)&foo66572;
+__attribute__((used)) void* use66573 = (void*)&foo66573;
+__attribute__((used)) void* use66574 = (void*)&foo66574;
+__attribute__((used)) void* use66575 = (void*)&foo66575;
+__attribute__((used)) void* use66576 = (void*)&foo66576;
+__attribute__((used)) void* use66577 = (void*)&foo66577;
+__attribute__((used)) void* use66578 = (void*)&foo66578;
+__attribute__((used)) void* use66579 = (void*)&foo66579;
+__attribute__((used)) void* use66580 = (void*)&foo66580;
+__attribute__((used)) void* use66581 = (void*)&foo66581;
+__attribute__((used)) void* use66582 = (void*)&foo66582;
+__attribute__((used)) void* use66583 = (void*)&foo66583;
+__attribute__((used)) void* use66584 = (void*)&foo66584;
+__attribute__((used)) void* use66585 = (void*)&foo66585;
+__attribute__((used)) void* use66586 = (void*)&foo66586;
+__attribute__((used)) void* use66587 = (void*)&foo66587;
+__attribute__((used)) void* use66588 = (void*)&foo66588;
+__attribute__((used)) void* use66589 = (void*)&foo66589;
+__attribute__((used)) void* use66590 = (void*)&foo66590;
+__attribute__((used)) void* use66591 = (void*)&foo66591;
+__attribute__((used)) void* use66592 = (void*)&foo66592;
+__attribute__((used)) void* use66593 = (void*)&foo66593;
+__attribute__((used)) void* use66594 = (void*)&foo66594;
+__attribute__((used)) void* use66595 = (void*)&foo66595;
+__attribute__((used)) void* use66596 = (void*)&foo66596;
+__attribute__((used)) void* use66597 = (void*)&foo66597;
+__attribute__((used)) void* use66598 = (void*)&foo66598;
+__attribute__((used)) void* use66599 = (void*)&foo66599;
+__attribute__((used)) void* use66600 = (void*)&foo66600;
+__attribute__((used)) void* use66601 = (void*)&foo66601;
+__attribute__((used)) void* use66602 = (void*)&foo66602;
+__attribute__((used)) void* use66603 = (void*)&foo66603;
+__attribute__((used)) void* use66604 = (void*)&foo66604;
+__attribute__((used)) void* use66605 = (void*)&foo66605;
+__attribute__((used)) void* use66606 = (void*)&foo66606;
+__attribute__((used)) void* use66607 = (void*)&foo66607;
+__attribute__((used)) void* use66608 = (void*)&foo66608;
+__attribute__((used)) void* use66609 = (void*)&foo66609;
+__attribute__((used)) void* use66610 = (void*)&foo66610;
+__attribute__((used)) void* use66611 = (void*)&foo66611;
+__attribute__((used)) void* use66612 = (void*)&foo66612;
+__attribute__((used)) void* use66613 = (void*)&foo66613;
+__attribute__((used)) void* use66614 = (void*)&foo66614;
+__attribute__((used)) void* use66615 = (void*)&foo66615;
+__attribute__((used)) void* use66616 = (void*)&foo66616;
+__attribute__((used)) void* use66617 = (void*)&foo66617;
+__attribute__((used)) void* use66618 = (void*)&foo66618;
+__attribute__((used)) void* use66619 = (void*)&foo66619;
+__attribute__((used)) void* use66620 = (void*)&foo66620;
+__attribute__((used)) void* use66621 = (void*)&foo66621;
+__attribute__((used)) void* use66622 = (void*)&foo66622;
+__attribute__((used)) void* use66623 = (void*)&foo66623;
+__attribute__((used)) void* use66624 = (void*)&foo66624;
+__attribute__((used)) void* use66625 = (void*)&foo66625;
+__attribute__((used)) void* use66626 = (void*)&foo66626;
+__attribute__((used)) void* use66627 = (void*)&foo66627;
+__attribute__((used)) void* use66628 = (void*)&foo66628;
+__attribute__((used)) void* use66629 = (void*)&foo66629;
+__attribute__((used)) void* use66630 = (void*)&foo66630;
+__attribute__((used)) void* use66631 = (void*)&foo66631;
+__attribute__((used)) void* use66632 = (void*)&foo66632;
+__attribute__((used)) void* use66633 = (void*)&foo66633;
+__attribute__((used)) void* use66634 = (void*)&foo66634;
+__attribute__((used)) void* use66635 = (void*)&foo66635;
+__attribute__((used)) void* use66636 = (void*)&foo66636;
+__attribute__((used)) void* use66637 = (void*)&foo66637;
+__attribute__((used)) void* use66638 = (void*)&foo66638;
+__attribute__((used)) void* use66639 = (void*)&foo66639;
+__attribute__((used)) void* use66640 = (void*)&foo66640;
+__attribute__((used)) void* use66641 = (void*)&foo66641;
+__attribute__((used)) void* use66642 = (void*)&foo66642;
+__attribute__((used)) void* use66643 = (void*)&foo66643;
+__attribute__((used)) void* use66644 = (void*)&foo66644;
+__attribute__((used)) void* use66645 = (void*)&foo66645;
+__attribute__((used)) void* use66646 = (void*)&foo66646;
+__attribute__((used)) void* use66647 = (void*)&foo66647;
+__attribute__((used)) void* use66648 = (void*)&foo66648;
+__attribute__((used)) void* use66649 = (void*)&foo66649;
+__attribute__((used)) void* use66650 = (void*)&foo66650;
+__attribute__((used)) void* use66651 = (void*)&foo66651;
+__attribute__((used)) void* use66652 = (void*)&foo66652;
+__attribute__((used)) void* use66653 = (void*)&foo66653;
+__attribute__((used)) void* use66654 = (void*)&foo66654;
+__attribute__((used)) void* use66655 = (void*)&foo66655;
+__attribute__((used)) void* use66656 = (void*)&foo66656;
+__attribute__((used)) void* use66657 = (void*)&foo66657;
+__attribute__((used)) void* use66658 = (void*)&foo66658;
+__attribute__((used)) void* use66659 = (void*)&foo66659;
+__attribute__((used)) void* use66660 = (void*)&foo66660;
+__attribute__((used)) void* use66661 = (void*)&foo66661;
+__attribute__((used)) void* use66662 = (void*)&foo66662;
+__attribute__((used)) void* use66663 = (void*)&foo66663;
+__attribute__((used)) void* use66664 = (void*)&foo66664;
+__attribute__((used)) void* use66665 = (void*)&foo66665;
+__attribute__((used)) void* use66666 = (void*)&foo66666;
+__attribute__((used)) void* use66667 = (void*)&foo66667;
+__attribute__((used)) void* use66668 = (void*)&foo66668;
+__attribute__((used)) void* use66669 = (void*)&foo66669;
+__attribute__((used)) void* use66670 = (void*)&foo66670;
+__attribute__((used)) void* use66671 = (void*)&foo66671;
+__attribute__((used)) void* use66672 = (void*)&foo66672;
+__attribute__((used)) void* use66673 = (void*)&foo66673;
+__attribute__((used)) void* use66674 = (void*)&foo66674;
+__attribute__((used)) void* use66675 = (void*)&foo66675;
+__attribute__((used)) void* use66676 = (void*)&foo66676;
+__attribute__((used)) void* use66677 = (void*)&foo66677;
+__attribute__((used)) void* use66678 = (void*)&foo66678;
+__attribute__((used)) void* use66679 = (void*)&foo66679;
+__attribute__((used)) void* use66680 = (void*)&foo66680;
+__attribute__((used)) void* use66681 = (void*)&foo66681;
+__attribute__((used)) void* use66682 = (void*)&foo66682;
+__attribute__((used)) void* use66683 = (void*)&foo66683;
+__attribute__((used)) void* use66684 = (void*)&foo66684;
+__attribute__((used)) void* use66685 = (void*)&foo66685;
+__attribute__((used)) void* use66686 = (void*)&foo66686;
+__attribute__((used)) void* use66687 = (void*)&foo66687;
+__attribute__((used)) void* use66688 = (void*)&foo66688;
+__attribute__((used)) void* use66689 = (void*)&foo66689;
+__attribute__((used)) void* use66690 = (void*)&foo66690;
+__attribute__((used)) void* use66691 = (void*)&foo66691;
+__attribute__((used)) void* use66692 = (void*)&foo66692;
+__attribute__((used)) void* use66693 = (void*)&foo66693;
+__attribute__((used)) void* use66694 = (void*)&foo66694;
+__attribute__((used)) void* use66695 = (void*)&foo66695;
+__attribute__((used)) void* use66696 = (void*)&foo66696;
+__attribute__((used)) void* use66697 = (void*)&foo66697;
+__attribute__((used)) void* use66698 = (void*)&foo66698;
+__attribute__((used)) void* use66699 = (void*)&foo66699;
+__attribute__((used)) void* use66700 = (void*)&foo66700;
+__attribute__((used)) void* use66701 = (void*)&foo66701;
+__attribute__((used)) void* use66702 = (void*)&foo66702;
+__attribute__((used)) void* use66703 = (void*)&foo66703;
+__attribute__((used)) void* use66704 = (void*)&foo66704;
+__attribute__((used)) void* use66705 = (void*)&foo66705;
+__attribute__((used)) void* use66706 = (void*)&foo66706;
+__attribute__((used)) void* use66707 = (void*)&foo66707;
+__attribute__((used)) void* use66708 = (void*)&foo66708;
+__attribute__((used)) void* use66709 = (void*)&foo66709;
+__attribute__((used)) void* use66710 = (void*)&foo66710;
+__attribute__((used)) void* use66711 = (void*)&foo66711;
+__attribute__((used)) void* use66712 = (void*)&foo66712;
+__attribute__((used)) void* use66713 = (void*)&foo66713;
+__attribute__((used)) void* use66714 = (void*)&foo66714;
+__attribute__((used)) void* use66715 = (void*)&foo66715;
+__attribute__((used)) void* use66716 = (void*)&foo66716;
+__attribute__((used)) void* use66717 = (void*)&foo66717;
+__attribute__((used)) void* use66718 = (void*)&foo66718;
+__attribute__((used)) void* use66719 = (void*)&foo66719;
+__attribute__((used)) void* use66720 = (void*)&foo66720;
+__attribute__((used)) void* use66721 = (void*)&foo66721;
+__attribute__((used)) void* use66722 = (void*)&foo66722;
+__attribute__((used)) void* use66723 = (void*)&foo66723;
+__attribute__((used)) void* use66724 = (void*)&foo66724;
+__attribute__((used)) void* use66725 = (void*)&foo66725;
+__attribute__((used)) void* use66726 = (void*)&foo66726;
+__attribute__((used)) void* use66727 = (void*)&foo66727;
+__attribute__((used)) void* use66728 = (void*)&foo66728;
+__attribute__((used)) void* use66729 = (void*)&foo66729;
+__attribute__((used)) void* use66730 = (void*)&foo66730;
+__attribute__((used)) void* use66731 = (void*)&foo66731;
+__attribute__((used)) void* use66732 = (void*)&foo66732;
+__attribute__((used)) void* use66733 = (void*)&foo66733;
+__attribute__((used)) void* use66734 = (void*)&foo66734;
+__attribute__((used)) void* use66735 = (void*)&foo66735;
+__attribute__((used)) void* use66736 = (void*)&foo66736;
+__attribute__((used)) void* use66737 = (void*)&foo66737;
+__attribute__((used)) void* use66738 = (void*)&foo66738;
+__attribute__((used)) void* use66739 = (void*)&foo66739;
+__attribute__((used)) void* use66740 = (void*)&foo66740;
+__attribute__((used)) void* use66741 = (void*)&foo66741;
+__attribute__((used)) void* use66742 = (void*)&foo66742;
+__attribute__((used)) void* use66743 = (void*)&foo66743;
+__attribute__((used)) void* use66744 = (void*)&foo66744;
+__attribute__((used)) void* use66745 = (void*)&foo66745;
+__attribute__((used)) void* use66746 = (void*)&foo66746;
+__attribute__((used)) void* use66747 = (void*)&foo66747;
+__attribute__((used)) void* use66748 = (void*)&foo66748;
+__attribute__((used)) void* use66749 = (void*)&foo66749;
+__attribute__((used)) void* use66750 = (void*)&foo66750;
+__attribute__((used)) void* use66751 = (void*)&foo66751;
+__attribute__((used)) void* use66752 = (void*)&foo66752;
+__attribute__((used)) void* use66753 = (void*)&foo66753;
+__attribute__((used)) void* use66754 = (void*)&foo66754;
+__attribute__((used)) void* use66755 = (void*)&foo66755;
+__attribute__((used)) void* use66756 = (void*)&foo66756;
+__attribute__((used)) void* use66757 = (void*)&foo66757;
+__attribute__((used)) void* use66758 = (void*)&foo66758;
+__attribute__((used)) void* use66759 = (void*)&foo66759;
+__attribute__((used)) void* use66760 = (void*)&foo66760;
+__attribute__((used)) void* use66761 = (void*)&foo66761;
+__attribute__((used)) void* use66762 = (void*)&foo66762;
+__attribute__((used)) void* use66763 = (void*)&foo66763;
+__attribute__((used)) void* use66764 = (void*)&foo66764;
+__attribute__((used)) void* use66765 = (void*)&foo66765;
+__attribute__((used)) void* use66766 = (void*)&foo66766;
+__attribute__((used)) void* use66767 = (void*)&foo66767;
+__attribute__((used)) void* use66768 = (void*)&foo66768;
+__attribute__((used)) void* use66769 = (void*)&foo66769;
+__attribute__((used)) void* use66770 = (void*)&foo66770;
+__attribute__((used)) void* use66771 = (void*)&foo66771;
+__attribute__((used)) void* use66772 = (void*)&foo66772;
+__attribute__((used)) void* use66773 = (void*)&foo66773;
+__attribute__((used)) void* use66774 = (void*)&foo66774;
+__attribute__((used)) void* use66775 = (void*)&foo66775;
+__attribute__((used)) void* use66776 = (void*)&foo66776;
+__attribute__((used)) void* use66777 = (void*)&foo66777;
+__attribute__((used)) void* use66778 = (void*)&foo66778;
+__attribute__((used)) void* use66779 = (void*)&foo66779;
+__attribute__((used)) void* use66780 = (void*)&foo66780;
+__attribute__((used)) void* use66781 = (void*)&foo66781;
+__attribute__((used)) void* use66782 = (void*)&foo66782;
+__attribute__((used)) void* use66783 = (void*)&foo66783;
+__attribute__((used)) void* use66784 = (void*)&foo66784;
+__attribute__((used)) void* use66785 = (void*)&foo66785;
+__attribute__((used)) void* use66786 = (void*)&foo66786;
+__attribute__((used)) void* use66787 = (void*)&foo66787;
+__attribute__((used)) void* use66788 = (void*)&foo66788;
+__attribute__((used)) void* use66789 = (void*)&foo66789;
+__attribute__((used)) void* use66790 = (void*)&foo66790;
+__attribute__((used)) void* use66791 = (void*)&foo66791;
+__attribute__((used)) void* use66792 = (void*)&foo66792;
+__attribute__((used)) void* use66793 = (void*)&foo66793;
+__attribute__((used)) void* use66794 = (void*)&foo66794;
+__attribute__((used)) void* use66795 = (void*)&foo66795;
+__attribute__((used)) void* use66796 = (void*)&foo66796;
+__attribute__((used)) void* use66797 = (void*)&foo66797;
+__attribute__((used)) void* use66798 = (void*)&foo66798;
+__attribute__((used)) void* use66799 = (void*)&foo66799;
+__attribute__((used)) void* use66800 = (void*)&foo66800;
+__attribute__((used)) void* use66801 = (void*)&foo66801;
+__attribute__((used)) void* use66802 = (void*)&foo66802;
+__attribute__((used)) void* use66803 = (void*)&foo66803;
+__attribute__((used)) void* use66804 = (void*)&foo66804;
+__attribute__((used)) void* use66805 = (void*)&foo66805;
+__attribute__((used)) void* use66806 = (void*)&foo66806;
+__attribute__((used)) void* use66807 = (void*)&foo66807;
+__attribute__((used)) void* use66808 = (void*)&foo66808;
+__attribute__((used)) void* use66809 = (void*)&foo66809;
+__attribute__((used)) void* use66810 = (void*)&foo66810;
+__attribute__((used)) void* use66811 = (void*)&foo66811;
+__attribute__((used)) void* use66812 = (void*)&foo66812;
+__attribute__((used)) void* use66813 = (void*)&foo66813;
+__attribute__((used)) void* use66814 = (void*)&foo66814;
+__attribute__((used)) void* use66815 = (void*)&foo66815;
+__attribute__((used)) void* use66816 = (void*)&foo66816;
+__attribute__((used)) void* use66817 = (void*)&foo66817;
+__attribute__((used)) void* use66818 = (void*)&foo66818;
+__attribute__((used)) void* use66819 = (void*)&foo66819;
+__attribute__((used)) void* use66820 = (void*)&foo66820;
+__attribute__((used)) void* use66821 = (void*)&foo66821;
+__attribute__((used)) void* use66822 = (void*)&foo66822;
+__attribute__((used)) void* use66823 = (void*)&foo66823;
+__attribute__((used)) void* use66824 = (void*)&foo66824;
+__attribute__((used)) void* use66825 = (void*)&foo66825;
+__attribute__((used)) void* use66826 = (void*)&foo66826;
+__attribute__((used)) void* use66827 = (void*)&foo66827;
+__attribute__((used)) void* use66828 = (void*)&foo66828;
+__attribute__((used)) void* use66829 = (void*)&foo66829;
+__attribute__((used)) void* use66830 = (void*)&foo66830;
+__attribute__((used)) void* use66831 = (void*)&foo66831;
+__attribute__((used)) void* use66832 = (void*)&foo66832;
+__attribute__((used)) void* use66833 = (void*)&foo66833;
+__attribute__((used)) void* use66834 = (void*)&foo66834;
+__attribute__((used)) void* use66835 = (void*)&foo66835;
+__attribute__((used)) void* use66836 = (void*)&foo66836;
+__attribute__((used)) void* use66837 = (void*)&foo66837;
+__attribute__((used)) void* use66838 = (void*)&foo66838;
+__attribute__((used)) void* use66839 = (void*)&foo66839;
+__attribute__((used)) void* use66840 = (void*)&foo66840;
+__attribute__((used)) void* use66841 = (void*)&foo66841;
+__attribute__((used)) void* use66842 = (void*)&foo66842;
+__attribute__((used)) void* use66843 = (void*)&foo66843;
+__attribute__((used)) void* use66844 = (void*)&foo66844;
+__attribute__((used)) void* use66845 = (void*)&foo66845;
+__attribute__((used)) void* use66846 = (void*)&foo66846;
+__attribute__((used)) void* use66847 = (void*)&foo66847;
+__attribute__((used)) void* use66848 = (void*)&foo66848;
+__attribute__((used)) void* use66849 = (void*)&foo66849;
+__attribute__((used)) void* use66850 = (void*)&foo66850;
+__attribute__((used)) void* use66851 = (void*)&foo66851;
+__attribute__((used)) void* use66852 = (void*)&foo66852;
+__attribute__((used)) void* use66853 = (void*)&foo66853;
+__attribute__((used)) void* use66854 = (void*)&foo66854;
+__attribute__((used)) void* use66855 = (void*)&foo66855;
+__attribute__((used)) void* use66856 = (void*)&foo66856;
+__attribute__((used)) void* use66857 = (void*)&foo66857;
+__attribute__((used)) void* use66858 = (void*)&foo66858;
+__attribute__((used)) void* use66859 = (void*)&foo66859;
+__attribute__((used)) void* use66860 = (void*)&foo66860;
+__attribute__((used)) void* use66861 = (void*)&foo66861;
+__attribute__((used)) void* use66862 = (void*)&foo66862;
+__attribute__((used)) void* use66863 = (void*)&foo66863;
+__attribute__((used)) void* use66864 = (void*)&foo66864;
+__attribute__((used)) void* use66865 = (void*)&foo66865;
+__attribute__((used)) void* use66866 = (void*)&foo66866;
+__attribute__((used)) void* use66867 = (void*)&foo66867;
+__attribute__((used)) void* use66868 = (void*)&foo66868;
+__attribute__((used)) void* use66869 = (void*)&foo66869;
+__attribute__((used)) void* use66870 = (void*)&foo66870;
+__attribute__((used)) void* use66871 = (void*)&foo66871;
+__attribute__((used)) void* use66872 = (void*)&foo66872;
+__attribute__((used)) void* use66873 = (void*)&foo66873;
+__attribute__((used)) void* use66874 = (void*)&foo66874;
+__attribute__((used)) void* use66875 = (void*)&foo66875;
+__attribute__((used)) void* use66876 = (void*)&foo66876;
+__attribute__((used)) void* use66877 = (void*)&foo66877;
+__attribute__((used)) void* use66878 = (void*)&foo66878;
+__attribute__((used)) void* use66879 = (void*)&foo66879;
+__attribute__((used)) void* use66880 = (void*)&foo66880;
+__attribute__((used)) void* use66881 = (void*)&foo66881;
+__attribute__((used)) void* use66882 = (void*)&foo66882;
+__attribute__((used)) void* use66883 = (void*)&foo66883;
+__attribute__((used)) void* use66884 = (void*)&foo66884;
+__attribute__((used)) void* use66885 = (void*)&foo66885;
+__attribute__((used)) void* use66886 = (void*)&foo66886;
+__attribute__((used)) void* use66887 = (void*)&foo66887;
+__attribute__((used)) void* use66888 = (void*)&foo66888;
+__attribute__((used)) void* use66889 = (void*)&foo66889;
+__attribute__((used)) void* use66890 = (void*)&foo66890;
+__attribute__((used)) void* use66891 = (void*)&foo66891;
+__attribute__((used)) void* use66892 = (void*)&foo66892;
+__attribute__((used)) void* use66893 = (void*)&foo66893;
+__attribute__((used)) void* use66894 = (void*)&foo66894;
+__attribute__((used)) void* use66895 = (void*)&foo66895;
+__attribute__((used)) void* use66896 = (void*)&foo66896;
+__attribute__((used)) void* use66897 = (void*)&foo66897;
+__attribute__((used)) void* use66898 = (void*)&foo66898;
+__attribute__((used)) void* use66899 = (void*)&foo66899;
+__attribute__((used)) void* use66900 = (void*)&foo66900;
+__attribute__((used)) void* use66901 = (void*)&foo66901;
+__attribute__((used)) void* use66902 = (void*)&foo66902;
+__attribute__((used)) void* use66903 = (void*)&foo66903;
+__attribute__((used)) void* use66904 = (void*)&foo66904;
+__attribute__((used)) void* use66905 = (void*)&foo66905;
+__attribute__((used)) void* use66906 = (void*)&foo66906;
+__attribute__((used)) void* use66907 = (void*)&foo66907;
+__attribute__((used)) void* use66908 = (void*)&foo66908;
+__attribute__((used)) void* use66909 = (void*)&foo66909;
+__attribute__((used)) void* use66910 = (void*)&foo66910;
+__attribute__((used)) void* use66911 = (void*)&foo66911;
+__attribute__((used)) void* use66912 = (void*)&foo66912;
+__attribute__((used)) void* use66913 = (void*)&foo66913;
+__attribute__((used)) void* use66914 = (void*)&foo66914;
+__attribute__((used)) void* use66915 = (void*)&foo66915;
+__attribute__((used)) void* use66916 = (void*)&foo66916;
+__attribute__((used)) void* use66917 = (void*)&foo66917;
+__attribute__((used)) void* use66918 = (void*)&foo66918;
+__attribute__((used)) void* use66919 = (void*)&foo66919;
+__attribute__((used)) void* use66920 = (void*)&foo66920;
+__attribute__((used)) void* use66921 = (void*)&foo66921;
+__attribute__((used)) void* use66922 = (void*)&foo66922;
+__attribute__((used)) void* use66923 = (void*)&foo66923;
+__attribute__((used)) void* use66924 = (void*)&foo66924;
+__attribute__((used)) void* use66925 = (void*)&foo66925;
+__attribute__((used)) void* use66926 = (void*)&foo66926;
+__attribute__((used)) void* use66927 = (void*)&foo66927;
+__attribute__((used)) void* use66928 = (void*)&foo66928;
+__attribute__((used)) void* use66929 = (void*)&foo66929;
+__attribute__((used)) void* use66930 = (void*)&foo66930;
+__attribute__((used)) void* use66931 = (void*)&foo66931;
+__attribute__((used)) void* use66932 = (void*)&foo66932;
+__attribute__((used)) void* use66933 = (void*)&foo66933;
+__attribute__((used)) void* use66934 = (void*)&foo66934;
+__attribute__((used)) void* use66935 = (void*)&foo66935;
+__attribute__((used)) void* use66936 = (void*)&foo66936;
+__attribute__((used)) void* use66937 = (void*)&foo66937;
+__attribute__((used)) void* use66938 = (void*)&foo66938;
+__attribute__((used)) void* use66939 = (void*)&foo66939;
+__attribute__((used)) void* use66940 = (void*)&foo66940;
+__attribute__((used)) void* use66941 = (void*)&foo66941;
+__attribute__((used)) void* use66942 = (void*)&foo66942;
+__attribute__((used)) void* use66943 = (void*)&foo66943;
+__attribute__((used)) void* use66944 = (void*)&foo66944;
+__attribute__((used)) void* use66945 = (void*)&foo66945;
+__attribute__((used)) void* use66946 = (void*)&foo66946;
+__attribute__((used)) void* use66947 = (void*)&foo66947;
+__attribute__((used)) void* use66948 = (void*)&foo66948;
+__attribute__((used)) void* use66949 = (void*)&foo66949;
+__attribute__((used)) void* use66950 = (void*)&foo66950;
+__attribute__((used)) void* use66951 = (void*)&foo66951;
+__attribute__((used)) void* use66952 = (void*)&foo66952;
+__attribute__((used)) void* use66953 = (void*)&foo66953;
+__attribute__((used)) void* use66954 = (void*)&foo66954;
+__attribute__((used)) void* use66955 = (void*)&foo66955;
+__attribute__((used)) void* use66956 = (void*)&foo66956;
+__attribute__((used)) void* use66957 = (void*)&foo66957;
+__attribute__((used)) void* use66958 = (void*)&foo66958;
+__attribute__((used)) void* use66959 = (void*)&foo66959;
+__attribute__((used)) void* use66960 = (void*)&foo66960;
+__attribute__((used)) void* use66961 = (void*)&foo66961;
+__attribute__((used)) void* use66962 = (void*)&foo66962;
+__attribute__((used)) void* use66963 = (void*)&foo66963;
+__attribute__((used)) void* use66964 = (void*)&foo66964;
+__attribute__((used)) void* use66965 = (void*)&foo66965;
+__attribute__((used)) void* use66966 = (void*)&foo66966;
+__attribute__((used)) void* use66967 = (void*)&foo66967;
+__attribute__((used)) void* use66968 = (void*)&foo66968;
+__attribute__((used)) void* use66969 = (void*)&foo66969;
+__attribute__((used)) void* use66970 = (void*)&foo66970;
+__attribute__((used)) void* use66971 = (void*)&foo66971;
+__attribute__((used)) void* use66972 = (void*)&foo66972;
+__attribute__((used)) void* use66973 = (void*)&foo66973;
+__attribute__((used)) void* use66974 = (void*)&foo66974;
+__attribute__((used)) void* use66975 = (void*)&foo66975;
+__attribute__((used)) void* use66976 = (void*)&foo66976;
+__attribute__((used)) void* use66977 = (void*)&foo66977;
+__attribute__((used)) void* use66978 = (void*)&foo66978;
+__attribute__((used)) void* use66979 = (void*)&foo66979;
+__attribute__((used)) void* use66980 = (void*)&foo66980;
+__attribute__((used)) void* use66981 = (void*)&foo66981;
+__attribute__((used)) void* use66982 = (void*)&foo66982;
+__attribute__((used)) void* use66983 = (void*)&foo66983;
+__attribute__((used)) void* use66984 = (void*)&foo66984;
+__attribute__((used)) void* use66985 = (void*)&foo66985;
+__attribute__((used)) void* use66986 = (void*)&foo66986;
+__attribute__((used)) void* use66987 = (void*)&foo66987;
+__attribute__((used)) void* use66988 = (void*)&foo66988;
+__attribute__((used)) void* use66989 = (void*)&foo66989;
+__attribute__((used)) void* use66990 = (void*)&foo66990;
+__attribute__((used)) void* use66991 = (void*)&foo66991;
+__attribute__((used)) void* use66992 = (void*)&foo66992;
+__attribute__((used)) void* use66993 = (void*)&foo66993;
+__attribute__((used)) void* use66994 = (void*)&foo66994;
+__attribute__((used)) void* use66995 = (void*)&foo66995;
+__attribute__((used)) void* use66996 = (void*)&foo66996;
+__attribute__((used)) void* use66997 = (void*)&foo66997;
+__attribute__((used)) void* use66998 = (void*)&foo66998;
+__attribute__((used)) void* use66999 = (void*)&foo66999;
+__attribute__((used)) void* use67000 = (void*)&foo67000;
+__attribute__((used)) void* use67001 = (void*)&foo67001;
+__attribute__((used)) void* use67002 = (void*)&foo67002;
+__attribute__((used)) void* use67003 = (void*)&foo67003;
+__attribute__((used)) void* use67004 = (void*)&foo67004;
+__attribute__((used)) void* use67005 = (void*)&foo67005;
+__attribute__((used)) void* use67006 = (void*)&foo67006;
+__attribute__((used)) void* use67007 = (void*)&foo67007;
+__attribute__((used)) void* use67008 = (void*)&foo67008;
+__attribute__((used)) void* use67009 = (void*)&foo67009;
+__attribute__((used)) void* use67010 = (void*)&foo67010;
+__attribute__((used)) void* use67011 = (void*)&foo67011;
+__attribute__((used)) void* use67012 = (void*)&foo67012;
+__attribute__((used)) void* use67013 = (void*)&foo67013;
+__attribute__((used)) void* use67014 = (void*)&foo67014;
+__attribute__((used)) void* use67015 = (void*)&foo67015;
+__attribute__((used)) void* use67016 = (void*)&foo67016;
+__attribute__((used)) void* use67017 = (void*)&foo67017;
+__attribute__((used)) void* use67018 = (void*)&foo67018;
+__attribute__((used)) void* use67019 = (void*)&foo67019;
+__attribute__((used)) void* use67020 = (void*)&foo67020;
+__attribute__((used)) void* use67021 = (void*)&foo67021;
+__attribute__((used)) void* use67022 = (void*)&foo67022;
+__attribute__((used)) void* use67023 = (void*)&foo67023;
+__attribute__((used)) void* use67024 = (void*)&foo67024;
+__attribute__((used)) void* use67025 = (void*)&foo67025;
+__attribute__((used)) void* use67026 = (void*)&foo67026;
+__attribute__((used)) void* use67027 = (void*)&foo67027;
+__attribute__((used)) void* use67028 = (void*)&foo67028;
+__attribute__((used)) void* use67029 = (void*)&foo67029;
+__attribute__((used)) void* use67030 = (void*)&foo67030;
+__attribute__((used)) void* use67031 = (void*)&foo67031;
+__attribute__((used)) void* use67032 = (void*)&foo67032;
+__attribute__((used)) void* use67033 = (void*)&foo67033;
+__attribute__((used)) void* use67034 = (void*)&foo67034;
+__attribute__((used)) void* use67035 = (void*)&foo67035;
+__attribute__((used)) void* use67036 = (void*)&foo67036;
+__attribute__((used)) void* use67037 = (void*)&foo67037;
+__attribute__((used)) void* use67038 = (void*)&foo67038;
+__attribute__((used)) void* use67039 = (void*)&foo67039;
+__attribute__((used)) void* use67040 = (void*)&foo67040;
+__attribute__((used)) void* use67041 = (void*)&foo67041;
+__attribute__((used)) void* use67042 = (void*)&foo67042;
+__attribute__((used)) void* use67043 = (void*)&foo67043;
+__attribute__((used)) void* use67044 = (void*)&foo67044;
+__attribute__((used)) void* use67045 = (void*)&foo67045;
+__attribute__((used)) void* use67046 = (void*)&foo67046;
+__attribute__((used)) void* use67047 = (void*)&foo67047;
+__attribute__((used)) void* use67048 = (void*)&foo67048;
+__attribute__((used)) void* use67049 = (void*)&foo67049;
+__attribute__((used)) void* use67050 = (void*)&foo67050;
+__attribute__((used)) void* use67051 = (void*)&foo67051;
+__attribute__((used)) void* use67052 = (void*)&foo67052;
+__attribute__((used)) void* use67053 = (void*)&foo67053;
+__attribute__((used)) void* use67054 = (void*)&foo67054;
+__attribute__((used)) void* use67055 = (void*)&foo67055;
+__attribute__((used)) void* use67056 = (void*)&foo67056;
+__attribute__((used)) void* use67057 = (void*)&foo67057;
+__attribute__((used)) void* use67058 = (void*)&foo67058;
+__attribute__((used)) void* use67059 = (void*)&foo67059;
+__attribute__((used)) void* use67060 = (void*)&foo67060;
+__attribute__((used)) void* use67061 = (void*)&foo67061;
+__attribute__((used)) void* use67062 = (void*)&foo67062;
+__attribute__((used)) void* use67063 = (void*)&foo67063;
+__attribute__((used)) void* use67064 = (void*)&foo67064;
+__attribute__((used)) void* use67065 = (void*)&foo67065;
+__attribute__((used)) void* use67066 = (void*)&foo67066;
+__attribute__((used)) void* use67067 = (void*)&foo67067;
+__attribute__((used)) void* use67068 = (void*)&foo67068;
+__attribute__((used)) void* use67069 = (void*)&foo67069;
+__attribute__((used)) void* use67070 = (void*)&foo67070;
+__attribute__((used)) void* use67071 = (void*)&foo67071;
+__attribute__((used)) void* use67072 = (void*)&foo67072;
+__attribute__((used)) void* use67073 = (void*)&foo67073;
+__attribute__((used)) void* use67074 = (void*)&foo67074;
+__attribute__((used)) void* use67075 = (void*)&foo67075;
+__attribute__((used)) void* use67076 = (void*)&foo67076;
+__attribute__((used)) void* use67077 = (void*)&foo67077;
+__attribute__((used)) void* use67078 = (void*)&foo67078;
+__attribute__((used)) void* use67079 = (void*)&foo67079;
+__attribute__((used)) void* use67080 = (void*)&foo67080;
+__attribute__((used)) void* use67081 = (void*)&foo67081;
+__attribute__((used)) void* use67082 = (void*)&foo67082;
+__attribute__((used)) void* use67083 = (void*)&foo67083;
+__attribute__((used)) void* use67084 = (void*)&foo67084;
+__attribute__((used)) void* use67085 = (void*)&foo67085;
+__attribute__((used)) void* use67086 = (void*)&foo67086;
+__attribute__((used)) void* use67087 = (void*)&foo67087;
+__attribute__((used)) void* use67088 = (void*)&foo67088;
+__attribute__((used)) void* use67089 = (void*)&foo67089;
+__attribute__((used)) void* use67090 = (void*)&foo67090;
+__attribute__((used)) void* use67091 = (void*)&foo67091;
+__attribute__((used)) void* use67092 = (void*)&foo67092;
+__attribute__((used)) void* use67093 = (void*)&foo67093;
+__attribute__((used)) void* use67094 = (void*)&foo67094;
+__attribute__((used)) void* use67095 = (void*)&foo67095;
+__attribute__((used)) void* use67096 = (void*)&foo67096;
+__attribute__((used)) void* use67097 = (void*)&foo67097;
+__attribute__((used)) void* use67098 = (void*)&foo67098;
+__attribute__((used)) void* use67099 = (void*)&foo67099;
+__attribute__((used)) void* use67100 = (void*)&foo67100;
+__attribute__((used)) void* use67101 = (void*)&foo67101;
+__attribute__((used)) void* use67102 = (void*)&foo67102;
+__attribute__((used)) void* use67103 = (void*)&foo67103;
+__attribute__((used)) void* use67104 = (void*)&foo67104;
+__attribute__((used)) void* use67105 = (void*)&foo67105;
+__attribute__((used)) void* use67106 = (void*)&foo67106;
+__attribute__((used)) void* use67107 = (void*)&foo67107;
+__attribute__((used)) void* use67108 = (void*)&foo67108;
+__attribute__((used)) void* use67109 = (void*)&foo67109;
+__attribute__((used)) void* use67110 = (void*)&foo67110;
+__attribute__((used)) void* use67111 = (void*)&foo67111;
+__attribute__((used)) void* use67112 = (void*)&foo67112;
+__attribute__((used)) void* use67113 = (void*)&foo67113;
+__attribute__((used)) void* use67114 = (void*)&foo67114;
+__attribute__((used)) void* use67115 = (void*)&foo67115;
+__attribute__((used)) void* use67116 = (void*)&foo67116;
+__attribute__((used)) void* use67117 = (void*)&foo67117;
+__attribute__((used)) void* use67118 = (void*)&foo67118;
+__attribute__((used)) void* use67119 = (void*)&foo67119;
+__attribute__((used)) void* use67120 = (void*)&foo67120;
+__attribute__((used)) void* use67121 = (void*)&foo67121;
+__attribute__((used)) void* use67122 = (void*)&foo67122;
+__attribute__((used)) void* use67123 = (void*)&foo67123;
+__attribute__((used)) void* use67124 = (void*)&foo67124;
+__attribute__((used)) void* use67125 = (void*)&foo67125;
+__attribute__((used)) void* use67126 = (void*)&foo67126;
+__attribute__((used)) void* use67127 = (void*)&foo67127;
+__attribute__((used)) void* use67128 = (void*)&foo67128;
+__attribute__((used)) void* use67129 = (void*)&foo67129;
+__attribute__((used)) void* use67130 = (void*)&foo67130;
+__attribute__((used)) void* use67131 = (void*)&foo67131;
+__attribute__((used)) void* use67132 = (void*)&foo67132;
+__attribute__((used)) void* use67133 = (void*)&foo67133;
+__attribute__((used)) void* use67134 = (void*)&foo67134;
+__attribute__((used)) void* use67135 = (void*)&foo67135;
+__attribute__((used)) void* use67136 = (void*)&foo67136;
+__attribute__((used)) void* use67137 = (void*)&foo67137;
+__attribute__((used)) void* use67138 = (void*)&foo67138;
+__attribute__((used)) void* use67139 = (void*)&foo67139;
+__attribute__((used)) void* use67140 = (void*)&foo67140;
+__attribute__((used)) void* use67141 = (void*)&foo67141;
+__attribute__((used)) void* use67142 = (void*)&foo67142;
+__attribute__((used)) void* use67143 = (void*)&foo67143;
+__attribute__((used)) void* use67144 = (void*)&foo67144;
+__attribute__((used)) void* use67145 = (void*)&foo67145;
+__attribute__((used)) void* use67146 = (void*)&foo67146;
+__attribute__((used)) void* use67147 = (void*)&foo67147;
+__attribute__((used)) void* use67148 = (void*)&foo67148;
+__attribute__((used)) void* use67149 = (void*)&foo67149;
+__attribute__((used)) void* use67150 = (void*)&foo67150;
+__attribute__((used)) void* use67151 = (void*)&foo67151;
+__attribute__((used)) void* use67152 = (void*)&foo67152;
+__attribute__((used)) void* use67153 = (void*)&foo67153;
+__attribute__((used)) void* use67154 = (void*)&foo67154;
+__attribute__((used)) void* use67155 = (void*)&foo67155;
+__attribute__((used)) void* use67156 = (void*)&foo67156;
+__attribute__((used)) void* use67157 = (void*)&foo67157;
+__attribute__((used)) void* use67158 = (void*)&foo67158;
+__attribute__((used)) void* use67159 = (void*)&foo67159;
+__attribute__((used)) void* use67160 = (void*)&foo67160;
+__attribute__((used)) void* use67161 = (void*)&foo67161;
+__attribute__((used)) void* use67162 = (void*)&foo67162;
+__attribute__((used)) void* use67163 = (void*)&foo67163;
+__attribute__((used)) void* use67164 = (void*)&foo67164;
+__attribute__((used)) void* use67165 = (void*)&foo67165;
+__attribute__((used)) void* use67166 = (void*)&foo67166;
+__attribute__((used)) void* use67167 = (void*)&foo67167;
+__attribute__((used)) void* use67168 = (void*)&foo67168;
+__attribute__((used)) void* use67169 = (void*)&foo67169;
+__attribute__((used)) void* use67170 = (void*)&foo67170;
+__attribute__((used)) void* use67171 = (void*)&foo67171;
+__attribute__((used)) void* use67172 = (void*)&foo67172;
+__attribute__((used)) void* use67173 = (void*)&foo67173;
+__attribute__((used)) void* use67174 = (void*)&foo67174;
+__attribute__((used)) void* use67175 = (void*)&foo67175;
+__attribute__((used)) void* use67176 = (void*)&foo67176;
+__attribute__((used)) void* use67177 = (void*)&foo67177;
+__attribute__((used)) void* use67178 = (void*)&foo67178;
+__attribute__((used)) void* use67179 = (void*)&foo67179;
+__attribute__((used)) void* use67180 = (void*)&foo67180;
+__attribute__((used)) void* use67181 = (void*)&foo67181;
+__attribute__((used)) void* use67182 = (void*)&foo67182;
+__attribute__((used)) void* use67183 = (void*)&foo67183;
+__attribute__((used)) void* use67184 = (void*)&foo67184;
+__attribute__((used)) void* use67185 = (void*)&foo67185;
+__attribute__((used)) void* use67186 = (void*)&foo67186;
+__attribute__((used)) void* use67187 = (void*)&foo67187;
+__attribute__((used)) void* use67188 = (void*)&foo67188;
+__attribute__((used)) void* use67189 = (void*)&foo67189;
+__attribute__((used)) void* use67190 = (void*)&foo67190;
+__attribute__((used)) void* use67191 = (void*)&foo67191;
+__attribute__((used)) void* use67192 = (void*)&foo67192;
+__attribute__((used)) void* use67193 = (void*)&foo67193;
+__attribute__((used)) void* use67194 = (void*)&foo67194;
+__attribute__((used)) void* use67195 = (void*)&foo67195;
+__attribute__((used)) void* use67196 = (void*)&foo67196;
+__attribute__((used)) void* use67197 = (void*)&foo67197;
+__attribute__((used)) void* use67198 = (void*)&foo67198;
+__attribute__((used)) void* use67199 = (void*)&foo67199;
+__attribute__((used)) void* use67200 = (void*)&foo67200;
+__attribute__((used)) void* use67201 = (void*)&foo67201;
+__attribute__((used)) void* use67202 = (void*)&foo67202;
+__attribute__((used)) void* use67203 = (void*)&foo67203;
+__attribute__((used)) void* use67204 = (void*)&foo67204;
+__attribute__((used)) void* use67205 = (void*)&foo67205;
+__attribute__((used)) void* use67206 = (void*)&foo67206;
+__attribute__((used)) void* use67207 = (void*)&foo67207;
+__attribute__((used)) void* use67208 = (void*)&foo67208;
+__attribute__((used)) void* use67209 = (void*)&foo67209;
+__attribute__((used)) void* use67210 = (void*)&foo67210;
+__attribute__((used)) void* use67211 = (void*)&foo67211;
+__attribute__((used)) void* use67212 = (void*)&foo67212;
+__attribute__((used)) void* use67213 = (void*)&foo67213;
+__attribute__((used)) void* use67214 = (void*)&foo67214;
+__attribute__((used)) void* use67215 = (void*)&foo67215;
+__attribute__((used)) void* use67216 = (void*)&foo67216;
+__attribute__((used)) void* use67217 = (void*)&foo67217;
+__attribute__((used)) void* use67218 = (void*)&foo67218;
+__attribute__((used)) void* use67219 = (void*)&foo67219;
+__attribute__((used)) void* use67220 = (void*)&foo67220;
+__attribute__((used)) void* use67221 = (void*)&foo67221;
+__attribute__((used)) void* use67222 = (void*)&foo67222;
+__attribute__((used)) void* use67223 = (void*)&foo67223;
+__attribute__((used)) void* use67224 = (void*)&foo67224;
+__attribute__((used)) void* use67225 = (void*)&foo67225;
+__attribute__((used)) void* use67226 = (void*)&foo67226;
+__attribute__((used)) void* use67227 = (void*)&foo67227;
+__attribute__((used)) void* use67228 = (void*)&foo67228;
+__attribute__((used)) void* use67229 = (void*)&foo67229;
+__attribute__((used)) void* use67230 = (void*)&foo67230;
+__attribute__((used)) void* use67231 = (void*)&foo67231;
+__attribute__((used)) void* use67232 = (void*)&foo67232;
+__attribute__((used)) void* use67233 = (void*)&foo67233;
+__attribute__((used)) void* use67234 = (void*)&foo67234;
+__attribute__((used)) void* use67235 = (void*)&foo67235;
+__attribute__((used)) void* use67236 = (void*)&foo67236;
+__attribute__((used)) void* use67237 = (void*)&foo67237;
+__attribute__((used)) void* use67238 = (void*)&foo67238;
+__attribute__((used)) void* use67239 = (void*)&foo67239;
+__attribute__((used)) void* use67240 = (void*)&foo67240;
+__attribute__((used)) void* use67241 = (void*)&foo67241;
+__attribute__((used)) void* use67242 = (void*)&foo67242;
+__attribute__((used)) void* use67243 = (void*)&foo67243;
+__attribute__((used)) void* use67244 = (void*)&foo67244;
+__attribute__((used)) void* use67245 = (void*)&foo67245;
+__attribute__((used)) void* use67246 = (void*)&foo67246;
+__attribute__((used)) void* use67247 = (void*)&foo67247;
+__attribute__((used)) void* use67248 = (void*)&foo67248;
+__attribute__((used)) void* use67249 = (void*)&foo67249;
+__attribute__((used)) void* use67250 = (void*)&foo67250;
+__attribute__((used)) void* use67251 = (void*)&foo67251;
+__attribute__((used)) void* use67252 = (void*)&foo67252;
+__attribute__((used)) void* use67253 = (void*)&foo67253;
+__attribute__((used)) void* use67254 = (void*)&foo67254;
+__attribute__((used)) void* use67255 = (void*)&foo67255;
+__attribute__((used)) void* use67256 = (void*)&foo67256;
+__attribute__((used)) void* use67257 = (void*)&foo67257;
+__attribute__((used)) void* use67258 = (void*)&foo67258;
+__attribute__((used)) void* use67259 = (void*)&foo67259;
+__attribute__((used)) void* use67260 = (void*)&foo67260;
+__attribute__((used)) void* use67261 = (void*)&foo67261;
+__attribute__((used)) void* use67262 = (void*)&foo67262;
+__attribute__((used)) void* use67263 = (void*)&foo67263;
+__attribute__((used)) void* use67264 = (void*)&foo67264;
+__attribute__((used)) void* use67265 = (void*)&foo67265;
+__attribute__((used)) void* use67266 = (void*)&foo67266;
+__attribute__((used)) void* use67267 = (void*)&foo67267;
+__attribute__((used)) void* use67268 = (void*)&foo67268;
+__attribute__((used)) void* use67269 = (void*)&foo67269;
+__attribute__((used)) void* use67270 = (void*)&foo67270;
+__attribute__((used)) void* use67271 = (void*)&foo67271;
+__attribute__((used)) void* use67272 = (void*)&foo67272;
+__attribute__((used)) void* use67273 = (void*)&foo67273;
+__attribute__((used)) void* use67274 = (void*)&foo67274;
+__attribute__((used)) void* use67275 = (void*)&foo67275;
+__attribute__((used)) void* use67276 = (void*)&foo67276;
+__attribute__((used)) void* use67277 = (void*)&foo67277;
+__attribute__((used)) void* use67278 = (void*)&foo67278;
+__attribute__((used)) void* use67279 = (void*)&foo67279;
+__attribute__((used)) void* use67280 = (void*)&foo67280;
+__attribute__((used)) void* use67281 = (void*)&foo67281;
+__attribute__((used)) void* use67282 = (void*)&foo67282;
+__attribute__((used)) void* use67283 = (void*)&foo67283;
+__attribute__((used)) void* use67284 = (void*)&foo67284;
+__attribute__((used)) void* use67285 = (void*)&foo67285;
+__attribute__((used)) void* use67286 = (void*)&foo67286;
+__attribute__((used)) void* use67287 = (void*)&foo67287;
+__attribute__((used)) void* use67288 = (void*)&foo67288;
+__attribute__((used)) void* use67289 = (void*)&foo67289;
+__attribute__((used)) void* use67290 = (void*)&foo67290;
+__attribute__((used)) void* use67291 = (void*)&foo67291;
+__attribute__((used)) void* use67292 = (void*)&foo67292;
+__attribute__((used)) void* use67293 = (void*)&foo67293;
+__attribute__((used)) void* use67294 = (void*)&foo67294;
+__attribute__((used)) void* use67295 = (void*)&foo67295;
+__attribute__((used)) void* use67296 = (void*)&foo67296;
+__attribute__((used)) void* use67297 = (void*)&foo67297;
+__attribute__((used)) void* use67298 = (void*)&foo67298;
+__attribute__((used)) void* use67299 = (void*)&foo67299;
+__attribute__((used)) void* use67300 = (void*)&foo67300;
+__attribute__((used)) void* use67301 = (void*)&foo67301;
+__attribute__((used)) void* use67302 = (void*)&foo67302;
+__attribute__((used)) void* use67303 = (void*)&foo67303;
+__attribute__((used)) void* use67304 = (void*)&foo67304;
+__attribute__((used)) void* use67305 = (void*)&foo67305;
+__attribute__((used)) void* use67306 = (void*)&foo67306;
+__attribute__((used)) void* use67307 = (void*)&foo67307;
+__attribute__((used)) void* use67308 = (void*)&foo67308;
+__attribute__((used)) void* use67309 = (void*)&foo67309;
+__attribute__((used)) void* use67310 = (void*)&foo67310;
+__attribute__((used)) void* use67311 = (void*)&foo67311;
+__attribute__((used)) void* use67312 = (void*)&foo67312;
+__attribute__((used)) void* use67313 = (void*)&foo67313;
+__attribute__((used)) void* use67314 = (void*)&foo67314;
+__attribute__((used)) void* use67315 = (void*)&foo67315;
+__attribute__((used)) void* use67316 = (void*)&foo67316;
+__attribute__((used)) void* use67317 = (void*)&foo67317;
+__attribute__((used)) void* use67318 = (void*)&foo67318;
+__attribute__((used)) void* use67319 = (void*)&foo67319;
+__attribute__((used)) void* use67320 = (void*)&foo67320;
+__attribute__((used)) void* use67321 = (void*)&foo67321;
+__attribute__((used)) void* use67322 = (void*)&foo67322;
+__attribute__((used)) void* use67323 = (void*)&foo67323;
+__attribute__((used)) void* use67324 = (void*)&foo67324;
+__attribute__((used)) void* use67325 = (void*)&foo67325;
+__attribute__((used)) void* use67326 = (void*)&foo67326;
+__attribute__((used)) void* use67327 = (void*)&foo67327;
+__attribute__((used)) void* use67328 = (void*)&foo67328;
+__attribute__((used)) void* use67329 = (void*)&foo67329;
+__attribute__((used)) void* use67330 = (void*)&foo67330;
+__attribute__((used)) void* use67331 = (void*)&foo67331;
+__attribute__((used)) void* use67332 = (void*)&foo67332;
+__attribute__((used)) void* use67333 = (void*)&foo67333;
+__attribute__((used)) void* use67334 = (void*)&foo67334;
+__attribute__((used)) void* use67335 = (void*)&foo67335;
+__attribute__((used)) void* use67336 = (void*)&foo67336;
+__attribute__((used)) void* use67337 = (void*)&foo67337;
+__attribute__((used)) void* use67338 = (void*)&foo67338;
+__attribute__((used)) void* use67339 = (void*)&foo67339;
+__attribute__((used)) void* use67340 = (void*)&foo67340;
+__attribute__((used)) void* use67341 = (void*)&foo67341;
+__attribute__((used)) void* use67342 = (void*)&foo67342;
+__attribute__((used)) void* use67343 = (void*)&foo67343;
+__attribute__((used)) void* use67344 = (void*)&foo67344;
+__attribute__((used)) void* use67345 = (void*)&foo67345;
+__attribute__((used)) void* use67346 = (void*)&foo67346;
+__attribute__((used)) void* use67347 = (void*)&foo67347;
+__attribute__((used)) void* use67348 = (void*)&foo67348;
+__attribute__((used)) void* use67349 = (void*)&foo67349;
+__attribute__((used)) void* use67350 = (void*)&foo67350;
+__attribute__((used)) void* use67351 = (void*)&foo67351;
+__attribute__((used)) void* use67352 = (void*)&foo67352;
+__attribute__((used)) void* use67353 = (void*)&foo67353;
+__attribute__((used)) void* use67354 = (void*)&foo67354;
+__attribute__((used)) void* use67355 = (void*)&foo67355;
+__attribute__((used)) void* use67356 = (void*)&foo67356;
+__attribute__((used)) void* use67357 = (void*)&foo67357;
+__attribute__((used)) void* use67358 = (void*)&foo67358;
+__attribute__((used)) void* use67359 = (void*)&foo67359;
+__attribute__((used)) void* use67360 = (void*)&foo67360;
+__attribute__((used)) void* use67361 = (void*)&foo67361;
+__attribute__((used)) void* use67362 = (void*)&foo67362;
+__attribute__((used)) void* use67363 = (void*)&foo67363;
+__attribute__((used)) void* use67364 = (void*)&foo67364;
+__attribute__((used)) void* use67365 = (void*)&foo67365;
+__attribute__((used)) void* use67366 = (void*)&foo67366;
+__attribute__((used)) void* use67367 = (void*)&foo67367;
+__attribute__((used)) void* use67368 = (void*)&foo67368;
+__attribute__((used)) void* use67369 = (void*)&foo67369;
+__attribute__((used)) void* use67370 = (void*)&foo67370;
+__attribute__((used)) void* use67371 = (void*)&foo67371;
+__attribute__((used)) void* use67372 = (void*)&foo67372;
+__attribute__((used)) void* use67373 = (void*)&foo67373;
+__attribute__((used)) void* use67374 = (void*)&foo67374;
+__attribute__((used)) void* use67375 = (void*)&foo67375;
+__attribute__((used)) void* use67376 = (void*)&foo67376;
+__attribute__((used)) void* use67377 = (void*)&foo67377;
+__attribute__((used)) void* use67378 = (void*)&foo67378;
+__attribute__((used)) void* use67379 = (void*)&foo67379;
+__attribute__((used)) void* use67380 = (void*)&foo67380;
+__attribute__((used)) void* use67381 = (void*)&foo67381;
+__attribute__((used)) void* use67382 = (void*)&foo67382;
+__attribute__((used)) void* use67383 = (void*)&foo67383;
+__attribute__((used)) void* use67384 = (void*)&foo67384;
+__attribute__((used)) void* use67385 = (void*)&foo67385;
+__attribute__((used)) void* use67386 = (void*)&foo67386;
+__attribute__((used)) void* use67387 = (void*)&foo67387;
+__attribute__((used)) void* use67388 = (void*)&foo67388;
+__attribute__((used)) void* use67389 = (void*)&foo67389;
+__attribute__((used)) void* use67390 = (void*)&foo67390;
+__attribute__((used)) void* use67391 = (void*)&foo67391;
+__attribute__((used)) void* use67392 = (void*)&foo67392;
+__attribute__((used)) void* use67393 = (void*)&foo67393;
+__attribute__((used)) void* use67394 = (void*)&foo67394;
+__attribute__((used)) void* use67395 = (void*)&foo67395;
+__attribute__((used)) void* use67396 = (void*)&foo67396;
+__attribute__((used)) void* use67397 = (void*)&foo67397;
+__attribute__((used)) void* use67398 = (void*)&foo67398;
+__attribute__((used)) void* use67399 = (void*)&foo67399;
+__attribute__((used)) void* use67400 = (void*)&foo67400;
+__attribute__((used)) void* use67401 = (void*)&foo67401;
+__attribute__((used)) void* use67402 = (void*)&foo67402;
+__attribute__((used)) void* use67403 = (void*)&foo67403;
+__attribute__((used)) void* use67404 = (void*)&foo67404;
+__attribute__((used)) void* use67405 = (void*)&foo67405;
+__attribute__((used)) void* use67406 = (void*)&foo67406;
+__attribute__((used)) void* use67407 = (void*)&foo67407;
+__attribute__((used)) void* use67408 = (void*)&foo67408;
+__attribute__((used)) void* use67409 = (void*)&foo67409;
+__attribute__((used)) void* use67410 = (void*)&foo67410;
+__attribute__((used)) void* use67411 = (void*)&foo67411;
+__attribute__((used)) void* use67412 = (void*)&foo67412;
+__attribute__((used)) void* use67413 = (void*)&foo67413;
+__attribute__((used)) void* use67414 = (void*)&foo67414;
+__attribute__((used)) void* use67415 = (void*)&foo67415;
+__attribute__((used)) void* use67416 = (void*)&foo67416;
+__attribute__((used)) void* use67417 = (void*)&foo67417;
+__attribute__((used)) void* use67418 = (void*)&foo67418;
+__attribute__((used)) void* use67419 = (void*)&foo67419;
+__attribute__((used)) void* use67420 = (void*)&foo67420;
+__attribute__((used)) void* use67421 = (void*)&foo67421;
+__attribute__((used)) void* use67422 = (void*)&foo67422;
+__attribute__((used)) void* use67423 = (void*)&foo67423;
+__attribute__((used)) void* use67424 = (void*)&foo67424;
+__attribute__((used)) void* use67425 = (void*)&foo67425;
+__attribute__((used)) void* use67426 = (void*)&foo67426;
+__attribute__((used)) void* use67427 = (void*)&foo67427;
+__attribute__((used)) void* use67428 = (void*)&foo67428;
+__attribute__((used)) void* use67429 = (void*)&foo67429;
+__attribute__((used)) void* use67430 = (void*)&foo67430;
+__attribute__((used)) void* use67431 = (void*)&foo67431;
+__attribute__((used)) void* use67432 = (void*)&foo67432;
+__attribute__((used)) void* use67433 = (void*)&foo67433;
+__attribute__((used)) void* use67434 = (void*)&foo67434;
+__attribute__((used)) void* use67435 = (void*)&foo67435;
+__attribute__((used)) void* use67436 = (void*)&foo67436;
+__attribute__((used)) void* use67437 = (void*)&foo67437;
+__attribute__((used)) void* use67438 = (void*)&foo67438;
+__attribute__((used)) void* use67439 = (void*)&foo67439;
+__attribute__((used)) void* use67440 = (void*)&foo67440;
+__attribute__((used)) void* use67441 = (void*)&foo67441;
+__attribute__((used)) void* use67442 = (void*)&foo67442;
+__attribute__((used)) void* use67443 = (void*)&foo67443;
+__attribute__((used)) void* use67444 = (void*)&foo67444;
+__attribute__((used)) void* use67445 = (void*)&foo67445;
+__attribute__((used)) void* use67446 = (void*)&foo67446;
+__attribute__((used)) void* use67447 = (void*)&foo67447;
+__attribute__((used)) void* use67448 = (void*)&foo67448;
+__attribute__((used)) void* use67449 = (void*)&foo67449;
+__attribute__((used)) void* use67450 = (void*)&foo67450;
+__attribute__((used)) void* use67451 = (void*)&foo67451;
+__attribute__((used)) void* use67452 = (void*)&foo67452;
+__attribute__((used)) void* use67453 = (void*)&foo67453;
+__attribute__((used)) void* use67454 = (void*)&foo67454;
+__attribute__((used)) void* use67455 = (void*)&foo67455;
+__attribute__((used)) void* use67456 = (void*)&foo67456;
+__attribute__((used)) void* use67457 = (void*)&foo67457;
+__attribute__((used)) void* use67458 = (void*)&foo67458;
+__attribute__((used)) void* use67459 = (void*)&foo67459;
+__attribute__((used)) void* use67460 = (void*)&foo67460;
+__attribute__((used)) void* use67461 = (void*)&foo67461;
+__attribute__((used)) void* use67462 = (void*)&foo67462;
+__attribute__((used)) void* use67463 = (void*)&foo67463;
+__attribute__((used)) void* use67464 = (void*)&foo67464;
+__attribute__((used)) void* use67465 = (void*)&foo67465;
+__attribute__((used)) void* use67466 = (void*)&foo67466;
+__attribute__((used)) void* use67467 = (void*)&foo67467;
+__attribute__((used)) void* use67468 = (void*)&foo67468;
+__attribute__((used)) void* use67469 = (void*)&foo67469;
+__attribute__((used)) void* use67470 = (void*)&foo67470;
+__attribute__((used)) void* use67471 = (void*)&foo67471;
+__attribute__((used)) void* use67472 = (void*)&foo67472;
+__attribute__((used)) void* use67473 = (void*)&foo67473;
+__attribute__((used)) void* use67474 = (void*)&foo67474;
+__attribute__((used)) void* use67475 = (void*)&foo67475;
+__attribute__((used)) void* use67476 = (void*)&foo67476;
+__attribute__((used)) void* use67477 = (void*)&foo67477;
+__attribute__((used)) void* use67478 = (void*)&foo67478;
+__attribute__((used)) void* use67479 = (void*)&foo67479;
+__attribute__((used)) void* use67480 = (void*)&foo67480;
+__attribute__((used)) void* use67481 = (void*)&foo67481;
+__attribute__((used)) void* use67482 = (void*)&foo67482;
+__attribute__((used)) void* use67483 = (void*)&foo67483;
+__attribute__((used)) void* use67484 = (void*)&foo67484;
+__attribute__((used)) void* use67485 = (void*)&foo67485;
+__attribute__((used)) void* use67486 = (void*)&foo67486;
+__attribute__((used)) void* use67487 = (void*)&foo67487;
+__attribute__((used)) void* use67488 = (void*)&foo67488;
+__attribute__((used)) void* use67489 = (void*)&foo67489;
+__attribute__((used)) void* use67490 = (void*)&foo67490;
+__attribute__((used)) void* use67491 = (void*)&foo67491;
+__attribute__((used)) void* use67492 = (void*)&foo67492;
+__attribute__((used)) void* use67493 = (void*)&foo67493;
+__attribute__((used)) void* use67494 = (void*)&foo67494;
+__attribute__((used)) void* use67495 = (void*)&foo67495;
+__attribute__((used)) void* use67496 = (void*)&foo67496;
+__attribute__((used)) void* use67497 = (void*)&foo67497;
+__attribute__((used)) void* use67498 = (void*)&foo67498;
+__attribute__((used)) void* use67499 = (void*)&foo67499;
+__attribute__((used)) void* use67500 = (void*)&foo67500;
+__attribute__((used)) void* use67501 = (void*)&foo67501;
+__attribute__((used)) void* use67502 = (void*)&foo67502;
+__attribute__((used)) void* use67503 = (void*)&foo67503;
+__attribute__((used)) void* use67504 = (void*)&foo67504;
+__attribute__((used)) void* use67505 = (void*)&foo67505;
+__attribute__((used)) void* use67506 = (void*)&foo67506;
+__attribute__((used)) void* use67507 = (void*)&foo67507;
+__attribute__((used)) void* use67508 = (void*)&foo67508;
+__attribute__((used)) void* use67509 = (void*)&foo67509;
+__attribute__((used)) void* use67510 = (void*)&foo67510;
+__attribute__((used)) void* use67511 = (void*)&foo67511;
+__attribute__((used)) void* use67512 = (void*)&foo67512;
+__attribute__((used)) void* use67513 = (void*)&foo67513;
+__attribute__((used)) void* use67514 = (void*)&foo67514;
+__attribute__((used)) void* use67515 = (void*)&foo67515;
+__attribute__((used)) void* use67516 = (void*)&foo67516;
+__attribute__((used)) void* use67517 = (void*)&foo67517;
+__attribute__((used)) void* use67518 = (void*)&foo67518;
+__attribute__((used)) void* use67519 = (void*)&foo67519;
+__attribute__((used)) void* use67520 = (void*)&foo67520;
+__attribute__((used)) void* use67521 = (void*)&foo67521;
+__attribute__((used)) void* use67522 = (void*)&foo67522;
+__attribute__((used)) void* use67523 = (void*)&foo67523;
+__attribute__((used)) void* use67524 = (void*)&foo67524;
+__attribute__((used)) void* use67525 = (void*)&foo67525;
+__attribute__((used)) void* use67526 = (void*)&foo67526;
+__attribute__((used)) void* use67527 = (void*)&foo67527;
+__attribute__((used)) void* use67528 = (void*)&foo67528;
+__attribute__((used)) void* use67529 = (void*)&foo67529;
+__attribute__((used)) void* use67530 = (void*)&foo67530;
+__attribute__((used)) void* use67531 = (void*)&foo67531;
+__attribute__((used)) void* use67532 = (void*)&foo67532;
+__attribute__((used)) void* use67533 = (void*)&foo67533;
+__attribute__((used)) void* use67534 = (void*)&foo67534;
+__attribute__((used)) void* use67535 = (void*)&foo67535;
+__attribute__((used)) void* use67536 = (void*)&foo67536;
+__attribute__((used)) void* use67537 = (void*)&foo67537;
+__attribute__((used)) void* use67538 = (void*)&foo67538;
+__attribute__((used)) void* use67539 = (void*)&foo67539;
+__attribute__((used)) void* use67540 = (void*)&foo67540;
+__attribute__((used)) void* use67541 = (void*)&foo67541;
+__attribute__((used)) void* use67542 = (void*)&foo67542;
+__attribute__((used)) void* use67543 = (void*)&foo67543;
+__attribute__((used)) void* use67544 = (void*)&foo67544;
+__attribute__((used)) void* use67545 = (void*)&foo67545;
+__attribute__((used)) void* use67546 = (void*)&foo67546;
+__attribute__((used)) void* use67547 = (void*)&foo67547;
+__attribute__((used)) void* use67548 = (void*)&foo67548;
+__attribute__((used)) void* use67549 = (void*)&foo67549;
+__attribute__((used)) void* use67550 = (void*)&foo67550;
+__attribute__((used)) void* use67551 = (void*)&foo67551;
+__attribute__((used)) void* use67552 = (void*)&foo67552;
+__attribute__((used)) void* use67553 = (void*)&foo67553;
+__attribute__((used)) void* use67554 = (void*)&foo67554;
+__attribute__((used)) void* use67555 = (void*)&foo67555;
+__attribute__((used)) void* use67556 = (void*)&foo67556;
+__attribute__((used)) void* use67557 = (void*)&foo67557;
+__attribute__((used)) void* use67558 = (void*)&foo67558;
+__attribute__((used)) void* use67559 = (void*)&foo67559;
+__attribute__((used)) void* use67560 = (void*)&foo67560;
+__attribute__((used)) void* use67561 = (void*)&foo67561;
+__attribute__((used)) void* use67562 = (void*)&foo67562;
+__attribute__((used)) void* use67563 = (void*)&foo67563;
+__attribute__((used)) void* use67564 = (void*)&foo67564;
+__attribute__((used)) void* use67565 = (void*)&foo67565;
+__attribute__((used)) void* use67566 = (void*)&foo67566;
+__attribute__((used)) void* use67567 = (void*)&foo67567;
+__attribute__((used)) void* use67568 = (void*)&foo67568;
+__attribute__((used)) void* use67569 = (void*)&foo67569;
+__attribute__((used)) void* use67570 = (void*)&foo67570;
+__attribute__((used)) void* use67571 = (void*)&foo67571;
+__attribute__((used)) void* use67572 = (void*)&foo67572;
+__attribute__((used)) void* use67573 = (void*)&foo67573;
+__attribute__((used)) void* use67574 = (void*)&foo67574;
+__attribute__((used)) void* use67575 = (void*)&foo67575;
+__attribute__((used)) void* use67576 = (void*)&foo67576;
+__attribute__((used)) void* use67577 = (void*)&foo67577;
+__attribute__((used)) void* use67578 = (void*)&foo67578;
+__attribute__((used)) void* use67579 = (void*)&foo67579;
+__attribute__((used)) void* use67580 = (void*)&foo67580;
+__attribute__((used)) void* use67581 = (void*)&foo67581;
+__attribute__((used)) void* use67582 = (void*)&foo67582;
+__attribute__((used)) void* use67583 = (void*)&foo67583;
+__attribute__((used)) void* use67584 = (void*)&foo67584;
+__attribute__((used)) void* use67585 = (void*)&foo67585;
+__attribute__((used)) void* use67586 = (void*)&foo67586;
+__attribute__((used)) void* use67587 = (void*)&foo67587;
+__attribute__((used)) void* use67588 = (void*)&foo67588;
+__attribute__((used)) void* use67589 = (void*)&foo67589;
+__attribute__((used)) void* use67590 = (void*)&foo67590;
+__attribute__((used)) void* use67591 = (void*)&foo67591;
+__attribute__((used)) void* use67592 = (void*)&foo67592;
+__attribute__((used)) void* use67593 = (void*)&foo67593;
+__attribute__((used)) void* use67594 = (void*)&foo67594;
+__attribute__((used)) void* use67595 = (void*)&foo67595;
+__attribute__((used)) void* use67596 = (void*)&foo67596;
+__attribute__((used)) void* use67597 = (void*)&foo67597;
+__attribute__((used)) void* use67598 = (void*)&foo67598;
+__attribute__((used)) void* use67599 = (void*)&foo67599;
+__attribute__((used)) void* use67600 = (void*)&foo67600;
+__attribute__((used)) void* use67601 = (void*)&foo67601;
+__attribute__((used)) void* use67602 = (void*)&foo67602;
+__attribute__((used)) void* use67603 = (void*)&foo67603;
+__attribute__((used)) void* use67604 = (void*)&foo67604;
+__attribute__((used)) void* use67605 = (void*)&foo67605;
+__attribute__((used)) void* use67606 = (void*)&foo67606;
+__attribute__((used)) void* use67607 = (void*)&foo67607;
+__attribute__((used)) void* use67608 = (void*)&foo67608;
+__attribute__((used)) void* use67609 = (void*)&foo67609;
+__attribute__((used)) void* use67610 = (void*)&foo67610;
+__attribute__((used)) void* use67611 = (void*)&foo67611;
+__attribute__((used)) void* use67612 = (void*)&foo67612;
+__attribute__((used)) void* use67613 = (void*)&foo67613;
+__attribute__((used)) void* use67614 = (void*)&foo67614;
+__attribute__((used)) void* use67615 = (void*)&foo67615;
+__attribute__((used)) void* use67616 = (void*)&foo67616;
+__attribute__((used)) void* use67617 = (void*)&foo67617;
+__attribute__((used)) void* use67618 = (void*)&foo67618;
+__attribute__((used)) void* use67619 = (void*)&foo67619;
+__attribute__((used)) void* use67620 = (void*)&foo67620;
+__attribute__((used)) void* use67621 = (void*)&foo67621;
+__attribute__((used)) void* use67622 = (void*)&foo67622;
+__attribute__((used)) void* use67623 = (void*)&foo67623;
+__attribute__((used)) void* use67624 = (void*)&foo67624;
+__attribute__((used)) void* use67625 = (void*)&foo67625;
+__attribute__((used)) void* use67626 = (void*)&foo67626;
+__attribute__((used)) void* use67627 = (void*)&foo67627;
+__attribute__((used)) void* use67628 = (void*)&foo67628;
+__attribute__((used)) void* use67629 = (void*)&foo67629;
+__attribute__((used)) void* use67630 = (void*)&foo67630;
+__attribute__((used)) void* use67631 = (void*)&foo67631;
+__attribute__((used)) void* use67632 = (void*)&foo67632;
+__attribute__((used)) void* use67633 = (void*)&foo67633;
+__attribute__((used)) void* use67634 = (void*)&foo67634;
+__attribute__((used)) void* use67635 = (void*)&foo67635;
+__attribute__((used)) void* use67636 = (void*)&foo67636;
+__attribute__((used)) void* use67637 = (void*)&foo67637;
+__attribute__((used)) void* use67638 = (void*)&foo67638;
+__attribute__((used)) void* use67639 = (void*)&foo67639;
+__attribute__((used)) void* use67640 = (void*)&foo67640;
+__attribute__((used)) void* use67641 = (void*)&foo67641;
+__attribute__((used)) void* use67642 = (void*)&foo67642;
+__attribute__((used)) void* use67643 = (void*)&foo67643;
+__attribute__((used)) void* use67644 = (void*)&foo67644;
+__attribute__((used)) void* use67645 = (void*)&foo67645;
+__attribute__((used)) void* use67646 = (void*)&foo67646;
+__attribute__((used)) void* use67647 = (void*)&foo67647;
+__attribute__((used)) void* use67648 = (void*)&foo67648;
+__attribute__((used)) void* use67649 = (void*)&foo67649;
+__attribute__((used)) void* use67650 = (void*)&foo67650;
+__attribute__((used)) void* use67651 = (void*)&foo67651;
+__attribute__((used)) void* use67652 = (void*)&foo67652;
+__attribute__((used)) void* use67653 = (void*)&foo67653;
+__attribute__((used)) void* use67654 = (void*)&foo67654;
+__attribute__((used)) void* use67655 = (void*)&foo67655;
+__attribute__((used)) void* use67656 = (void*)&foo67656;
+__attribute__((used)) void* use67657 = (void*)&foo67657;
+__attribute__((used)) void* use67658 = (void*)&foo67658;
+__attribute__((used)) void* use67659 = (void*)&foo67659;
+__attribute__((used)) void* use67660 = (void*)&foo67660;
+__attribute__((used)) void* use67661 = (void*)&foo67661;
+__attribute__((used)) void* use67662 = (void*)&foo67662;
+__attribute__((used)) void* use67663 = (void*)&foo67663;
+__attribute__((used)) void* use67664 = (void*)&foo67664;
+__attribute__((used)) void* use67665 = (void*)&foo67665;
+__attribute__((used)) void* use67666 = (void*)&foo67666;
+__attribute__((used)) void* use67667 = (void*)&foo67667;
+__attribute__((used)) void* use67668 = (void*)&foo67668;
+__attribute__((used)) void* use67669 = (void*)&foo67669;
+__attribute__((used)) void* use67670 = (void*)&foo67670;
+__attribute__((used)) void* use67671 = (void*)&foo67671;
+__attribute__((used)) void* use67672 = (void*)&foo67672;
+__attribute__((used)) void* use67673 = (void*)&foo67673;
+__attribute__((used)) void* use67674 = (void*)&foo67674;
+__attribute__((used)) void* use67675 = (void*)&foo67675;
+__attribute__((used)) void* use67676 = (void*)&foo67676;
+__attribute__((used)) void* use67677 = (void*)&foo67677;
+__attribute__((used)) void* use67678 = (void*)&foo67678;
+__attribute__((used)) void* use67679 = (void*)&foo67679;
+__attribute__((used)) void* use67680 = (void*)&foo67680;
+__attribute__((used)) void* use67681 = (void*)&foo67681;
+__attribute__((used)) void* use67682 = (void*)&foo67682;
+__attribute__((used)) void* use67683 = (void*)&foo67683;
+__attribute__((used)) void* use67684 = (void*)&foo67684;
+__attribute__((used)) void* use67685 = (void*)&foo67685;
+__attribute__((used)) void* use67686 = (void*)&foo67686;
+__attribute__((used)) void* use67687 = (void*)&foo67687;
+__attribute__((used)) void* use67688 = (void*)&foo67688;
+__attribute__((used)) void* use67689 = (void*)&foo67689;
+__attribute__((used)) void* use67690 = (void*)&foo67690;
+__attribute__((used)) void* use67691 = (void*)&foo67691;
+__attribute__((used)) void* use67692 = (void*)&foo67692;
+__attribute__((used)) void* use67693 = (void*)&foo67693;
+__attribute__((used)) void* use67694 = (void*)&foo67694;
+__attribute__((used)) void* use67695 = (void*)&foo67695;
+__attribute__((used)) void* use67696 = (void*)&foo67696;
+__attribute__((used)) void* use67697 = (void*)&foo67697;
+__attribute__((used)) void* use67698 = (void*)&foo67698;
+__attribute__((used)) void* use67699 = (void*)&foo67699;
+__attribute__((used)) void* use67700 = (void*)&foo67700;
+__attribute__((used)) void* use67701 = (void*)&foo67701;
+__attribute__((used)) void* use67702 = (void*)&foo67702;
+__attribute__((used)) void* use67703 = (void*)&foo67703;
+__attribute__((used)) void* use67704 = (void*)&foo67704;
+__attribute__((used)) void* use67705 = (void*)&foo67705;
+__attribute__((used)) void* use67706 = (void*)&foo67706;
+__attribute__((used)) void* use67707 = (void*)&foo67707;
+__attribute__((used)) void* use67708 = (void*)&foo67708;
+__attribute__((used)) void* use67709 = (void*)&foo67709;
+__attribute__((used)) void* use67710 = (void*)&foo67710;
+__attribute__((used)) void* use67711 = (void*)&foo67711;
+__attribute__((used)) void* use67712 = (void*)&foo67712;
+__attribute__((used)) void* use67713 = (void*)&foo67713;
+__attribute__((used)) void* use67714 = (void*)&foo67714;
+__attribute__((used)) void* use67715 = (void*)&foo67715;
+__attribute__((used)) void* use67716 = (void*)&foo67716;
+__attribute__((used)) void* use67717 = (void*)&foo67717;
+__attribute__((used)) void* use67718 = (void*)&foo67718;
+__attribute__((used)) void* use67719 = (void*)&foo67719;
+__attribute__((used)) void* use67720 = (void*)&foo67720;
+__attribute__((used)) void* use67721 = (void*)&foo67721;
+__attribute__((used)) void* use67722 = (void*)&foo67722;
+__attribute__((used)) void* use67723 = (void*)&foo67723;
+__attribute__((used)) void* use67724 = (void*)&foo67724;
+__attribute__((used)) void* use67725 = (void*)&foo67725;
+__attribute__((used)) void* use67726 = (void*)&foo67726;
+__attribute__((used)) void* use67727 = (void*)&foo67727;
+__attribute__((used)) void* use67728 = (void*)&foo67728;
+__attribute__((used)) void* use67729 = (void*)&foo67729;
+__attribute__((used)) void* use67730 = (void*)&foo67730;
+__attribute__((used)) void* use67731 = (void*)&foo67731;
+__attribute__((used)) void* use67732 = (void*)&foo67732;
+__attribute__((used)) void* use67733 = (void*)&foo67733;
+__attribute__((used)) void* use67734 = (void*)&foo67734;
+__attribute__((used)) void* use67735 = (void*)&foo67735;
+__attribute__((used)) void* use67736 = (void*)&foo67736;
+__attribute__((used)) void* use67737 = (void*)&foo67737;
+__attribute__((used)) void* use67738 = (void*)&foo67738;
+__attribute__((used)) void* use67739 = (void*)&foo67739;
+__attribute__((used)) void* use67740 = (void*)&foo67740;
+__attribute__((used)) void* use67741 = (void*)&foo67741;
+__attribute__((used)) void* use67742 = (void*)&foo67742;
+__attribute__((used)) void* use67743 = (void*)&foo67743;
+__attribute__((used)) void* use67744 = (void*)&foo67744;
+__attribute__((used)) void* use67745 = (void*)&foo67745;
+__attribute__((used)) void* use67746 = (void*)&foo67746;
+__attribute__((used)) void* use67747 = (void*)&foo67747;
+__attribute__((used)) void* use67748 = (void*)&foo67748;
+__attribute__((used)) void* use67749 = (void*)&foo67749;
+__attribute__((used)) void* use67750 = (void*)&foo67750;
+__attribute__((used)) void* use67751 = (void*)&foo67751;
+__attribute__((used)) void* use67752 = (void*)&foo67752;
+__attribute__((used)) void* use67753 = (void*)&foo67753;
+__attribute__((used)) void* use67754 = (void*)&foo67754;
+__attribute__((used)) void* use67755 = (void*)&foo67755;
+__attribute__((used)) void* use67756 = (void*)&foo67756;
+__attribute__((used)) void* use67757 = (void*)&foo67757;
+__attribute__((used)) void* use67758 = (void*)&foo67758;
+__attribute__((used)) void* use67759 = (void*)&foo67759;
+__attribute__((used)) void* use67760 = (void*)&foo67760;
+__attribute__((used)) void* use67761 = (void*)&foo67761;
+__attribute__((used)) void* use67762 = (void*)&foo67762;
+__attribute__((used)) void* use67763 = (void*)&foo67763;
+__attribute__((used)) void* use67764 = (void*)&foo67764;
+__attribute__((used)) void* use67765 = (void*)&foo67765;
+__attribute__((used)) void* use67766 = (void*)&foo67766;
+__attribute__((used)) void* use67767 = (void*)&foo67767;
+__attribute__((used)) void* use67768 = (void*)&foo67768;
+__attribute__((used)) void* use67769 = (void*)&foo67769;
+__attribute__((used)) void* use67770 = (void*)&foo67770;
+__attribute__((used)) void* use67771 = (void*)&foo67771;
+__attribute__((used)) void* use67772 = (void*)&foo67772;
+__attribute__((used)) void* use67773 = (void*)&foo67773;
+__attribute__((used)) void* use67774 = (void*)&foo67774;
+__attribute__((used)) void* use67775 = (void*)&foo67775;
+__attribute__((used)) void* use67776 = (void*)&foo67776;
+__attribute__((used)) void* use67777 = (void*)&foo67777;
+__attribute__((used)) void* use67778 = (void*)&foo67778;
+__attribute__((used)) void* use67779 = (void*)&foo67779;
+__attribute__((used)) void* use67780 = (void*)&foo67780;
+__attribute__((used)) void* use67781 = (void*)&foo67781;
+__attribute__((used)) void* use67782 = (void*)&foo67782;
+__attribute__((used)) void* use67783 = (void*)&foo67783;
+__attribute__((used)) void* use67784 = (void*)&foo67784;
+__attribute__((used)) void* use67785 = (void*)&foo67785;
+__attribute__((used)) void* use67786 = (void*)&foo67786;
+__attribute__((used)) void* use67787 = (void*)&foo67787;
+__attribute__((used)) void* use67788 = (void*)&foo67788;
+__attribute__((used)) void* use67789 = (void*)&foo67789;
+__attribute__((used)) void* use67790 = (void*)&foo67790;
+__attribute__((used)) void* use67791 = (void*)&foo67791;
+__attribute__((used)) void* use67792 = (void*)&foo67792;
+__attribute__((used)) void* use67793 = (void*)&foo67793;
+__attribute__((used)) void* use67794 = (void*)&foo67794;
+__attribute__((used)) void* use67795 = (void*)&foo67795;
+__attribute__((used)) void* use67796 = (void*)&foo67796;
+__attribute__((used)) void* use67797 = (void*)&foo67797;
+__attribute__((used)) void* use67798 = (void*)&foo67798;
+__attribute__((used)) void* use67799 = (void*)&foo67799;
+__attribute__((used)) void* use67800 = (void*)&foo67800;
+__attribute__((used)) void* use67801 = (void*)&foo67801;
+__attribute__((used)) void* use67802 = (void*)&foo67802;
+__attribute__((used)) void* use67803 = (void*)&foo67803;
+__attribute__((used)) void* use67804 = (void*)&foo67804;
+__attribute__((used)) void* use67805 = (void*)&foo67805;
+__attribute__((used)) void* use67806 = (void*)&foo67806;
+__attribute__((used)) void* use67807 = (void*)&foo67807;
+__attribute__((used)) void* use67808 = (void*)&foo67808;
+__attribute__((used)) void* use67809 = (void*)&foo67809;
+__attribute__((used)) void* use67810 = (void*)&foo67810;
+__attribute__((used)) void* use67811 = (void*)&foo67811;
+__attribute__((used)) void* use67812 = (void*)&foo67812;
+__attribute__((used)) void* use67813 = (void*)&foo67813;
+__attribute__((used)) void* use67814 = (void*)&foo67814;
+__attribute__((used)) void* use67815 = (void*)&foo67815;
+__attribute__((used)) void* use67816 = (void*)&foo67816;
+__attribute__((used)) void* use67817 = (void*)&foo67817;
+__attribute__((used)) void* use67818 = (void*)&foo67818;
+__attribute__((used)) void* use67819 = (void*)&foo67819;
+__attribute__((used)) void* use67820 = (void*)&foo67820;
+__attribute__((used)) void* use67821 = (void*)&foo67821;
+__attribute__((used)) void* use67822 = (void*)&foo67822;
+__attribute__((used)) void* use67823 = (void*)&foo67823;
+__attribute__((used)) void* use67824 = (void*)&foo67824;
+__attribute__((used)) void* use67825 = (void*)&foo67825;
+__attribute__((used)) void* use67826 = (void*)&foo67826;
+__attribute__((used)) void* use67827 = (void*)&foo67827;
+__attribute__((used)) void* use67828 = (void*)&foo67828;
+__attribute__((used)) void* use67829 = (void*)&foo67829;
+__attribute__((used)) void* use67830 = (void*)&foo67830;
+__attribute__((used)) void* use67831 = (void*)&foo67831;
+__attribute__((used)) void* use67832 = (void*)&foo67832;
+__attribute__((used)) void* use67833 = (void*)&foo67833;
+__attribute__((used)) void* use67834 = (void*)&foo67834;
+__attribute__((used)) void* use67835 = (void*)&foo67835;
+__attribute__((used)) void* use67836 = (void*)&foo67836;
+__attribute__((used)) void* use67837 = (void*)&foo67837;
+__attribute__((used)) void* use67838 = (void*)&foo67838;
+__attribute__((used)) void* use67839 = (void*)&foo67839;
+__attribute__((used)) void* use67840 = (void*)&foo67840;
+__attribute__((used)) void* use67841 = (void*)&foo67841;
+__attribute__((used)) void* use67842 = (void*)&foo67842;
+__attribute__((used)) void* use67843 = (void*)&foo67843;
+__attribute__((used)) void* use67844 = (void*)&foo67844;
+__attribute__((used)) void* use67845 = (void*)&foo67845;
+__attribute__((used)) void* use67846 = (void*)&foo67846;
+__attribute__((used)) void* use67847 = (void*)&foo67847;
+__attribute__((used)) void* use67848 = (void*)&foo67848;
+__attribute__((used)) void* use67849 = (void*)&foo67849;
+__attribute__((used)) void* use67850 = (void*)&foo67850;
+__attribute__((used)) void* use67851 = (void*)&foo67851;
+__attribute__((used)) void* use67852 = (void*)&foo67852;
+__attribute__((used)) void* use67853 = (void*)&foo67853;
+__attribute__((used)) void* use67854 = (void*)&foo67854;
+__attribute__((used)) void* use67855 = (void*)&foo67855;
+__attribute__((used)) void* use67856 = (void*)&foo67856;
+__attribute__((used)) void* use67857 = (void*)&foo67857;
+__attribute__((used)) void* use67858 = (void*)&foo67858;
+__attribute__((used)) void* use67859 = (void*)&foo67859;
+__attribute__((used)) void* use67860 = (void*)&foo67860;
+__attribute__((used)) void* use67861 = (void*)&foo67861;
+__attribute__((used)) void* use67862 = (void*)&foo67862;
+__attribute__((used)) void* use67863 = (void*)&foo67863;
+__attribute__((used)) void* use67864 = (void*)&foo67864;
+__attribute__((used)) void* use67865 = (void*)&foo67865;
+__attribute__((used)) void* use67866 = (void*)&foo67866;
+__attribute__((used)) void* use67867 = (void*)&foo67867;
+__attribute__((used)) void* use67868 = (void*)&foo67868;
+__attribute__((used)) void* use67869 = (void*)&foo67869;
+__attribute__((used)) void* use67870 = (void*)&foo67870;
+__attribute__((used)) void* use67871 = (void*)&foo67871;
+__attribute__((used)) void* use67872 = (void*)&foo67872;
+__attribute__((used)) void* use67873 = (void*)&foo67873;
+__attribute__((used)) void* use67874 = (void*)&foo67874;
+__attribute__((used)) void* use67875 = (void*)&foo67875;
+__attribute__((used)) void* use67876 = (void*)&foo67876;
+__attribute__((used)) void* use67877 = (void*)&foo67877;
+__attribute__((used)) void* use67878 = (void*)&foo67878;
+__attribute__((used)) void* use67879 = (void*)&foo67879;
+__attribute__((used)) void* use67880 = (void*)&foo67880;
+__attribute__((used)) void* use67881 = (void*)&foo67881;
+__attribute__((used)) void* use67882 = (void*)&foo67882;
+__attribute__((used)) void* use67883 = (void*)&foo67883;
+__attribute__((used)) void* use67884 = (void*)&foo67884;
+__attribute__((used)) void* use67885 = (void*)&foo67885;
+__attribute__((used)) void* use67886 = (void*)&foo67886;
+__attribute__((used)) void* use67887 = (void*)&foo67887;
+__attribute__((used)) void* use67888 = (void*)&foo67888;
+__attribute__((used)) void* use67889 = (void*)&foo67889;
+__attribute__((used)) void* use67890 = (void*)&foo67890;
+__attribute__((used)) void* use67891 = (void*)&foo67891;
+__attribute__((used)) void* use67892 = (void*)&foo67892;
+__attribute__((used)) void* use67893 = (void*)&foo67893;
+__attribute__((used)) void* use67894 = (void*)&foo67894;
+__attribute__((used)) void* use67895 = (void*)&foo67895;
+__attribute__((used)) void* use67896 = (void*)&foo67896;
+__attribute__((used)) void* use67897 = (void*)&foo67897;
+__attribute__((used)) void* use67898 = (void*)&foo67898;
+__attribute__((used)) void* use67899 = (void*)&foo67899;
+__attribute__((used)) void* use67900 = (void*)&foo67900;
+__attribute__((used)) void* use67901 = (void*)&foo67901;
+__attribute__((used)) void* use67902 = (void*)&foo67902;
+__attribute__((used)) void* use67903 = (void*)&foo67903;
+__attribute__((used)) void* use67904 = (void*)&foo67904;
+__attribute__((used)) void* use67905 = (void*)&foo67905;
+__attribute__((used)) void* use67906 = (void*)&foo67906;
+__attribute__((used)) void* use67907 = (void*)&foo67907;
+__attribute__((used)) void* use67908 = (void*)&foo67908;
+__attribute__((used)) void* use67909 = (void*)&foo67909;
+__attribute__((used)) void* use67910 = (void*)&foo67910;
+__attribute__((used)) void* use67911 = (void*)&foo67911;
+__attribute__((used)) void* use67912 = (void*)&foo67912;
+__attribute__((used)) void* use67913 = (void*)&foo67913;
+__attribute__((used)) void* use67914 = (void*)&foo67914;
+__attribute__((used)) void* use67915 = (void*)&foo67915;
+__attribute__((used)) void* use67916 = (void*)&foo67916;
+__attribute__((used)) void* use67917 = (void*)&foo67917;
+__attribute__((used)) void* use67918 = (void*)&foo67918;
+__attribute__((used)) void* use67919 = (void*)&foo67919;
+__attribute__((used)) void* use67920 = (void*)&foo67920;
+__attribute__((used)) void* use67921 = (void*)&foo67921;
+__attribute__((used)) void* use67922 = (void*)&foo67922;
+__attribute__((used)) void* use67923 = (void*)&foo67923;
+__attribute__((used)) void* use67924 = (void*)&foo67924;
+__attribute__((used)) void* use67925 = (void*)&foo67925;
+__attribute__((used)) void* use67926 = (void*)&foo67926;
+__attribute__((used)) void* use67927 = (void*)&foo67927;
+__attribute__((used)) void* use67928 = (void*)&foo67928;
+__attribute__((used)) void* use67929 = (void*)&foo67929;
+__attribute__((used)) void* use67930 = (void*)&foo67930;
+__attribute__((used)) void* use67931 = (void*)&foo67931;
+__attribute__((used)) void* use67932 = (void*)&foo67932;
+__attribute__((used)) void* use67933 = (void*)&foo67933;
+__attribute__((used)) void* use67934 = (void*)&foo67934;
+__attribute__((used)) void* use67935 = (void*)&foo67935;
+__attribute__((used)) void* use67936 = (void*)&foo67936;
+__attribute__((used)) void* use67937 = (void*)&foo67937;
+__attribute__((used)) void* use67938 = (void*)&foo67938;
+__attribute__((used)) void* use67939 = (void*)&foo67939;
+__attribute__((used)) void* use67940 = (void*)&foo67940;
+__attribute__((used)) void* use67941 = (void*)&foo67941;
+__attribute__((used)) void* use67942 = (void*)&foo67942;
+__attribute__((used)) void* use67943 = (void*)&foo67943;
+__attribute__((used)) void* use67944 = (void*)&foo67944;
+__attribute__((used)) void* use67945 = (void*)&foo67945;
+__attribute__((used)) void* use67946 = (void*)&foo67946;
+__attribute__((used)) void* use67947 = (void*)&foo67947;
+__attribute__((used)) void* use67948 = (void*)&foo67948;
+__attribute__((used)) void* use67949 = (void*)&foo67949;
+__attribute__((used)) void* use67950 = (void*)&foo67950;
+__attribute__((used)) void* use67951 = (void*)&foo67951;
+__attribute__((used)) void* use67952 = (void*)&foo67952;
+__attribute__((used)) void* use67953 = (void*)&foo67953;
+__attribute__((used)) void* use67954 = (void*)&foo67954;
+__attribute__((used)) void* use67955 = (void*)&foo67955;
+__attribute__((used)) void* use67956 = (void*)&foo67956;
+__attribute__((used)) void* use67957 = (void*)&foo67957;
+__attribute__((used)) void* use67958 = (void*)&foo67958;
+__attribute__((used)) void* use67959 = (void*)&foo67959;
+__attribute__((used)) void* use67960 = (void*)&foo67960;
+__attribute__((used)) void* use67961 = (void*)&foo67961;
+__attribute__((used)) void* use67962 = (void*)&foo67962;
+__attribute__((used)) void* use67963 = (void*)&foo67963;
+__attribute__((used)) void* use67964 = (void*)&foo67964;
+__attribute__((used)) void* use67965 = (void*)&foo67965;
+__attribute__((used)) void* use67966 = (void*)&foo67966;
+__attribute__((used)) void* use67967 = (void*)&foo67967;
+__attribute__((used)) void* use67968 = (void*)&foo67968;
+__attribute__((used)) void* use67969 = (void*)&foo67969;
+__attribute__((used)) void* use67970 = (void*)&foo67970;
+__attribute__((used)) void* use67971 = (void*)&foo67971;
+__attribute__((used)) void* use67972 = (void*)&foo67972;
+__attribute__((used)) void* use67973 = (void*)&foo67973;
+__attribute__((used)) void* use67974 = (void*)&foo67974;
+__attribute__((used)) void* use67975 = (void*)&foo67975;
+__attribute__((used)) void* use67976 = (void*)&foo67976;
+__attribute__((used)) void* use67977 = (void*)&foo67977;
+__attribute__((used)) void* use67978 = (void*)&foo67978;
+__attribute__((used)) void* use67979 = (void*)&foo67979;
+__attribute__((used)) void* use67980 = (void*)&foo67980;
+__attribute__((used)) void* use67981 = (void*)&foo67981;
+__attribute__((used)) void* use67982 = (void*)&foo67982;
+__attribute__((used)) void* use67983 = (void*)&foo67983;
+__attribute__((used)) void* use67984 = (void*)&foo67984;
+__attribute__((used)) void* use67985 = (void*)&foo67985;
+__attribute__((used)) void* use67986 = (void*)&foo67986;
+__attribute__((used)) void* use67987 = (void*)&foo67987;
+__attribute__((used)) void* use67988 = (void*)&foo67988;
+__attribute__((used)) void* use67989 = (void*)&foo67989;
+__attribute__((used)) void* use67990 = (void*)&foo67990;
+__attribute__((used)) void* use67991 = (void*)&foo67991;
+__attribute__((used)) void* use67992 = (void*)&foo67992;
+__attribute__((used)) void* use67993 = (void*)&foo67993;
+__attribute__((used)) void* use67994 = (void*)&foo67994;
+__attribute__((used)) void* use67995 = (void*)&foo67995;
+__attribute__((used)) void* use67996 = (void*)&foo67996;
+__attribute__((used)) void* use67997 = (void*)&foo67997;
+__attribute__((used)) void* use67998 = (void*)&foo67998;
+__attribute__((used)) void* use67999 = (void*)&foo67999;
+__attribute__((used)) void* use68000 = (void*)&foo68000;
+__attribute__((used)) void* use68001 = (void*)&foo68001;
+__attribute__((used)) void* use68002 = (void*)&foo68002;
+__attribute__((used)) void* use68003 = (void*)&foo68003;
+__attribute__((used)) void* use68004 = (void*)&foo68004;
+__attribute__((used)) void* use68005 = (void*)&foo68005;
+__attribute__((used)) void* use68006 = (void*)&foo68006;
+__attribute__((used)) void* use68007 = (void*)&foo68007;
+__attribute__((used)) void* use68008 = (void*)&foo68008;
+__attribute__((used)) void* use68009 = (void*)&foo68009;
+__attribute__((used)) void* use68010 = (void*)&foo68010;
+__attribute__((used)) void* use68011 = (void*)&foo68011;
+__attribute__((used)) void* use68012 = (void*)&foo68012;
+__attribute__((used)) void* use68013 = (void*)&foo68013;
+__attribute__((used)) void* use68014 = (void*)&foo68014;
+__attribute__((used)) void* use68015 = (void*)&foo68015;
+__attribute__((used)) void* use68016 = (void*)&foo68016;
+__attribute__((used)) void* use68017 = (void*)&foo68017;
+__attribute__((used)) void* use68018 = (void*)&foo68018;
+__attribute__((used)) void* use68019 = (void*)&foo68019;
+__attribute__((used)) void* use68020 = (void*)&foo68020;
+__attribute__((used)) void* use68021 = (void*)&foo68021;
+__attribute__((used)) void* use68022 = (void*)&foo68022;
+__attribute__((used)) void* use68023 = (void*)&foo68023;
+__attribute__((used)) void* use68024 = (void*)&foo68024;
+__attribute__((used)) void* use68025 = (void*)&foo68025;
+__attribute__((used)) void* use68026 = (void*)&foo68026;
+__attribute__((used)) void* use68027 = (void*)&foo68027;
+__attribute__((used)) void* use68028 = (void*)&foo68028;
+__attribute__((used)) void* use68029 = (void*)&foo68029;
+__attribute__((used)) void* use68030 = (void*)&foo68030;
+__attribute__((used)) void* use68031 = (void*)&foo68031;
+__attribute__((used)) void* use68032 = (void*)&foo68032;
+__attribute__((used)) void* use68033 = (void*)&foo68033;
+__attribute__((used)) void* use68034 = (void*)&foo68034;
+__attribute__((used)) void* use68035 = (void*)&foo68035;
+__attribute__((used)) void* use68036 = (void*)&foo68036;
+__attribute__((used)) void* use68037 = (void*)&foo68037;
+__attribute__((used)) void* use68038 = (void*)&foo68038;
+__attribute__((used)) void* use68039 = (void*)&foo68039;
+__attribute__((used)) void* use68040 = (void*)&foo68040;
+__attribute__((used)) void* use68041 = (void*)&foo68041;
+__attribute__((used)) void* use68042 = (void*)&foo68042;
+__attribute__((used)) void* use68043 = (void*)&foo68043;
+__attribute__((used)) void* use68044 = (void*)&foo68044;
+__attribute__((used)) void* use68045 = (void*)&foo68045;
+__attribute__((used)) void* use68046 = (void*)&foo68046;
+__attribute__((used)) void* use68047 = (void*)&foo68047;
+__attribute__((used)) void* use68048 = (void*)&foo68048;
+__attribute__((used)) void* use68049 = (void*)&foo68049;
+__attribute__((used)) void* use68050 = (void*)&foo68050;
+__attribute__((used)) void* use68051 = (void*)&foo68051;
+__attribute__((used)) void* use68052 = (void*)&foo68052;
+__attribute__((used)) void* use68053 = (void*)&foo68053;
+__attribute__((used)) void* use68054 = (void*)&foo68054;
+__attribute__((used)) void* use68055 = (void*)&foo68055;
+__attribute__((used)) void* use68056 = (void*)&foo68056;
+__attribute__((used)) void* use68057 = (void*)&foo68057;
+__attribute__((used)) void* use68058 = (void*)&foo68058;
+__attribute__((used)) void* use68059 = (void*)&foo68059;
+__attribute__((used)) void* use68060 = (void*)&foo68060;
+__attribute__((used)) void* use68061 = (void*)&foo68061;
+__attribute__((used)) void* use68062 = (void*)&foo68062;
+__attribute__((used)) void* use68063 = (void*)&foo68063;
+__attribute__((used)) void* use68064 = (void*)&foo68064;
+__attribute__((used)) void* use68065 = (void*)&foo68065;
+__attribute__((used)) void* use68066 = (void*)&foo68066;
+__attribute__((used)) void* use68067 = (void*)&foo68067;
+__attribute__((used)) void* use68068 = (void*)&foo68068;
+__attribute__((used)) void* use68069 = (void*)&foo68069;
+__attribute__((used)) void* use68070 = (void*)&foo68070;
+__attribute__((used)) void* use68071 = (void*)&foo68071;
+__attribute__((used)) void* use68072 = (void*)&foo68072;
+__attribute__((used)) void* use68073 = (void*)&foo68073;
+__attribute__((used)) void* use68074 = (void*)&foo68074;
+__attribute__((used)) void* use68075 = (void*)&foo68075;
+__attribute__((used)) void* use68076 = (void*)&foo68076;
+__attribute__((used)) void* use68077 = (void*)&foo68077;
+__attribute__((used)) void* use68078 = (void*)&foo68078;
+__attribute__((used)) void* use68079 = (void*)&foo68079;
+__attribute__((used)) void* use68080 = (void*)&foo68080;
+__attribute__((used)) void* use68081 = (void*)&foo68081;
+__attribute__((used)) void* use68082 = (void*)&foo68082;
+__attribute__((used)) void* use68083 = (void*)&foo68083;
+__attribute__((used)) void* use68084 = (void*)&foo68084;
+__attribute__((used)) void* use68085 = (void*)&foo68085;
+__attribute__((used)) void* use68086 = (void*)&foo68086;
+__attribute__((used)) void* use68087 = (void*)&foo68087;
+__attribute__((used)) void* use68088 = (void*)&foo68088;
+__attribute__((used)) void* use68089 = (void*)&foo68089;
+__attribute__((used)) void* use68090 = (void*)&foo68090;
+__attribute__((used)) void* use68091 = (void*)&foo68091;
+__attribute__((used)) void* use68092 = (void*)&foo68092;
+__attribute__((used)) void* use68093 = (void*)&foo68093;
+__attribute__((used)) void* use68094 = (void*)&foo68094;
+__attribute__((used)) void* use68095 = (void*)&foo68095;
+__attribute__((used)) void* use68096 = (void*)&foo68096;
+__attribute__((used)) void* use68097 = (void*)&foo68097;
+__attribute__((used)) void* use68098 = (void*)&foo68098;
+__attribute__((used)) void* use68099 = (void*)&foo68099;
+__attribute__((used)) void* use68100 = (void*)&foo68100;
+__attribute__((used)) void* use68101 = (void*)&foo68101;
+__attribute__((used)) void* use68102 = (void*)&foo68102;
+__attribute__((used)) void* use68103 = (void*)&foo68103;
+__attribute__((used)) void* use68104 = (void*)&foo68104;
+__attribute__((used)) void* use68105 = (void*)&foo68105;
+__attribute__((used)) void* use68106 = (void*)&foo68106;
+__attribute__((used)) void* use68107 = (void*)&foo68107;
+__attribute__((used)) void* use68108 = (void*)&foo68108;
+__attribute__((used)) void* use68109 = (void*)&foo68109;
+__attribute__((used)) void* use68110 = (void*)&foo68110;
+__attribute__((used)) void* use68111 = (void*)&foo68111;
+__attribute__((used)) void* use68112 = (void*)&foo68112;
+__attribute__((used)) void* use68113 = (void*)&foo68113;
+__attribute__((used)) void* use68114 = (void*)&foo68114;
+__attribute__((used)) void* use68115 = (void*)&foo68115;
+__attribute__((used)) void* use68116 = (void*)&foo68116;
+__attribute__((used)) void* use68117 = (void*)&foo68117;
+__attribute__((used)) void* use68118 = (void*)&foo68118;
+__attribute__((used)) void* use68119 = (void*)&foo68119;
+__attribute__((used)) void* use68120 = (void*)&foo68120;
+__attribute__((used)) void* use68121 = (void*)&foo68121;
+__attribute__((used)) void* use68122 = (void*)&foo68122;
+__attribute__((used)) void* use68123 = (void*)&foo68123;
+__attribute__((used)) void* use68124 = (void*)&foo68124;
+__attribute__((used)) void* use68125 = (void*)&foo68125;
+__attribute__((used)) void* use68126 = (void*)&foo68126;
+__attribute__((used)) void* use68127 = (void*)&foo68127;
+__attribute__((used)) void* use68128 = (void*)&foo68128;
+__attribute__((used)) void* use68129 = (void*)&foo68129;
+__attribute__((used)) void* use68130 = (void*)&foo68130;
+__attribute__((used)) void* use68131 = (void*)&foo68131;
+__attribute__((used)) void* use68132 = (void*)&foo68132;
+__attribute__((used)) void* use68133 = (void*)&foo68133;
+__attribute__((used)) void* use68134 = (void*)&foo68134;
+__attribute__((used)) void* use68135 = (void*)&foo68135;
+__attribute__((used)) void* use68136 = (void*)&foo68136;
+__attribute__((used)) void* use68137 = (void*)&foo68137;
+__attribute__((used)) void* use68138 = (void*)&foo68138;
+__attribute__((used)) void* use68139 = (void*)&foo68139;
+__attribute__((used)) void* use68140 = (void*)&foo68140;
+__attribute__((used)) void* use68141 = (void*)&foo68141;
+__attribute__((used)) void* use68142 = (void*)&foo68142;
+__attribute__((used)) void* use68143 = (void*)&foo68143;
+__attribute__((used)) void* use68144 = (void*)&foo68144;
+__attribute__((used)) void* use68145 = (void*)&foo68145;
+__attribute__((used)) void* use68146 = (void*)&foo68146;
+__attribute__((used)) void* use68147 = (void*)&foo68147;
+__attribute__((used)) void* use68148 = (void*)&foo68148;
+__attribute__((used)) void* use68149 = (void*)&foo68149;
+__attribute__((used)) void* use68150 = (void*)&foo68150;
+__attribute__((used)) void* use68151 = (void*)&foo68151;
+__attribute__((used)) void* use68152 = (void*)&foo68152;
+__attribute__((used)) void* use68153 = (void*)&foo68153;
+__attribute__((used)) void* use68154 = (void*)&foo68154;
+__attribute__((used)) void* use68155 = (void*)&foo68155;
+__attribute__((used)) void* use68156 = (void*)&foo68156;
+__attribute__((used)) void* use68157 = (void*)&foo68157;
+__attribute__((used)) void* use68158 = (void*)&foo68158;
+__attribute__((used)) void* use68159 = (void*)&foo68159;
+__attribute__((used)) void* use68160 = (void*)&foo68160;
+__attribute__((used)) void* use68161 = (void*)&foo68161;
+__attribute__((used)) void* use68162 = (void*)&foo68162;
+__attribute__((used)) void* use68163 = (void*)&foo68163;
+__attribute__((used)) void* use68164 = (void*)&foo68164;
+__attribute__((used)) void* use68165 = (void*)&foo68165;
+__attribute__((used)) void* use68166 = (void*)&foo68166;
+__attribute__((used)) void* use68167 = (void*)&foo68167;
+__attribute__((used)) void* use68168 = (void*)&foo68168;
+__attribute__((used)) void* use68169 = (void*)&foo68169;
+__attribute__((used)) void* use68170 = (void*)&foo68170;
+__attribute__((used)) void* use68171 = (void*)&foo68171;
+__attribute__((used)) void* use68172 = (void*)&foo68172;
+__attribute__((used)) void* use68173 = (void*)&foo68173;
+__attribute__((used)) void* use68174 = (void*)&foo68174;
+__attribute__((used)) void* use68175 = (void*)&foo68175;
+__attribute__((used)) void* use68176 = (void*)&foo68176;
+__attribute__((used)) void* use68177 = (void*)&foo68177;
+__attribute__((used)) void* use68178 = (void*)&foo68178;
+__attribute__((used)) void* use68179 = (void*)&foo68179;
+__attribute__((used)) void* use68180 = (void*)&foo68180;
+__attribute__((used)) void* use68181 = (void*)&foo68181;
+__attribute__((used)) void* use68182 = (void*)&foo68182;
+__attribute__((used)) void* use68183 = (void*)&foo68183;
+__attribute__((used)) void* use68184 = (void*)&foo68184;
+__attribute__((used)) void* use68185 = (void*)&foo68185;
+__attribute__((used)) void* use68186 = (void*)&foo68186;
+__attribute__((used)) void* use68187 = (void*)&foo68187;
+__attribute__((used)) void* use68188 = (void*)&foo68188;
+__attribute__((used)) void* use68189 = (void*)&foo68189;
+__attribute__((used)) void* use68190 = (void*)&foo68190;
+__attribute__((used)) void* use68191 = (void*)&foo68191;
+__attribute__((used)) void* use68192 = (void*)&foo68192;
+__attribute__((used)) void* use68193 = (void*)&foo68193;
+__attribute__((used)) void* use68194 = (void*)&foo68194;
+__attribute__((used)) void* use68195 = (void*)&foo68195;
+__attribute__((used)) void* use68196 = (void*)&foo68196;
+__attribute__((used)) void* use68197 = (void*)&foo68197;
+__attribute__((used)) void* use68198 = (void*)&foo68198;
+__attribute__((used)) void* use68199 = (void*)&foo68199;
+__attribute__((used)) void* use68200 = (void*)&foo68200;
+__attribute__((used)) void* use68201 = (void*)&foo68201;
+__attribute__((used)) void* use68202 = (void*)&foo68202;
+__attribute__((used)) void* use68203 = (void*)&foo68203;
+__attribute__((used)) void* use68204 = (void*)&foo68204;
+__attribute__((used)) void* use68205 = (void*)&foo68205;
+__attribute__((used)) void* use68206 = (void*)&foo68206;
+__attribute__((used)) void* use68207 = (void*)&foo68207;
+__attribute__((used)) void* use68208 = (void*)&foo68208;
+__attribute__((used)) void* use68209 = (void*)&foo68209;
+__attribute__((used)) void* use68210 = (void*)&foo68210;
+__attribute__((used)) void* use68211 = (void*)&foo68211;
+__attribute__((used)) void* use68212 = (void*)&foo68212;
+__attribute__((used)) void* use68213 = (void*)&foo68213;
+__attribute__((used)) void* use68214 = (void*)&foo68214;
+__attribute__((used)) void* use68215 = (void*)&foo68215;
+__attribute__((used)) void* use68216 = (void*)&foo68216;
+__attribute__((used)) void* use68217 = (void*)&foo68217;
+__attribute__((used)) void* use68218 = (void*)&foo68218;
+__attribute__((used)) void* use68219 = (void*)&foo68219;
+__attribute__((used)) void* use68220 = (void*)&foo68220;
+__attribute__((used)) void* use68221 = (void*)&foo68221;
+__attribute__((used)) void* use68222 = (void*)&foo68222;
+__attribute__((used)) void* use68223 = (void*)&foo68223;
+__attribute__((used)) void* use68224 = (void*)&foo68224;
+__attribute__((used)) void* use68225 = (void*)&foo68225;
+__attribute__((used)) void* use68226 = (void*)&foo68226;
+__attribute__((used)) void* use68227 = (void*)&foo68227;
+__attribute__((used)) void* use68228 = (void*)&foo68228;
+__attribute__((used)) void* use68229 = (void*)&foo68229;
+__attribute__((used)) void* use68230 = (void*)&foo68230;
+__attribute__((used)) void* use68231 = (void*)&foo68231;
+__attribute__((used)) void* use68232 = (void*)&foo68232;
+__attribute__((used)) void* use68233 = (void*)&foo68233;
+__attribute__((used)) void* use68234 = (void*)&foo68234;
+__attribute__((used)) void* use68235 = (void*)&foo68235;
+__attribute__((used)) void* use68236 = (void*)&foo68236;
+__attribute__((used)) void* use68237 = (void*)&foo68237;
+__attribute__((used)) void* use68238 = (void*)&foo68238;
+__attribute__((used)) void* use68239 = (void*)&foo68239;
+__attribute__((used)) void* use68240 = (void*)&foo68240;
+__attribute__((used)) void* use68241 = (void*)&foo68241;
+__attribute__((used)) void* use68242 = (void*)&foo68242;
+__attribute__((used)) void* use68243 = (void*)&foo68243;
+__attribute__((used)) void* use68244 = (void*)&foo68244;
+__attribute__((used)) void* use68245 = (void*)&foo68245;
+__attribute__((used)) void* use68246 = (void*)&foo68246;
+__attribute__((used)) void* use68247 = (void*)&foo68247;
+__attribute__((used)) void* use68248 = (void*)&foo68248;
+__attribute__((used)) void* use68249 = (void*)&foo68249;
+__attribute__((used)) void* use68250 = (void*)&foo68250;
+__attribute__((used)) void* use68251 = (void*)&foo68251;
+__attribute__((used)) void* use68252 = (void*)&foo68252;
+__attribute__((used)) void* use68253 = (void*)&foo68253;
+__attribute__((used)) void* use68254 = (void*)&foo68254;
+__attribute__((used)) void* use68255 = (void*)&foo68255;
+__attribute__((used)) void* use68256 = (void*)&foo68256;
+__attribute__((used)) void* use68257 = (void*)&foo68257;
+__attribute__((used)) void* use68258 = (void*)&foo68258;
+__attribute__((used)) void* use68259 = (void*)&foo68259;
+__attribute__((used)) void* use68260 = (void*)&foo68260;
+__attribute__((used)) void* use68261 = (void*)&foo68261;
+__attribute__((used)) void* use68262 = (void*)&foo68262;
+__attribute__((used)) void* use68263 = (void*)&foo68263;
+__attribute__((used)) void* use68264 = (void*)&foo68264;
+__attribute__((used)) void* use68265 = (void*)&foo68265;
+__attribute__((used)) void* use68266 = (void*)&foo68266;
+__attribute__((used)) void* use68267 = (void*)&foo68267;
+__attribute__((used)) void* use68268 = (void*)&foo68268;
+__attribute__((used)) void* use68269 = (void*)&foo68269;
+__attribute__((used)) void* use68270 = (void*)&foo68270;
+__attribute__((used)) void* use68271 = (void*)&foo68271;
+__attribute__((used)) void* use68272 = (void*)&foo68272;
+__attribute__((used)) void* use68273 = (void*)&foo68273;
+__attribute__((used)) void* use68274 = (void*)&foo68274;
+__attribute__((used)) void* use68275 = (void*)&foo68275;
+__attribute__((used)) void* use68276 = (void*)&foo68276;
+__attribute__((used)) void* use68277 = (void*)&foo68277;
+__attribute__((used)) void* use68278 = (void*)&foo68278;
+__attribute__((used)) void* use68279 = (void*)&foo68279;
+__attribute__((used)) void* use68280 = (void*)&foo68280;
+__attribute__((used)) void* use68281 = (void*)&foo68281;
+__attribute__((used)) void* use68282 = (void*)&foo68282;
+__attribute__((used)) void* use68283 = (void*)&foo68283;
+__attribute__((used)) void* use68284 = (void*)&foo68284;
+__attribute__((used)) void* use68285 = (void*)&foo68285;
+__attribute__((used)) void* use68286 = (void*)&foo68286;
+__attribute__((used)) void* use68287 = (void*)&foo68287;
+__attribute__((used)) void* use68288 = (void*)&foo68288;
+__attribute__((used)) void* use68289 = (void*)&foo68289;
+__attribute__((used)) void* use68290 = (void*)&foo68290;
+__attribute__((used)) void* use68291 = (void*)&foo68291;
+__attribute__((used)) void* use68292 = (void*)&foo68292;
+__attribute__((used)) void* use68293 = (void*)&foo68293;
+__attribute__((used)) void* use68294 = (void*)&foo68294;
+__attribute__((used)) void* use68295 = (void*)&foo68295;
+__attribute__((used)) void* use68296 = (void*)&foo68296;
+__attribute__((used)) void* use68297 = (void*)&foo68297;
+__attribute__((used)) void* use68298 = (void*)&foo68298;
+__attribute__((used)) void* use68299 = (void*)&foo68299;
+__attribute__((used)) void* use68300 = (void*)&foo68300;
+__attribute__((used)) void* use68301 = (void*)&foo68301;
+__attribute__((used)) void* use68302 = (void*)&foo68302;
+__attribute__((used)) void* use68303 = (void*)&foo68303;
+__attribute__((used)) void* use68304 = (void*)&foo68304;
+__attribute__((used)) void* use68305 = (void*)&foo68305;
+__attribute__((used)) void* use68306 = (void*)&foo68306;
+__attribute__((used)) void* use68307 = (void*)&foo68307;
+__attribute__((used)) void* use68308 = (void*)&foo68308;
+__attribute__((used)) void* use68309 = (void*)&foo68309;
+__attribute__((used)) void* use68310 = (void*)&foo68310;
+__attribute__((used)) void* use68311 = (void*)&foo68311;
+__attribute__((used)) void* use68312 = (void*)&foo68312;
+__attribute__((used)) void* use68313 = (void*)&foo68313;
+__attribute__((used)) void* use68314 = (void*)&foo68314;
+__attribute__((used)) void* use68315 = (void*)&foo68315;
+__attribute__((used)) void* use68316 = (void*)&foo68316;
+__attribute__((used)) void* use68317 = (void*)&foo68317;
+__attribute__((used)) void* use68318 = (void*)&foo68318;
+__attribute__((used)) void* use68319 = (void*)&foo68319;
+__attribute__((used)) void* use68320 = (void*)&foo68320;
+__attribute__((used)) void* use68321 = (void*)&foo68321;
+__attribute__((used)) void* use68322 = (void*)&foo68322;
+__attribute__((used)) void* use68323 = (void*)&foo68323;
+__attribute__((used)) void* use68324 = (void*)&foo68324;
+__attribute__((used)) void* use68325 = (void*)&foo68325;
+__attribute__((used)) void* use68326 = (void*)&foo68326;
+__attribute__((used)) void* use68327 = (void*)&foo68327;
+__attribute__((used)) void* use68328 = (void*)&foo68328;
+__attribute__((used)) void* use68329 = (void*)&foo68329;
+__attribute__((used)) void* use68330 = (void*)&foo68330;
+__attribute__((used)) void* use68331 = (void*)&foo68331;
+__attribute__((used)) void* use68332 = (void*)&foo68332;
+__attribute__((used)) void* use68333 = (void*)&foo68333;
+__attribute__((used)) void* use68334 = (void*)&foo68334;
+__attribute__((used)) void* use68335 = (void*)&foo68335;
+__attribute__((used)) void* use68336 = (void*)&foo68336;
+__attribute__((used)) void* use68337 = (void*)&foo68337;
+__attribute__((used)) void* use68338 = (void*)&foo68338;
+__attribute__((used)) void* use68339 = (void*)&foo68339;
+__attribute__((used)) void* use68340 = (void*)&foo68340;
+__attribute__((used)) void* use68341 = (void*)&foo68341;
+__attribute__((used)) void* use68342 = (void*)&foo68342;
+__attribute__((used)) void* use68343 = (void*)&foo68343;
+__attribute__((used)) void* use68344 = (void*)&foo68344;
+__attribute__((used)) void* use68345 = (void*)&foo68345;
+__attribute__((used)) void* use68346 = (void*)&foo68346;
+__attribute__((used)) void* use68347 = (void*)&foo68347;
+__attribute__((used)) void* use68348 = (void*)&foo68348;
+__attribute__((used)) void* use68349 = (void*)&foo68349;
+__attribute__((used)) void* use68350 = (void*)&foo68350;
+__attribute__((used)) void* use68351 = (void*)&foo68351;
+__attribute__((used)) void* use68352 = (void*)&foo68352;
+__attribute__((used)) void* use68353 = (void*)&foo68353;
+__attribute__((used)) void* use68354 = (void*)&foo68354;
+__attribute__((used)) void* use68355 = (void*)&foo68355;
+__attribute__((used)) void* use68356 = (void*)&foo68356;
+__attribute__((used)) void* use68357 = (void*)&foo68357;
+__attribute__((used)) void* use68358 = (void*)&foo68358;
+__attribute__((used)) void* use68359 = (void*)&foo68359;
+__attribute__((used)) void* use68360 = (void*)&foo68360;
+__attribute__((used)) void* use68361 = (void*)&foo68361;
+__attribute__((used)) void* use68362 = (void*)&foo68362;
+__attribute__((used)) void* use68363 = (void*)&foo68363;
+__attribute__((used)) void* use68364 = (void*)&foo68364;
+__attribute__((used)) void* use68365 = (void*)&foo68365;
+__attribute__((used)) void* use68366 = (void*)&foo68366;
+__attribute__((used)) void* use68367 = (void*)&foo68367;
+__attribute__((used)) void* use68368 = (void*)&foo68368;
+__attribute__((used)) void* use68369 = (void*)&foo68369;
+__attribute__((used)) void* use68370 = (void*)&foo68370;
+__attribute__((used)) void* use68371 = (void*)&foo68371;
+__attribute__((used)) void* use68372 = (void*)&foo68372;
+__attribute__((used)) void* use68373 = (void*)&foo68373;
+__attribute__((used)) void* use68374 = (void*)&foo68374;
+__attribute__((used)) void* use68375 = (void*)&foo68375;
+__attribute__((used)) void* use68376 = (void*)&foo68376;
+__attribute__((used)) void* use68377 = (void*)&foo68377;
+__attribute__((used)) void* use68378 = (void*)&foo68378;
+__attribute__((used)) void* use68379 = (void*)&foo68379;
+__attribute__((used)) void* use68380 = (void*)&foo68380;
+__attribute__((used)) void* use68381 = (void*)&foo68381;
+__attribute__((used)) void* use68382 = (void*)&foo68382;
+__attribute__((used)) void* use68383 = (void*)&foo68383;
+__attribute__((used)) void* use68384 = (void*)&foo68384;
+__attribute__((used)) void* use68385 = (void*)&foo68385;
+__attribute__((used)) void* use68386 = (void*)&foo68386;
+__attribute__((used)) void* use68387 = (void*)&foo68387;
+__attribute__((used)) void* use68388 = (void*)&foo68388;
+__attribute__((used)) void* use68389 = (void*)&foo68389;
+__attribute__((used)) void* use68390 = (void*)&foo68390;
+__attribute__((used)) void* use68391 = (void*)&foo68391;
+__attribute__((used)) void* use68392 = (void*)&foo68392;
+__attribute__((used)) void* use68393 = (void*)&foo68393;
+__attribute__((used)) void* use68394 = (void*)&foo68394;
+__attribute__((used)) void* use68395 = (void*)&foo68395;
+__attribute__((used)) void* use68396 = (void*)&foo68396;
+__attribute__((used)) void* use68397 = (void*)&foo68397;
+__attribute__((used)) void* use68398 = (void*)&foo68398;
+__attribute__((used)) void* use68399 = (void*)&foo68399;
+__attribute__((used)) void* use68400 = (void*)&foo68400;
+__attribute__((used)) void* use68401 = (void*)&foo68401;
+__attribute__((used)) void* use68402 = (void*)&foo68402;
+__attribute__((used)) void* use68403 = (void*)&foo68403;
+__attribute__((used)) void* use68404 = (void*)&foo68404;
+__attribute__((used)) void* use68405 = (void*)&foo68405;
+__attribute__((used)) void* use68406 = (void*)&foo68406;
+__attribute__((used)) void* use68407 = (void*)&foo68407;
+__attribute__((used)) void* use68408 = (void*)&foo68408;
+__attribute__((used)) void* use68409 = (void*)&foo68409;
+__attribute__((used)) void* use68410 = (void*)&foo68410;
+__attribute__((used)) void* use68411 = (void*)&foo68411;
+__attribute__((used)) void* use68412 = (void*)&foo68412;
+__attribute__((used)) void* use68413 = (void*)&foo68413;
+__attribute__((used)) void* use68414 = (void*)&foo68414;
+__attribute__((used)) void* use68415 = (void*)&foo68415;
+__attribute__((used)) void* use68416 = (void*)&foo68416;
+__attribute__((used)) void* use68417 = (void*)&foo68417;
+__attribute__((used)) void* use68418 = (void*)&foo68418;
+__attribute__((used)) void* use68419 = (void*)&foo68419;
+__attribute__((used)) void* use68420 = (void*)&foo68420;
+__attribute__((used)) void* use68421 = (void*)&foo68421;
+__attribute__((used)) void* use68422 = (void*)&foo68422;
+__attribute__((used)) void* use68423 = (void*)&foo68423;
+__attribute__((used)) void* use68424 = (void*)&foo68424;
+__attribute__((used)) void* use68425 = (void*)&foo68425;
+__attribute__((used)) void* use68426 = (void*)&foo68426;
+__attribute__((used)) void* use68427 = (void*)&foo68427;
+__attribute__((used)) void* use68428 = (void*)&foo68428;
+__attribute__((used)) void* use68429 = (void*)&foo68429;
+__attribute__((used)) void* use68430 = (void*)&foo68430;
+__attribute__((used)) void* use68431 = (void*)&foo68431;
+__attribute__((used)) void* use68432 = (void*)&foo68432;
+__attribute__((used)) void* use68433 = (void*)&foo68433;
+__attribute__((used)) void* use68434 = (void*)&foo68434;
+__attribute__((used)) void* use68435 = (void*)&foo68435;
+__attribute__((used)) void* use68436 = (void*)&foo68436;
+__attribute__((used)) void* use68437 = (void*)&foo68437;
+__attribute__((used)) void* use68438 = (void*)&foo68438;
+__attribute__((used)) void* use68439 = (void*)&foo68439;
+__attribute__((used)) void* use68440 = (void*)&foo68440;
+__attribute__((used)) void* use68441 = (void*)&foo68441;
+__attribute__((used)) void* use68442 = (void*)&foo68442;
+__attribute__((used)) void* use68443 = (void*)&foo68443;
+__attribute__((used)) void* use68444 = (void*)&foo68444;
+__attribute__((used)) void* use68445 = (void*)&foo68445;
+__attribute__((used)) void* use68446 = (void*)&foo68446;
+__attribute__((used)) void* use68447 = (void*)&foo68447;
+__attribute__((used)) void* use68448 = (void*)&foo68448;
+__attribute__((used)) void* use68449 = (void*)&foo68449;
+__attribute__((used)) void* use68450 = (void*)&foo68450;
+__attribute__((used)) void* use68451 = (void*)&foo68451;
+__attribute__((used)) void* use68452 = (void*)&foo68452;
+__attribute__((used)) void* use68453 = (void*)&foo68453;
+__attribute__((used)) void* use68454 = (void*)&foo68454;
+__attribute__((used)) void* use68455 = (void*)&foo68455;
+__attribute__((used)) void* use68456 = (void*)&foo68456;
+__attribute__((used)) void* use68457 = (void*)&foo68457;
+__attribute__((used)) void* use68458 = (void*)&foo68458;
+__attribute__((used)) void* use68459 = (void*)&foo68459;
+__attribute__((used)) void* use68460 = (void*)&foo68460;
+__attribute__((used)) void* use68461 = (void*)&foo68461;
+__attribute__((used)) void* use68462 = (void*)&foo68462;
+__attribute__((used)) void* use68463 = (void*)&foo68463;
+__attribute__((used)) void* use68464 = (void*)&foo68464;
+__attribute__((used)) void* use68465 = (void*)&foo68465;
+__attribute__((used)) void* use68466 = (void*)&foo68466;
+__attribute__((used)) void* use68467 = (void*)&foo68467;
+__attribute__((used)) void* use68468 = (void*)&foo68468;
+__attribute__((used)) void* use68469 = (void*)&foo68469;
+__attribute__((used)) void* use68470 = (void*)&foo68470;
+__attribute__((used)) void* use68471 = (void*)&foo68471;
+__attribute__((used)) void* use68472 = (void*)&foo68472;
+__attribute__((used)) void* use68473 = (void*)&foo68473;
+__attribute__((used)) void* use68474 = (void*)&foo68474;
+__attribute__((used)) void* use68475 = (void*)&foo68475;
+__attribute__((used)) void* use68476 = (void*)&foo68476;
+__attribute__((used)) void* use68477 = (void*)&foo68477;
+__attribute__((used)) void* use68478 = (void*)&foo68478;
+__attribute__((used)) void* use68479 = (void*)&foo68479;
+__attribute__((used)) void* use68480 = (void*)&foo68480;
+__attribute__((used)) void* use68481 = (void*)&foo68481;
+__attribute__((used)) void* use68482 = (void*)&foo68482;
+__attribute__((used)) void* use68483 = (void*)&foo68483;
+__attribute__((used)) void* use68484 = (void*)&foo68484;
+__attribute__((used)) void* use68485 = (void*)&foo68485;
+__attribute__((used)) void* use68486 = (void*)&foo68486;
+__attribute__((used)) void* use68487 = (void*)&foo68487;
+__attribute__((used)) void* use68488 = (void*)&foo68488;
+__attribute__((used)) void* use68489 = (void*)&foo68489;
+__attribute__((used)) void* use68490 = (void*)&foo68490;
+__attribute__((used)) void* use68491 = (void*)&foo68491;
+__attribute__((used)) void* use68492 = (void*)&foo68492;
+__attribute__((used)) void* use68493 = (void*)&foo68493;
+__attribute__((used)) void* use68494 = (void*)&foo68494;
+__attribute__((used)) void* use68495 = (void*)&foo68495;
+__attribute__((used)) void* use68496 = (void*)&foo68496;
+__attribute__((used)) void* use68497 = (void*)&foo68497;
+__attribute__((used)) void* use68498 = (void*)&foo68498;
+__attribute__((used)) void* use68499 = (void*)&foo68499;
+__attribute__((used)) void* use68500 = (void*)&foo68500;
+__attribute__((used)) void* use68501 = (void*)&foo68501;
+__attribute__((used)) void* use68502 = (void*)&foo68502;
+__attribute__((used)) void* use68503 = (void*)&foo68503;
+__attribute__((used)) void* use68504 = (void*)&foo68504;
+__attribute__((used)) void* use68505 = (void*)&foo68505;
+__attribute__((used)) void* use68506 = (void*)&foo68506;
+__attribute__((used)) void* use68507 = (void*)&foo68507;
+__attribute__((used)) void* use68508 = (void*)&foo68508;
+__attribute__((used)) void* use68509 = (void*)&foo68509;
+__attribute__((used)) void* use68510 = (void*)&foo68510;
+__attribute__((used)) void* use68511 = (void*)&foo68511;
+__attribute__((used)) void* use68512 = (void*)&foo68512;
+__attribute__((used)) void* use68513 = (void*)&foo68513;
+__attribute__((used)) void* use68514 = (void*)&foo68514;
+__attribute__((used)) void* use68515 = (void*)&foo68515;
+__attribute__((used)) void* use68516 = (void*)&foo68516;
+__attribute__((used)) void* use68517 = (void*)&foo68517;
+__attribute__((used)) void* use68518 = (void*)&foo68518;
+__attribute__((used)) void* use68519 = (void*)&foo68519;
+__attribute__((used)) void* use68520 = (void*)&foo68520;
+__attribute__((used)) void* use68521 = (void*)&foo68521;
+__attribute__((used)) void* use68522 = (void*)&foo68522;
+__attribute__((used)) void* use68523 = (void*)&foo68523;
+__attribute__((used)) void* use68524 = (void*)&foo68524;
+__attribute__((used)) void* use68525 = (void*)&foo68525;
+__attribute__((used)) void* use68526 = (void*)&foo68526;
+__attribute__((used)) void* use68527 = (void*)&foo68527;
+__attribute__((used)) void* use68528 = (void*)&foo68528;
+__attribute__((used)) void* use68529 = (void*)&foo68529;
+__attribute__((used)) void* use68530 = (void*)&foo68530;
+__attribute__((used)) void* use68531 = (void*)&foo68531;
+__attribute__((used)) void* use68532 = (void*)&foo68532;
+__attribute__((used)) void* use68533 = (void*)&foo68533;
+__attribute__((used)) void* use68534 = (void*)&foo68534;
+__attribute__((used)) void* use68535 = (void*)&foo68535;
+__attribute__((used)) void* use68536 = (void*)&foo68536;
+__attribute__((used)) void* use68537 = (void*)&foo68537;
+__attribute__((used)) void* use68538 = (void*)&foo68538;
+__attribute__((used)) void* use68539 = (void*)&foo68539;
+__attribute__((used)) void* use68540 = (void*)&foo68540;
+__attribute__((used)) void* use68541 = (void*)&foo68541;
+__attribute__((used)) void* use68542 = (void*)&foo68542;
+__attribute__((used)) void* use68543 = (void*)&foo68543;
+__attribute__((used)) void* use68544 = (void*)&foo68544;
+__attribute__((used)) void* use68545 = (void*)&foo68545;
+__attribute__((used)) void* use68546 = (void*)&foo68546;
+__attribute__((used)) void* use68547 = (void*)&foo68547;
+__attribute__((used)) void* use68548 = (void*)&foo68548;
+__attribute__((used)) void* use68549 = (void*)&foo68549;
+__attribute__((used)) void* use68550 = (void*)&foo68550;
+__attribute__((used)) void* use68551 = (void*)&foo68551;
+__attribute__((used)) void* use68552 = (void*)&foo68552;
+__attribute__((used)) void* use68553 = (void*)&foo68553;
+__attribute__((used)) void* use68554 = (void*)&foo68554;
+__attribute__((used)) void* use68555 = (void*)&foo68555;
+__attribute__((used)) void* use68556 = (void*)&foo68556;
+__attribute__((used)) void* use68557 = (void*)&foo68557;
+__attribute__((used)) void* use68558 = (void*)&foo68558;
+__attribute__((used)) void* use68559 = (void*)&foo68559;
+__attribute__((used)) void* use68560 = (void*)&foo68560;
+__attribute__((used)) void* use68561 = (void*)&foo68561;
+__attribute__((used)) void* use68562 = (void*)&foo68562;
+__attribute__((used)) void* use68563 = (void*)&foo68563;
+__attribute__((used)) void* use68564 = (void*)&foo68564;
+__attribute__((used)) void* use68565 = (void*)&foo68565;
+__attribute__((used)) void* use68566 = (void*)&foo68566;
+__attribute__((used)) void* use68567 = (void*)&foo68567;
+__attribute__((used)) void* use68568 = (void*)&foo68568;
+__attribute__((used)) void* use68569 = (void*)&foo68569;
+__attribute__((used)) void* use68570 = (void*)&foo68570;
+__attribute__((used)) void* use68571 = (void*)&foo68571;
+__attribute__((used)) void* use68572 = (void*)&foo68572;
+__attribute__((used)) void* use68573 = (void*)&foo68573;
+__attribute__((used)) void* use68574 = (void*)&foo68574;
+__attribute__((used)) void* use68575 = (void*)&foo68575;
+__attribute__((used)) void* use68576 = (void*)&foo68576;
+__attribute__((used)) void* use68577 = (void*)&foo68577;
+__attribute__((used)) void* use68578 = (void*)&foo68578;
+__attribute__((used)) void* use68579 = (void*)&foo68579;
+__attribute__((used)) void* use68580 = (void*)&foo68580;
+__attribute__((used)) void* use68581 = (void*)&foo68581;
+__attribute__((used)) void* use68582 = (void*)&foo68582;
+__attribute__((used)) void* use68583 = (void*)&foo68583;
+__attribute__((used)) void* use68584 = (void*)&foo68584;
+__attribute__((used)) void* use68585 = (void*)&foo68585;
+__attribute__((used)) void* use68586 = (void*)&foo68586;
+__attribute__((used)) void* use68587 = (void*)&foo68587;
+__attribute__((used)) void* use68588 = (void*)&foo68588;
+__attribute__((used)) void* use68589 = (void*)&foo68589;
+__attribute__((used)) void* use68590 = (void*)&foo68590;
+__attribute__((used)) void* use68591 = (void*)&foo68591;
+__attribute__((used)) void* use68592 = (void*)&foo68592;
+__attribute__((used)) void* use68593 = (void*)&foo68593;
+__attribute__((used)) void* use68594 = (void*)&foo68594;
+__attribute__((used)) void* use68595 = (void*)&foo68595;
+__attribute__((used)) void* use68596 = (void*)&foo68596;
+__attribute__((used)) void* use68597 = (void*)&foo68597;
+__attribute__((used)) void* use68598 = (void*)&foo68598;
+__attribute__((used)) void* use68599 = (void*)&foo68599;
+__attribute__((used)) void* use68600 = (void*)&foo68600;
+__attribute__((used)) void* use68601 = (void*)&foo68601;
+__attribute__((used)) void* use68602 = (void*)&foo68602;
+__attribute__((used)) void* use68603 = (void*)&foo68603;
+__attribute__((used)) void* use68604 = (void*)&foo68604;
+__attribute__((used)) void* use68605 = (void*)&foo68605;
+__attribute__((used)) void* use68606 = (void*)&foo68606;
+__attribute__((used)) void* use68607 = (void*)&foo68607;
+__attribute__((used)) void* use68608 = (void*)&foo68608;
+__attribute__((used)) void* use68609 = (void*)&foo68609;
+__attribute__((used)) void* use68610 = (void*)&foo68610;
+__attribute__((used)) void* use68611 = (void*)&foo68611;
+__attribute__((used)) void* use68612 = (void*)&foo68612;
+__attribute__((used)) void* use68613 = (void*)&foo68613;
+__attribute__((used)) void* use68614 = (void*)&foo68614;
+__attribute__((used)) void* use68615 = (void*)&foo68615;
+__attribute__((used)) void* use68616 = (void*)&foo68616;
+__attribute__((used)) void* use68617 = (void*)&foo68617;
+__attribute__((used)) void* use68618 = (void*)&foo68618;
+__attribute__((used)) void* use68619 = (void*)&foo68619;
+__attribute__((used)) void* use68620 = (void*)&foo68620;
+__attribute__((used)) void* use68621 = (void*)&foo68621;
+__attribute__((used)) void* use68622 = (void*)&foo68622;
+__attribute__((used)) void* use68623 = (void*)&foo68623;
+__attribute__((used)) void* use68624 = (void*)&foo68624;
+__attribute__((used)) void* use68625 = (void*)&foo68625;
+__attribute__((used)) void* use68626 = (void*)&foo68626;
+__attribute__((used)) void* use68627 = (void*)&foo68627;
+__attribute__((used)) void* use68628 = (void*)&foo68628;
+__attribute__((used)) void* use68629 = (void*)&foo68629;
+__attribute__((used)) void* use68630 = (void*)&foo68630;
+__attribute__((used)) void* use68631 = (void*)&foo68631;
+__attribute__((used)) void* use68632 = (void*)&foo68632;
+__attribute__((used)) void* use68633 = (void*)&foo68633;
+__attribute__((used)) void* use68634 = (void*)&foo68634;
+__attribute__((used)) void* use68635 = (void*)&foo68635;
+__attribute__((used)) void* use68636 = (void*)&foo68636;
+__attribute__((used)) void* use68637 = (void*)&foo68637;
+__attribute__((used)) void* use68638 = (void*)&foo68638;
+__attribute__((used)) void* use68639 = (void*)&foo68639;
+__attribute__((used)) void* use68640 = (void*)&foo68640;
+__attribute__((used)) void* use68641 = (void*)&foo68641;
+__attribute__((used)) void* use68642 = (void*)&foo68642;
+__attribute__((used)) void* use68643 = (void*)&foo68643;
+__attribute__((used)) void* use68644 = (void*)&foo68644;
+__attribute__((used)) void* use68645 = (void*)&foo68645;
+__attribute__((used)) void* use68646 = (void*)&foo68646;
+__attribute__((used)) void* use68647 = (void*)&foo68647;
+__attribute__((used)) void* use68648 = (void*)&foo68648;
+__attribute__((used)) void* use68649 = (void*)&foo68649;
+__attribute__((used)) void* use68650 = (void*)&foo68650;
+__attribute__((used)) void* use68651 = (void*)&foo68651;
+__attribute__((used)) void* use68652 = (void*)&foo68652;
+__attribute__((used)) void* use68653 = (void*)&foo68653;
+__attribute__((used)) void* use68654 = (void*)&foo68654;
+__attribute__((used)) void* use68655 = (void*)&foo68655;
+__attribute__((used)) void* use68656 = (void*)&foo68656;
+__attribute__((used)) void* use68657 = (void*)&foo68657;
+__attribute__((used)) void* use68658 = (void*)&foo68658;
+__attribute__((used)) void* use68659 = (void*)&foo68659;
+__attribute__((used)) void* use68660 = (void*)&foo68660;
+__attribute__((used)) void* use68661 = (void*)&foo68661;
+__attribute__((used)) void* use68662 = (void*)&foo68662;
+__attribute__((used)) void* use68663 = (void*)&foo68663;
+__attribute__((used)) void* use68664 = (void*)&foo68664;
+__attribute__((used)) void* use68665 = (void*)&foo68665;
+__attribute__((used)) void* use68666 = (void*)&foo68666;
+__attribute__((used)) void* use68667 = (void*)&foo68667;
+__attribute__((used)) void* use68668 = (void*)&foo68668;
+__attribute__((used)) void* use68669 = (void*)&foo68669;
+__attribute__((used)) void* use68670 = (void*)&foo68670;
+__attribute__((used)) void* use68671 = (void*)&foo68671;
+__attribute__((used)) void* use68672 = (void*)&foo68672;
+__attribute__((used)) void* use68673 = (void*)&foo68673;
+__attribute__((used)) void* use68674 = (void*)&foo68674;
+__attribute__((used)) void* use68675 = (void*)&foo68675;
+__attribute__((used)) void* use68676 = (void*)&foo68676;
+__attribute__((used)) void* use68677 = (void*)&foo68677;
+__attribute__((used)) void* use68678 = (void*)&foo68678;
+__attribute__((used)) void* use68679 = (void*)&foo68679;
+__attribute__((used)) void* use68680 = (void*)&foo68680;
+__attribute__((used)) void* use68681 = (void*)&foo68681;
+__attribute__((used)) void* use68682 = (void*)&foo68682;
+__attribute__((used)) void* use68683 = (void*)&foo68683;
+__attribute__((used)) void* use68684 = (void*)&foo68684;
+__attribute__((used)) void* use68685 = (void*)&foo68685;
+__attribute__((used)) void* use68686 = (void*)&foo68686;
+__attribute__((used)) void* use68687 = (void*)&foo68687;
+__attribute__((used)) void* use68688 = (void*)&foo68688;
+__attribute__((used)) void* use68689 = (void*)&foo68689;
+__attribute__((used)) void* use68690 = (void*)&foo68690;
+__attribute__((used)) void* use68691 = (void*)&foo68691;
+__attribute__((used)) void* use68692 = (void*)&foo68692;
+__attribute__((used)) void* use68693 = (void*)&foo68693;
+__attribute__((used)) void* use68694 = (void*)&foo68694;
+__attribute__((used)) void* use68695 = (void*)&foo68695;
+__attribute__((used)) void* use68696 = (void*)&foo68696;
+__attribute__((used)) void* use68697 = (void*)&foo68697;
+__attribute__((used)) void* use68698 = (void*)&foo68698;
+__attribute__((used)) void* use68699 = (void*)&foo68699;
+__attribute__((used)) void* use68700 = (void*)&foo68700;
+__attribute__((used)) void* use68701 = (void*)&foo68701;
+__attribute__((used)) void* use68702 = (void*)&foo68702;
+__attribute__((used)) void* use68703 = (void*)&foo68703;
+__attribute__((used)) void* use68704 = (void*)&foo68704;
+__attribute__((used)) void* use68705 = (void*)&foo68705;
+__attribute__((used)) void* use68706 = (void*)&foo68706;
+__attribute__((used)) void* use68707 = (void*)&foo68707;
+__attribute__((used)) void* use68708 = (void*)&foo68708;
+__attribute__((used)) void* use68709 = (void*)&foo68709;
+__attribute__((used)) void* use68710 = (void*)&foo68710;
+__attribute__((used)) void* use68711 = (void*)&foo68711;
+__attribute__((used)) void* use68712 = (void*)&foo68712;
+__attribute__((used)) void* use68713 = (void*)&foo68713;
+__attribute__((used)) void* use68714 = (void*)&foo68714;
+__attribute__((used)) void* use68715 = (void*)&foo68715;
+__attribute__((used)) void* use68716 = (void*)&foo68716;
+__attribute__((used)) void* use68717 = (void*)&foo68717;
+__attribute__((used)) void* use68718 = (void*)&foo68718;
+__attribute__((used)) void* use68719 = (void*)&foo68719;
+__attribute__((used)) void* use68720 = (void*)&foo68720;
+__attribute__((used)) void* use68721 = (void*)&foo68721;
+__attribute__((used)) void* use68722 = (void*)&foo68722;
+__attribute__((used)) void* use68723 = (void*)&foo68723;
+__attribute__((used)) void* use68724 = (void*)&foo68724;
+__attribute__((used)) void* use68725 = (void*)&foo68725;
+__attribute__((used)) void* use68726 = (void*)&foo68726;
+__attribute__((used)) void* use68727 = (void*)&foo68727;
+__attribute__((used)) void* use68728 = (void*)&foo68728;
+__attribute__((used)) void* use68729 = (void*)&foo68729;
+__attribute__((used)) void* use68730 = (void*)&foo68730;
+__attribute__((used)) void* use68731 = (void*)&foo68731;
+__attribute__((used)) void* use68732 = (void*)&foo68732;
+__attribute__((used)) void* use68733 = (void*)&foo68733;
+__attribute__((used)) void* use68734 = (void*)&foo68734;
+__attribute__((used)) void* use68735 = (void*)&foo68735;
+__attribute__((used)) void* use68736 = (void*)&foo68736;
+__attribute__((used)) void* use68737 = (void*)&foo68737;
+__attribute__((used)) void* use68738 = (void*)&foo68738;
+__attribute__((used)) void* use68739 = (void*)&foo68739;
+__attribute__((used)) void* use68740 = (void*)&foo68740;
+__attribute__((used)) void* use68741 = (void*)&foo68741;
+__attribute__((used)) void* use68742 = (void*)&foo68742;
+__attribute__((used)) void* use68743 = (void*)&foo68743;
+__attribute__((used)) void* use68744 = (void*)&foo68744;
+__attribute__((used)) void* use68745 = (void*)&foo68745;
+__attribute__((used)) void* use68746 = (void*)&foo68746;
+__attribute__((used)) void* use68747 = (void*)&foo68747;
+__attribute__((used)) void* use68748 = (void*)&foo68748;
+__attribute__((used)) void* use68749 = (void*)&foo68749;
+__attribute__((used)) void* use68750 = (void*)&foo68750;
+__attribute__((used)) void* use68751 = (void*)&foo68751;
+__attribute__((used)) void* use68752 = (void*)&foo68752;
+__attribute__((used)) void* use68753 = (void*)&foo68753;
+__attribute__((used)) void* use68754 = (void*)&foo68754;
+__attribute__((used)) void* use68755 = (void*)&foo68755;
+__attribute__((used)) void* use68756 = (void*)&foo68756;
+__attribute__((used)) void* use68757 = (void*)&foo68757;
+__attribute__((used)) void* use68758 = (void*)&foo68758;
+__attribute__((used)) void* use68759 = (void*)&foo68759;
+__attribute__((used)) void* use68760 = (void*)&foo68760;
+__attribute__((used)) void* use68761 = (void*)&foo68761;
+__attribute__((used)) void* use68762 = (void*)&foo68762;
+__attribute__((used)) void* use68763 = (void*)&foo68763;
+__attribute__((used)) void* use68764 = (void*)&foo68764;
+__attribute__((used)) void* use68765 = (void*)&foo68765;
+__attribute__((used)) void* use68766 = (void*)&foo68766;
+__attribute__((used)) void* use68767 = (void*)&foo68767;
+__attribute__((used)) void* use68768 = (void*)&foo68768;
+__attribute__((used)) void* use68769 = (void*)&foo68769;
+__attribute__((used)) void* use68770 = (void*)&foo68770;
+__attribute__((used)) void* use68771 = (void*)&foo68771;
+__attribute__((used)) void* use68772 = (void*)&foo68772;
+__attribute__((used)) void* use68773 = (void*)&foo68773;
+__attribute__((used)) void* use68774 = (void*)&foo68774;
+__attribute__((used)) void* use68775 = (void*)&foo68775;
+__attribute__((used)) void* use68776 = (void*)&foo68776;
+__attribute__((used)) void* use68777 = (void*)&foo68777;
+__attribute__((used)) void* use68778 = (void*)&foo68778;
+__attribute__((used)) void* use68779 = (void*)&foo68779;
+__attribute__((used)) void* use68780 = (void*)&foo68780;
+__attribute__((used)) void* use68781 = (void*)&foo68781;
+__attribute__((used)) void* use68782 = (void*)&foo68782;
+__attribute__((used)) void* use68783 = (void*)&foo68783;
+__attribute__((used)) void* use68784 = (void*)&foo68784;
+__attribute__((used)) void* use68785 = (void*)&foo68785;
+__attribute__((used)) void* use68786 = (void*)&foo68786;
+__attribute__((used)) void* use68787 = (void*)&foo68787;
+__attribute__((used)) void* use68788 = (void*)&foo68788;
+__attribute__((used)) void* use68789 = (void*)&foo68789;
+__attribute__((used)) void* use68790 = (void*)&foo68790;
+__attribute__((used)) void* use68791 = (void*)&foo68791;
+__attribute__((used)) void* use68792 = (void*)&foo68792;
+__attribute__((used)) void* use68793 = (void*)&foo68793;
+__attribute__((used)) void* use68794 = (void*)&foo68794;
+__attribute__((used)) void* use68795 = (void*)&foo68795;
+__attribute__((used)) void* use68796 = (void*)&foo68796;
+__attribute__((used)) void* use68797 = (void*)&foo68797;
+__attribute__((used)) void* use68798 = (void*)&foo68798;
+__attribute__((used)) void* use68799 = (void*)&foo68799;
+__attribute__((used)) void* use68800 = (void*)&foo68800;
+__attribute__((used)) void* use68801 = (void*)&foo68801;
+__attribute__((used)) void* use68802 = (void*)&foo68802;
+__attribute__((used)) void* use68803 = (void*)&foo68803;
+__attribute__((used)) void* use68804 = (void*)&foo68804;
+__attribute__((used)) void* use68805 = (void*)&foo68805;
+__attribute__((used)) void* use68806 = (void*)&foo68806;
+__attribute__((used)) void* use68807 = (void*)&foo68807;
+__attribute__((used)) void* use68808 = (void*)&foo68808;
+__attribute__((used)) void* use68809 = (void*)&foo68809;
+__attribute__((used)) void* use68810 = (void*)&foo68810;
+__attribute__((used)) void* use68811 = (void*)&foo68811;
+__attribute__((used)) void* use68812 = (void*)&foo68812;
+__attribute__((used)) void* use68813 = (void*)&foo68813;
+__attribute__((used)) void* use68814 = (void*)&foo68814;
+__attribute__((used)) void* use68815 = (void*)&foo68815;
+__attribute__((used)) void* use68816 = (void*)&foo68816;
+__attribute__((used)) void* use68817 = (void*)&foo68817;
+__attribute__((used)) void* use68818 = (void*)&foo68818;
+__attribute__((used)) void* use68819 = (void*)&foo68819;
+__attribute__((used)) void* use68820 = (void*)&foo68820;
+__attribute__((used)) void* use68821 = (void*)&foo68821;
+__attribute__((used)) void* use68822 = (void*)&foo68822;
+__attribute__((used)) void* use68823 = (void*)&foo68823;
+__attribute__((used)) void* use68824 = (void*)&foo68824;
+__attribute__((used)) void* use68825 = (void*)&foo68825;
+__attribute__((used)) void* use68826 = (void*)&foo68826;
+__attribute__((used)) void* use68827 = (void*)&foo68827;
+__attribute__((used)) void* use68828 = (void*)&foo68828;
+__attribute__((used)) void* use68829 = (void*)&foo68829;
+__attribute__((used)) void* use68830 = (void*)&foo68830;
+__attribute__((used)) void* use68831 = (void*)&foo68831;
+__attribute__((used)) void* use68832 = (void*)&foo68832;
+__attribute__((used)) void* use68833 = (void*)&foo68833;
+__attribute__((used)) void* use68834 = (void*)&foo68834;
+__attribute__((used)) void* use68835 = (void*)&foo68835;
+__attribute__((used)) void* use68836 = (void*)&foo68836;
+__attribute__((used)) void* use68837 = (void*)&foo68837;
+__attribute__((used)) void* use68838 = (void*)&foo68838;
+__attribute__((used)) void* use68839 = (void*)&foo68839;
+__attribute__((used)) void* use68840 = (void*)&foo68840;
+__attribute__((used)) void* use68841 = (void*)&foo68841;
+__attribute__((used)) void* use68842 = (void*)&foo68842;
+__attribute__((used)) void* use68843 = (void*)&foo68843;
+__attribute__((used)) void* use68844 = (void*)&foo68844;
+__attribute__((used)) void* use68845 = (void*)&foo68845;
+__attribute__((used)) void* use68846 = (void*)&foo68846;
+__attribute__((used)) void* use68847 = (void*)&foo68847;
+__attribute__((used)) void* use68848 = (void*)&foo68848;
+__attribute__((used)) void* use68849 = (void*)&foo68849;
+__attribute__((used)) void* use68850 = (void*)&foo68850;
+__attribute__((used)) void* use68851 = (void*)&foo68851;
+__attribute__((used)) void* use68852 = (void*)&foo68852;
+__attribute__((used)) void* use68853 = (void*)&foo68853;
+__attribute__((used)) void* use68854 = (void*)&foo68854;
+__attribute__((used)) void* use68855 = (void*)&foo68855;
+__attribute__((used)) void* use68856 = (void*)&foo68856;
+__attribute__((used)) void* use68857 = (void*)&foo68857;
+__attribute__((used)) void* use68858 = (void*)&foo68858;
+__attribute__((used)) void* use68859 = (void*)&foo68859;
+__attribute__((used)) void* use68860 = (void*)&foo68860;
+__attribute__((used)) void* use68861 = (void*)&foo68861;
+__attribute__((used)) void* use68862 = (void*)&foo68862;
+__attribute__((used)) void* use68863 = (void*)&foo68863;
+__attribute__((used)) void* use68864 = (void*)&foo68864;
+__attribute__((used)) void* use68865 = (void*)&foo68865;
+__attribute__((used)) void* use68866 = (void*)&foo68866;
+__attribute__((used)) void* use68867 = (void*)&foo68867;
+__attribute__((used)) void* use68868 = (void*)&foo68868;
+__attribute__((used)) void* use68869 = (void*)&foo68869;
+__attribute__((used)) void* use68870 = (void*)&foo68870;
+__attribute__((used)) void* use68871 = (void*)&foo68871;
+__attribute__((used)) void* use68872 = (void*)&foo68872;
+__attribute__((used)) void* use68873 = (void*)&foo68873;
+__attribute__((used)) void* use68874 = (void*)&foo68874;
+__attribute__((used)) void* use68875 = (void*)&foo68875;
+__attribute__((used)) void* use68876 = (void*)&foo68876;
+__attribute__((used)) void* use68877 = (void*)&foo68877;
+__attribute__((used)) void* use68878 = (void*)&foo68878;
+__attribute__((used)) void* use68879 = (void*)&foo68879;
+__attribute__((used)) void* use68880 = (void*)&foo68880;
+__attribute__((used)) void* use68881 = (void*)&foo68881;
+__attribute__((used)) void* use68882 = (void*)&foo68882;
+__attribute__((used)) void* use68883 = (void*)&foo68883;
+__attribute__((used)) void* use68884 = (void*)&foo68884;
+__attribute__((used)) void* use68885 = (void*)&foo68885;
+__attribute__((used)) void* use68886 = (void*)&foo68886;
+__attribute__((used)) void* use68887 = (void*)&foo68887;
+__attribute__((used)) void* use68888 = (void*)&foo68888;
+__attribute__((used)) void* use68889 = (void*)&foo68889;
+__attribute__((used)) void* use68890 = (void*)&foo68890;
+__attribute__((used)) void* use68891 = (void*)&foo68891;
+__attribute__((used)) void* use68892 = (void*)&foo68892;
+__attribute__((used)) void* use68893 = (void*)&foo68893;
+__attribute__((used)) void* use68894 = (void*)&foo68894;
+__attribute__((used)) void* use68895 = (void*)&foo68895;
+__attribute__((used)) void* use68896 = (void*)&foo68896;
+__attribute__((used)) void* use68897 = (void*)&foo68897;
+__attribute__((used)) void* use68898 = (void*)&foo68898;
+__attribute__((used)) void* use68899 = (void*)&foo68899;
+__attribute__((used)) void* use68900 = (void*)&foo68900;
+__attribute__((used)) void* use68901 = (void*)&foo68901;
+__attribute__((used)) void* use68902 = (void*)&foo68902;
+__attribute__((used)) void* use68903 = (void*)&foo68903;
+__attribute__((used)) void* use68904 = (void*)&foo68904;
+__attribute__((used)) void* use68905 = (void*)&foo68905;
+__attribute__((used)) void* use68906 = (void*)&foo68906;
+__attribute__((used)) void* use68907 = (void*)&foo68907;
+__attribute__((used)) void* use68908 = (void*)&foo68908;
+__attribute__((used)) void* use68909 = (void*)&foo68909;
+__attribute__((used)) void* use68910 = (void*)&foo68910;
+__attribute__((used)) void* use68911 = (void*)&foo68911;
+__attribute__((used)) void* use68912 = (void*)&foo68912;
+__attribute__((used)) void* use68913 = (void*)&foo68913;
+__attribute__((used)) void* use68914 = (void*)&foo68914;
+__attribute__((used)) void* use68915 = (void*)&foo68915;
+__attribute__((used)) void* use68916 = (void*)&foo68916;
+__attribute__((used)) void* use68917 = (void*)&foo68917;
+__attribute__((used)) void* use68918 = (void*)&foo68918;
+__attribute__((used)) void* use68919 = (void*)&foo68919;
+__attribute__((used)) void* use68920 = (void*)&foo68920;
+__attribute__((used)) void* use68921 = (void*)&foo68921;
+__attribute__((used)) void* use68922 = (void*)&foo68922;
+__attribute__((used)) void* use68923 = (void*)&foo68923;
+__attribute__((used)) void* use68924 = (void*)&foo68924;
+__attribute__((used)) void* use68925 = (void*)&foo68925;
+__attribute__((used)) void* use68926 = (void*)&foo68926;
+__attribute__((used)) void* use68927 = (void*)&foo68927;
+__attribute__((used)) void* use68928 = (void*)&foo68928;
+__attribute__((used)) void* use68929 = (void*)&foo68929;
+__attribute__((used)) void* use68930 = (void*)&foo68930;
+__attribute__((used)) void* use68931 = (void*)&foo68931;
+__attribute__((used)) void* use68932 = (void*)&foo68932;
+__attribute__((used)) void* use68933 = (void*)&foo68933;
+__attribute__((used)) void* use68934 = (void*)&foo68934;
+__attribute__((used)) void* use68935 = (void*)&foo68935;
+__attribute__((used)) void* use68936 = (void*)&foo68936;
+__attribute__((used)) void* use68937 = (void*)&foo68937;
+__attribute__((used)) void* use68938 = (void*)&foo68938;
+__attribute__((used)) void* use68939 = (void*)&foo68939;
+__attribute__((used)) void* use68940 = (void*)&foo68940;
+__attribute__((used)) void* use68941 = (void*)&foo68941;
+__attribute__((used)) void* use68942 = (void*)&foo68942;
+__attribute__((used)) void* use68943 = (void*)&foo68943;
+__attribute__((used)) void* use68944 = (void*)&foo68944;
+__attribute__((used)) void* use68945 = (void*)&foo68945;
+__attribute__((used)) void* use68946 = (void*)&foo68946;
+__attribute__((used)) void* use68947 = (void*)&foo68947;
+__attribute__((used)) void* use68948 = (void*)&foo68948;
+__attribute__((used)) void* use68949 = (void*)&foo68949;
+__attribute__((used)) void* use68950 = (void*)&foo68950;
+__attribute__((used)) void* use68951 = (void*)&foo68951;
+__attribute__((used)) void* use68952 = (void*)&foo68952;
+__attribute__((used)) void* use68953 = (void*)&foo68953;
+__attribute__((used)) void* use68954 = (void*)&foo68954;
+__attribute__((used)) void* use68955 = (void*)&foo68955;
+__attribute__((used)) void* use68956 = (void*)&foo68956;
+__attribute__((used)) void* use68957 = (void*)&foo68957;
+__attribute__((used)) void* use68958 = (void*)&foo68958;
+__attribute__((used)) void* use68959 = (void*)&foo68959;
+__attribute__((used)) void* use68960 = (void*)&foo68960;
+__attribute__((used)) void* use68961 = (void*)&foo68961;
+__attribute__((used)) void* use68962 = (void*)&foo68962;
+__attribute__((used)) void* use68963 = (void*)&foo68963;
+__attribute__((used)) void* use68964 = (void*)&foo68964;
+__attribute__((used)) void* use68965 = (void*)&foo68965;
+__attribute__((used)) void* use68966 = (void*)&foo68966;
+__attribute__((used)) void* use68967 = (void*)&foo68967;
+__attribute__((used)) void* use68968 = (void*)&foo68968;
+__attribute__((used)) void* use68969 = (void*)&foo68969;
+__attribute__((used)) void* use68970 = (void*)&foo68970;
+__attribute__((used)) void* use68971 = (void*)&foo68971;
+__attribute__((used)) void* use68972 = (void*)&foo68972;
+__attribute__((used)) void* use68973 = (void*)&foo68973;
+__attribute__((used)) void* use68974 = (void*)&foo68974;
+__attribute__((used)) void* use68975 = (void*)&foo68975;
+__attribute__((used)) void* use68976 = (void*)&foo68976;
+__attribute__((used)) void* use68977 = (void*)&foo68977;
+__attribute__((used)) void* use68978 = (void*)&foo68978;
+__attribute__((used)) void* use68979 = (void*)&foo68979;
+__attribute__((used)) void* use68980 = (void*)&foo68980;
+__attribute__((used)) void* use68981 = (void*)&foo68981;
+__attribute__((used)) void* use68982 = (void*)&foo68982;
+__attribute__((used)) void* use68983 = (void*)&foo68983;
+__attribute__((used)) void* use68984 = (void*)&foo68984;
+__attribute__((used)) void* use68985 = (void*)&foo68985;
+__attribute__((used)) void* use68986 = (void*)&foo68986;
+__attribute__((used)) void* use68987 = (void*)&foo68987;
+__attribute__((used)) void* use68988 = (void*)&foo68988;
+__attribute__((used)) void* use68989 = (void*)&foo68989;
+__attribute__((used)) void* use68990 = (void*)&foo68990;
+__attribute__((used)) void* use68991 = (void*)&foo68991;
+__attribute__((used)) void* use68992 = (void*)&foo68992;
+__attribute__((used)) void* use68993 = (void*)&foo68993;
+__attribute__((used)) void* use68994 = (void*)&foo68994;
+__attribute__((used)) void* use68995 = (void*)&foo68995;
+__attribute__((used)) void* use68996 = (void*)&foo68996;
+__attribute__((used)) void* use68997 = (void*)&foo68997;
+__attribute__((used)) void* use68998 = (void*)&foo68998;
+__attribute__((used)) void* use68999 = (void*)&foo68999;
+__attribute__((used)) void* use69000 = (void*)&foo69000;
+__attribute__((used)) void* use69001 = (void*)&foo69001;
+__attribute__((used)) void* use69002 = (void*)&foo69002;
+__attribute__((used)) void* use69003 = (void*)&foo69003;
+__attribute__((used)) void* use69004 = (void*)&foo69004;
+__attribute__((used)) void* use69005 = (void*)&foo69005;
+__attribute__((used)) void* use69006 = (void*)&foo69006;
+__attribute__((used)) void* use69007 = (void*)&foo69007;
+__attribute__((used)) void* use69008 = (void*)&foo69008;
+__attribute__((used)) void* use69009 = (void*)&foo69009;
+__attribute__((used)) void* use69010 = (void*)&foo69010;
+__attribute__((used)) void* use69011 = (void*)&foo69011;
+__attribute__((used)) void* use69012 = (void*)&foo69012;
+__attribute__((used)) void* use69013 = (void*)&foo69013;
+__attribute__((used)) void* use69014 = (void*)&foo69014;
+__attribute__((used)) void* use69015 = (void*)&foo69015;
+__attribute__((used)) void* use69016 = (void*)&foo69016;
+__attribute__((used)) void* use69017 = (void*)&foo69017;
+__attribute__((used)) void* use69018 = (void*)&foo69018;
+__attribute__((used)) void* use69019 = (void*)&foo69019;
+__attribute__((used)) void* use69020 = (void*)&foo69020;
+__attribute__((used)) void* use69021 = (void*)&foo69021;
+__attribute__((used)) void* use69022 = (void*)&foo69022;
+__attribute__((used)) void* use69023 = (void*)&foo69023;
+__attribute__((used)) void* use69024 = (void*)&foo69024;
+__attribute__((used)) void* use69025 = (void*)&foo69025;
+__attribute__((used)) void* use69026 = (void*)&foo69026;
+__attribute__((used)) void* use69027 = (void*)&foo69027;
+__attribute__((used)) void* use69028 = (void*)&foo69028;
+__attribute__((used)) void* use69029 = (void*)&foo69029;
+__attribute__((used)) void* use69030 = (void*)&foo69030;
+__attribute__((used)) void* use69031 = (void*)&foo69031;
+__attribute__((used)) void* use69032 = (void*)&foo69032;
+__attribute__((used)) void* use69033 = (void*)&foo69033;
+__attribute__((used)) void* use69034 = (void*)&foo69034;
+__attribute__((used)) void* use69035 = (void*)&foo69035;
+__attribute__((used)) void* use69036 = (void*)&foo69036;
+__attribute__((used)) void* use69037 = (void*)&foo69037;
+__attribute__((used)) void* use69038 = (void*)&foo69038;
+__attribute__((used)) void* use69039 = (void*)&foo69039;
+__attribute__((used)) void* use69040 = (void*)&foo69040;
+__attribute__((used)) void* use69041 = (void*)&foo69041;
+__attribute__((used)) void* use69042 = (void*)&foo69042;
+__attribute__((used)) void* use69043 = (void*)&foo69043;
+__attribute__((used)) void* use69044 = (void*)&foo69044;
+__attribute__((used)) void* use69045 = (void*)&foo69045;
+__attribute__((used)) void* use69046 = (void*)&foo69046;
+__attribute__((used)) void* use69047 = (void*)&foo69047;
+__attribute__((used)) void* use69048 = (void*)&foo69048;
+__attribute__((used)) void* use69049 = (void*)&foo69049;
+__attribute__((used)) void* use69050 = (void*)&foo69050;
+__attribute__((used)) void* use69051 = (void*)&foo69051;
+__attribute__((used)) void* use69052 = (void*)&foo69052;
+__attribute__((used)) void* use69053 = (void*)&foo69053;
+__attribute__((used)) void* use69054 = (void*)&foo69054;
+__attribute__((used)) void* use69055 = (void*)&foo69055;
+__attribute__((used)) void* use69056 = (void*)&foo69056;
+__attribute__((used)) void* use69057 = (void*)&foo69057;
+__attribute__((used)) void* use69058 = (void*)&foo69058;
+__attribute__((used)) void* use69059 = (void*)&foo69059;
+__attribute__((used)) void* use69060 = (void*)&foo69060;
+__attribute__((used)) void* use69061 = (void*)&foo69061;
+__attribute__((used)) void* use69062 = (void*)&foo69062;
+__attribute__((used)) void* use69063 = (void*)&foo69063;
+__attribute__((used)) void* use69064 = (void*)&foo69064;
+__attribute__((used)) void* use69065 = (void*)&foo69065;
+__attribute__((used)) void* use69066 = (void*)&foo69066;
+__attribute__((used)) void* use69067 = (void*)&foo69067;
+__attribute__((used)) void* use69068 = (void*)&foo69068;
+__attribute__((used)) void* use69069 = (void*)&foo69069;
+__attribute__((used)) void* use69070 = (void*)&foo69070;
+__attribute__((used)) void* use69071 = (void*)&foo69071;
+__attribute__((used)) void* use69072 = (void*)&foo69072;
+__attribute__((used)) void* use69073 = (void*)&foo69073;
+__attribute__((used)) void* use69074 = (void*)&foo69074;
+__attribute__((used)) void* use69075 = (void*)&foo69075;
+__attribute__((used)) void* use69076 = (void*)&foo69076;
+__attribute__((used)) void* use69077 = (void*)&foo69077;
+__attribute__((used)) void* use69078 = (void*)&foo69078;
+__attribute__((used)) void* use69079 = (void*)&foo69079;
+__attribute__((used)) void* use69080 = (void*)&foo69080;
+__attribute__((used)) void* use69081 = (void*)&foo69081;
+__attribute__((used)) void* use69082 = (void*)&foo69082;
+__attribute__((used)) void* use69083 = (void*)&foo69083;
+__attribute__((used)) void* use69084 = (void*)&foo69084;
+__attribute__((used)) void* use69085 = (void*)&foo69085;
+__attribute__((used)) void* use69086 = (void*)&foo69086;
+__attribute__((used)) void* use69087 = (void*)&foo69087;
+__attribute__((used)) void* use69088 = (void*)&foo69088;
+__attribute__((used)) void* use69089 = (void*)&foo69089;
+__attribute__((used)) void* use69090 = (void*)&foo69090;
+__attribute__((used)) void* use69091 = (void*)&foo69091;
+__attribute__((used)) void* use69092 = (void*)&foo69092;
+__attribute__((used)) void* use69093 = (void*)&foo69093;
+__attribute__((used)) void* use69094 = (void*)&foo69094;
+__attribute__((used)) void* use69095 = (void*)&foo69095;
+__attribute__((used)) void* use69096 = (void*)&foo69096;
+__attribute__((used)) void* use69097 = (void*)&foo69097;
+__attribute__((used)) void* use69098 = (void*)&foo69098;
+__attribute__((used)) void* use69099 = (void*)&foo69099;
+__attribute__((used)) void* use69100 = (void*)&foo69100;
+__attribute__((used)) void* use69101 = (void*)&foo69101;
+__attribute__((used)) void* use69102 = (void*)&foo69102;
+__attribute__((used)) void* use69103 = (void*)&foo69103;
+__attribute__((used)) void* use69104 = (void*)&foo69104;
+__attribute__((used)) void* use69105 = (void*)&foo69105;
+__attribute__((used)) void* use69106 = (void*)&foo69106;
+__attribute__((used)) void* use69107 = (void*)&foo69107;
+__attribute__((used)) void* use69108 = (void*)&foo69108;
+__attribute__((used)) void* use69109 = (void*)&foo69109;
+__attribute__((used)) void* use69110 = (void*)&foo69110;
+__attribute__((used)) void* use69111 = (void*)&foo69111;
+__attribute__((used)) void* use69112 = (void*)&foo69112;
+__attribute__((used)) void* use69113 = (void*)&foo69113;
+__attribute__((used)) void* use69114 = (void*)&foo69114;
+__attribute__((used)) void* use69115 = (void*)&foo69115;
+__attribute__((used)) void* use69116 = (void*)&foo69116;
+__attribute__((used)) void* use69117 = (void*)&foo69117;
+__attribute__((used)) void* use69118 = (void*)&foo69118;
+__attribute__((used)) void* use69119 = (void*)&foo69119;
+__attribute__((used)) void* use69120 = (void*)&foo69120;
+__attribute__((used)) void* use69121 = (void*)&foo69121;
+__attribute__((used)) void* use69122 = (void*)&foo69122;
+__attribute__((used)) void* use69123 = (void*)&foo69123;
+__attribute__((used)) void* use69124 = (void*)&foo69124;
+__attribute__((used)) void* use69125 = (void*)&foo69125;
+__attribute__((used)) void* use69126 = (void*)&foo69126;
+__attribute__((used)) void* use69127 = (void*)&foo69127;
+__attribute__((used)) void* use69128 = (void*)&foo69128;
+__attribute__((used)) void* use69129 = (void*)&foo69129;
+__attribute__((used)) void* use69130 = (void*)&foo69130;
+__attribute__((used)) void* use69131 = (void*)&foo69131;
+__attribute__((used)) void* use69132 = (void*)&foo69132;
+__attribute__((used)) void* use69133 = (void*)&foo69133;
+__attribute__((used)) void* use69134 = (void*)&foo69134;
+__attribute__((used)) void* use69135 = (void*)&foo69135;
+__attribute__((used)) void* use69136 = (void*)&foo69136;
+__attribute__((used)) void* use69137 = (void*)&foo69137;
+__attribute__((used)) void* use69138 = (void*)&foo69138;
+__attribute__((used)) void* use69139 = (void*)&foo69139;
+__attribute__((used)) void* use69140 = (void*)&foo69140;
+__attribute__((used)) void* use69141 = (void*)&foo69141;
+__attribute__((used)) void* use69142 = (void*)&foo69142;
+__attribute__((used)) void* use69143 = (void*)&foo69143;
+__attribute__((used)) void* use69144 = (void*)&foo69144;
+__attribute__((used)) void* use69145 = (void*)&foo69145;
+__attribute__((used)) void* use69146 = (void*)&foo69146;
+__attribute__((used)) void* use69147 = (void*)&foo69147;
+__attribute__((used)) void* use69148 = (void*)&foo69148;
+__attribute__((used)) void* use69149 = (void*)&foo69149;
+__attribute__((used)) void* use69150 = (void*)&foo69150;
+__attribute__((used)) void* use69151 = (void*)&foo69151;
+__attribute__((used)) void* use69152 = (void*)&foo69152;
+__attribute__((used)) void* use69153 = (void*)&foo69153;
+__attribute__((used)) void* use69154 = (void*)&foo69154;
+__attribute__((used)) void* use69155 = (void*)&foo69155;
+__attribute__((used)) void* use69156 = (void*)&foo69156;
+__attribute__((used)) void* use69157 = (void*)&foo69157;
+__attribute__((used)) void* use69158 = (void*)&foo69158;
+__attribute__((used)) void* use69159 = (void*)&foo69159;
+__attribute__((used)) void* use69160 = (void*)&foo69160;
+__attribute__((used)) void* use69161 = (void*)&foo69161;
+__attribute__((used)) void* use69162 = (void*)&foo69162;
+__attribute__((used)) void* use69163 = (void*)&foo69163;
+__attribute__((used)) void* use69164 = (void*)&foo69164;
+__attribute__((used)) void* use69165 = (void*)&foo69165;
+__attribute__((used)) void* use69166 = (void*)&foo69166;
+__attribute__((used)) void* use69167 = (void*)&foo69167;
+__attribute__((used)) void* use69168 = (void*)&foo69168;
+__attribute__((used)) void* use69169 = (void*)&foo69169;
+__attribute__((used)) void* use69170 = (void*)&foo69170;
+__attribute__((used)) void* use69171 = (void*)&foo69171;
+__attribute__((used)) void* use69172 = (void*)&foo69172;
+__attribute__((used)) void* use69173 = (void*)&foo69173;
+__attribute__((used)) void* use69174 = (void*)&foo69174;
+__attribute__((used)) void* use69175 = (void*)&foo69175;
+__attribute__((used)) void* use69176 = (void*)&foo69176;
+__attribute__((used)) void* use69177 = (void*)&foo69177;
+__attribute__((used)) void* use69178 = (void*)&foo69178;
+__attribute__((used)) void* use69179 = (void*)&foo69179;
+__attribute__((used)) void* use69180 = (void*)&foo69180;
+__attribute__((used)) void* use69181 = (void*)&foo69181;
+__attribute__((used)) void* use69182 = (void*)&foo69182;
+__attribute__((used)) void* use69183 = (void*)&foo69183;
+__attribute__((used)) void* use69184 = (void*)&foo69184;
+__attribute__((used)) void* use69185 = (void*)&foo69185;
+__attribute__((used)) void* use69186 = (void*)&foo69186;
+__attribute__((used)) void* use69187 = (void*)&foo69187;
+__attribute__((used)) void* use69188 = (void*)&foo69188;
+__attribute__((used)) void* use69189 = (void*)&foo69189;
+__attribute__((used)) void* use69190 = (void*)&foo69190;
+__attribute__((used)) void* use69191 = (void*)&foo69191;
+__attribute__((used)) void* use69192 = (void*)&foo69192;
+__attribute__((used)) void* use69193 = (void*)&foo69193;
+__attribute__((used)) void* use69194 = (void*)&foo69194;
+__attribute__((used)) void* use69195 = (void*)&foo69195;
+__attribute__((used)) void* use69196 = (void*)&foo69196;
+__attribute__((used)) void* use69197 = (void*)&foo69197;
+__attribute__((used)) void* use69198 = (void*)&foo69198;
+__attribute__((used)) void* use69199 = (void*)&foo69199;
+__attribute__((used)) void* use69200 = (void*)&foo69200;
+__attribute__((used)) void* use69201 = (void*)&foo69201;
+__attribute__((used)) void* use69202 = (void*)&foo69202;
+__attribute__((used)) void* use69203 = (void*)&foo69203;
+__attribute__((used)) void* use69204 = (void*)&foo69204;
+__attribute__((used)) void* use69205 = (void*)&foo69205;
+__attribute__((used)) void* use69206 = (void*)&foo69206;
+__attribute__((used)) void* use69207 = (void*)&foo69207;
+__attribute__((used)) void* use69208 = (void*)&foo69208;
+__attribute__((used)) void* use69209 = (void*)&foo69209;
+__attribute__((used)) void* use69210 = (void*)&foo69210;
+__attribute__((used)) void* use69211 = (void*)&foo69211;
+__attribute__((used)) void* use69212 = (void*)&foo69212;
+__attribute__((used)) void* use69213 = (void*)&foo69213;
+__attribute__((used)) void* use69214 = (void*)&foo69214;
+__attribute__((used)) void* use69215 = (void*)&foo69215;
+__attribute__((used)) void* use69216 = (void*)&foo69216;
+__attribute__((used)) void* use69217 = (void*)&foo69217;
+__attribute__((used)) void* use69218 = (void*)&foo69218;
+__attribute__((used)) void* use69219 = (void*)&foo69219;
+__attribute__((used)) void* use69220 = (void*)&foo69220;
+__attribute__((used)) void* use69221 = (void*)&foo69221;
+__attribute__((used)) void* use69222 = (void*)&foo69222;
+__attribute__((used)) void* use69223 = (void*)&foo69223;
+__attribute__((used)) void* use69224 = (void*)&foo69224;
+__attribute__((used)) void* use69225 = (void*)&foo69225;
+__attribute__((used)) void* use69226 = (void*)&foo69226;
+__attribute__((used)) void* use69227 = (void*)&foo69227;
+__attribute__((used)) void* use69228 = (void*)&foo69228;
+__attribute__((used)) void* use69229 = (void*)&foo69229;
+__attribute__((used)) void* use69230 = (void*)&foo69230;
+__attribute__((used)) void* use69231 = (void*)&foo69231;
+__attribute__((used)) void* use69232 = (void*)&foo69232;
+__attribute__((used)) void* use69233 = (void*)&foo69233;
+__attribute__((used)) void* use69234 = (void*)&foo69234;
+__attribute__((used)) void* use69235 = (void*)&foo69235;
+__attribute__((used)) void* use69236 = (void*)&foo69236;
+__attribute__((used)) void* use69237 = (void*)&foo69237;
+__attribute__((used)) void* use69238 = (void*)&foo69238;
+__attribute__((used)) void* use69239 = (void*)&foo69239;
+__attribute__((used)) void* use69240 = (void*)&foo69240;
+__attribute__((used)) void* use69241 = (void*)&foo69241;
+__attribute__((used)) void* use69242 = (void*)&foo69242;
+__attribute__((used)) void* use69243 = (void*)&foo69243;
+__attribute__((used)) void* use69244 = (void*)&foo69244;
+__attribute__((used)) void* use69245 = (void*)&foo69245;
+__attribute__((used)) void* use69246 = (void*)&foo69246;
+__attribute__((used)) void* use69247 = (void*)&foo69247;
+__attribute__((used)) void* use69248 = (void*)&foo69248;
+__attribute__((used)) void* use69249 = (void*)&foo69249;
+__attribute__((used)) void* use69250 = (void*)&foo69250;
+__attribute__((used)) void* use69251 = (void*)&foo69251;
+__attribute__((used)) void* use69252 = (void*)&foo69252;
+__attribute__((used)) void* use69253 = (void*)&foo69253;
+__attribute__((used)) void* use69254 = (void*)&foo69254;
+__attribute__((used)) void* use69255 = (void*)&foo69255;
+__attribute__((used)) void* use69256 = (void*)&foo69256;
+__attribute__((used)) void* use69257 = (void*)&foo69257;
+__attribute__((used)) void* use69258 = (void*)&foo69258;
+__attribute__((used)) void* use69259 = (void*)&foo69259;
+__attribute__((used)) void* use69260 = (void*)&foo69260;
+__attribute__((used)) void* use69261 = (void*)&foo69261;
+__attribute__((used)) void* use69262 = (void*)&foo69262;
+__attribute__((used)) void* use69263 = (void*)&foo69263;
+__attribute__((used)) void* use69264 = (void*)&foo69264;
+__attribute__((used)) void* use69265 = (void*)&foo69265;
+__attribute__((used)) void* use69266 = (void*)&foo69266;
+__attribute__((used)) void* use69267 = (void*)&foo69267;
+__attribute__((used)) void* use69268 = (void*)&foo69268;
+__attribute__((used)) void* use69269 = (void*)&foo69269;
+__attribute__((used)) void* use69270 = (void*)&foo69270;
+__attribute__((used)) void* use69271 = (void*)&foo69271;
+__attribute__((used)) void* use69272 = (void*)&foo69272;
+__attribute__((used)) void* use69273 = (void*)&foo69273;
+__attribute__((used)) void* use69274 = (void*)&foo69274;
+__attribute__((used)) void* use69275 = (void*)&foo69275;
+__attribute__((used)) void* use69276 = (void*)&foo69276;
+__attribute__((used)) void* use69277 = (void*)&foo69277;
+__attribute__((used)) void* use69278 = (void*)&foo69278;
+__attribute__((used)) void* use69279 = (void*)&foo69279;
+__attribute__((used)) void* use69280 = (void*)&foo69280;
+__attribute__((used)) void* use69281 = (void*)&foo69281;
+__attribute__((used)) void* use69282 = (void*)&foo69282;
+__attribute__((used)) void* use69283 = (void*)&foo69283;
+__attribute__((used)) void* use69284 = (void*)&foo69284;
+__attribute__((used)) void* use69285 = (void*)&foo69285;
+__attribute__((used)) void* use69286 = (void*)&foo69286;
+__attribute__((used)) void* use69287 = (void*)&foo69287;
+__attribute__((used)) void* use69288 = (void*)&foo69288;
+__attribute__((used)) void* use69289 = (void*)&foo69289;
+__attribute__((used)) void* use69290 = (void*)&foo69290;
+__attribute__((used)) void* use69291 = (void*)&foo69291;
+__attribute__((used)) void* use69292 = (void*)&foo69292;
+__attribute__((used)) void* use69293 = (void*)&foo69293;
+__attribute__((used)) void* use69294 = (void*)&foo69294;
+__attribute__((used)) void* use69295 = (void*)&foo69295;
+__attribute__((used)) void* use69296 = (void*)&foo69296;
+__attribute__((used)) void* use69297 = (void*)&foo69297;
+__attribute__((used)) void* use69298 = (void*)&foo69298;
+__attribute__((used)) void* use69299 = (void*)&foo69299;
+__attribute__((used)) void* use69300 = (void*)&foo69300;
+__attribute__((used)) void* use69301 = (void*)&foo69301;
+__attribute__((used)) void* use69302 = (void*)&foo69302;
+__attribute__((used)) void* use69303 = (void*)&foo69303;
+__attribute__((used)) void* use69304 = (void*)&foo69304;
+__attribute__((used)) void* use69305 = (void*)&foo69305;
+__attribute__((used)) void* use69306 = (void*)&foo69306;
+__attribute__((used)) void* use69307 = (void*)&foo69307;
+__attribute__((used)) void* use69308 = (void*)&foo69308;
+__attribute__((used)) void* use69309 = (void*)&foo69309;
+__attribute__((used)) void* use69310 = (void*)&foo69310;
+__attribute__((used)) void* use69311 = (void*)&foo69311;
+__attribute__((used)) void* use69312 = (void*)&foo69312;
+__attribute__((used)) void* use69313 = (void*)&foo69313;
+__attribute__((used)) void* use69314 = (void*)&foo69314;
+__attribute__((used)) void* use69315 = (void*)&foo69315;
+__attribute__((used)) void* use69316 = (void*)&foo69316;
+__attribute__((used)) void* use69317 = (void*)&foo69317;
+__attribute__((used)) void* use69318 = (void*)&foo69318;
+__attribute__((used)) void* use69319 = (void*)&foo69319;
+__attribute__((used)) void* use69320 = (void*)&foo69320;
+__attribute__((used)) void* use69321 = (void*)&foo69321;
+__attribute__((used)) void* use69322 = (void*)&foo69322;
+__attribute__((used)) void* use69323 = (void*)&foo69323;
+__attribute__((used)) void* use69324 = (void*)&foo69324;
+__attribute__((used)) void* use69325 = (void*)&foo69325;
+__attribute__((used)) void* use69326 = (void*)&foo69326;
+__attribute__((used)) void* use69327 = (void*)&foo69327;
+__attribute__((used)) void* use69328 = (void*)&foo69328;
+__attribute__((used)) void* use69329 = (void*)&foo69329;
+__attribute__((used)) void* use69330 = (void*)&foo69330;
+__attribute__((used)) void* use69331 = (void*)&foo69331;
+__attribute__((used)) void* use69332 = (void*)&foo69332;
+__attribute__((used)) void* use69333 = (void*)&foo69333;
+__attribute__((used)) void* use69334 = (void*)&foo69334;
+__attribute__((used)) void* use69335 = (void*)&foo69335;
+__attribute__((used)) void* use69336 = (void*)&foo69336;
+__attribute__((used)) void* use69337 = (void*)&foo69337;
+__attribute__((used)) void* use69338 = (void*)&foo69338;
+__attribute__((used)) void* use69339 = (void*)&foo69339;
+__attribute__((used)) void* use69340 = (void*)&foo69340;
+__attribute__((used)) void* use69341 = (void*)&foo69341;
+__attribute__((used)) void* use69342 = (void*)&foo69342;
+__attribute__((used)) void* use69343 = (void*)&foo69343;
+__attribute__((used)) void* use69344 = (void*)&foo69344;
+__attribute__((used)) void* use69345 = (void*)&foo69345;
+__attribute__((used)) void* use69346 = (void*)&foo69346;
+__attribute__((used)) void* use69347 = (void*)&foo69347;
+__attribute__((used)) void* use69348 = (void*)&foo69348;
+__attribute__((used)) void* use69349 = (void*)&foo69349;
+__attribute__((used)) void* use69350 = (void*)&foo69350;
+__attribute__((used)) void* use69351 = (void*)&foo69351;
+__attribute__((used)) void* use69352 = (void*)&foo69352;
+__attribute__((used)) void* use69353 = (void*)&foo69353;
+__attribute__((used)) void* use69354 = (void*)&foo69354;
+__attribute__((used)) void* use69355 = (void*)&foo69355;
+__attribute__((used)) void* use69356 = (void*)&foo69356;
+__attribute__((used)) void* use69357 = (void*)&foo69357;
+__attribute__((used)) void* use69358 = (void*)&foo69358;
+__attribute__((used)) void* use69359 = (void*)&foo69359;
+__attribute__((used)) void* use69360 = (void*)&foo69360;
+__attribute__((used)) void* use69361 = (void*)&foo69361;
+__attribute__((used)) void* use69362 = (void*)&foo69362;
+__attribute__((used)) void* use69363 = (void*)&foo69363;
+__attribute__((used)) void* use69364 = (void*)&foo69364;
+__attribute__((used)) void* use69365 = (void*)&foo69365;
+__attribute__((used)) void* use69366 = (void*)&foo69366;
+__attribute__((used)) void* use69367 = (void*)&foo69367;
+__attribute__((used)) void* use69368 = (void*)&foo69368;
+__attribute__((used)) void* use69369 = (void*)&foo69369;
+__attribute__((used)) void* use69370 = (void*)&foo69370;
+__attribute__((used)) void* use69371 = (void*)&foo69371;
+__attribute__((used)) void* use69372 = (void*)&foo69372;
+__attribute__((used)) void* use69373 = (void*)&foo69373;
+__attribute__((used)) void* use69374 = (void*)&foo69374;
+__attribute__((used)) void* use69375 = (void*)&foo69375;
+__attribute__((used)) void* use69376 = (void*)&foo69376;
+__attribute__((used)) void* use69377 = (void*)&foo69377;
+__attribute__((used)) void* use69378 = (void*)&foo69378;
+__attribute__((used)) void* use69379 = (void*)&foo69379;
+__attribute__((used)) void* use69380 = (void*)&foo69380;
+__attribute__((used)) void* use69381 = (void*)&foo69381;
+__attribute__((used)) void* use69382 = (void*)&foo69382;
+__attribute__((used)) void* use69383 = (void*)&foo69383;
+__attribute__((used)) void* use69384 = (void*)&foo69384;
+__attribute__((used)) void* use69385 = (void*)&foo69385;
+__attribute__((used)) void* use69386 = (void*)&foo69386;
+__attribute__((used)) void* use69387 = (void*)&foo69387;
+__attribute__((used)) void* use69388 = (void*)&foo69388;
+__attribute__((used)) void* use69389 = (void*)&foo69389;
+__attribute__((used)) void* use69390 = (void*)&foo69390;
+__attribute__((used)) void* use69391 = (void*)&foo69391;
+__attribute__((used)) void* use69392 = (void*)&foo69392;
+__attribute__((used)) void* use69393 = (void*)&foo69393;
+__attribute__((used)) void* use69394 = (void*)&foo69394;
+__attribute__((used)) void* use69395 = (void*)&foo69395;
+__attribute__((used)) void* use69396 = (void*)&foo69396;
+__attribute__((used)) void* use69397 = (void*)&foo69397;
+__attribute__((used)) void* use69398 = (void*)&foo69398;
+__attribute__((used)) void* use69399 = (void*)&foo69399;
+__attribute__((used)) void* use69400 = (void*)&foo69400;
+__attribute__((used)) void* use69401 = (void*)&foo69401;
+__attribute__((used)) void* use69402 = (void*)&foo69402;
+__attribute__((used)) void* use69403 = (void*)&foo69403;
+__attribute__((used)) void* use69404 = (void*)&foo69404;
+__attribute__((used)) void* use69405 = (void*)&foo69405;
+__attribute__((used)) void* use69406 = (void*)&foo69406;
+__attribute__((used)) void* use69407 = (void*)&foo69407;
+__attribute__((used)) void* use69408 = (void*)&foo69408;
+__attribute__((used)) void* use69409 = (void*)&foo69409;
+__attribute__((used)) void* use69410 = (void*)&foo69410;
+__attribute__((used)) void* use69411 = (void*)&foo69411;
+__attribute__((used)) void* use69412 = (void*)&foo69412;
+__attribute__((used)) void* use69413 = (void*)&foo69413;
+__attribute__((used)) void* use69414 = (void*)&foo69414;
+__attribute__((used)) void* use69415 = (void*)&foo69415;
+__attribute__((used)) void* use69416 = (void*)&foo69416;
+__attribute__((used)) void* use69417 = (void*)&foo69417;
+__attribute__((used)) void* use69418 = (void*)&foo69418;
+__attribute__((used)) void* use69419 = (void*)&foo69419;
+__attribute__((used)) void* use69420 = (void*)&foo69420;
+__attribute__((used)) void* use69421 = (void*)&foo69421;
+__attribute__((used)) void* use69422 = (void*)&foo69422;
+__attribute__((used)) void* use69423 = (void*)&foo69423;
+__attribute__((used)) void* use69424 = (void*)&foo69424;
+__attribute__((used)) void* use69425 = (void*)&foo69425;
+__attribute__((used)) void* use69426 = (void*)&foo69426;
+__attribute__((used)) void* use69427 = (void*)&foo69427;
+__attribute__((used)) void* use69428 = (void*)&foo69428;
+__attribute__((used)) void* use69429 = (void*)&foo69429;
+__attribute__((used)) void* use69430 = (void*)&foo69430;
+__attribute__((used)) void* use69431 = (void*)&foo69431;
+__attribute__((used)) void* use69432 = (void*)&foo69432;
+__attribute__((used)) void* use69433 = (void*)&foo69433;
+__attribute__((used)) void* use69434 = (void*)&foo69434;
+__attribute__((used)) void* use69435 = (void*)&foo69435;
+__attribute__((used)) void* use69436 = (void*)&foo69436;
+__attribute__((used)) void* use69437 = (void*)&foo69437;
+__attribute__((used)) void* use69438 = (void*)&foo69438;
+__attribute__((used)) void* use69439 = (void*)&foo69439;
+__attribute__((used)) void* use69440 = (void*)&foo69440;
+__attribute__((used)) void* use69441 = (void*)&foo69441;
+__attribute__((used)) void* use69442 = (void*)&foo69442;
+__attribute__((used)) void* use69443 = (void*)&foo69443;
+__attribute__((used)) void* use69444 = (void*)&foo69444;
+__attribute__((used)) void* use69445 = (void*)&foo69445;
+__attribute__((used)) void* use69446 = (void*)&foo69446;
+__attribute__((used)) void* use69447 = (void*)&foo69447;
+__attribute__((used)) void* use69448 = (void*)&foo69448;
+__attribute__((used)) void* use69449 = (void*)&foo69449;
+__attribute__((used)) void* use69450 = (void*)&foo69450;
+__attribute__((used)) void* use69451 = (void*)&foo69451;
+__attribute__((used)) void* use69452 = (void*)&foo69452;
+__attribute__((used)) void* use69453 = (void*)&foo69453;
+__attribute__((used)) void* use69454 = (void*)&foo69454;
+__attribute__((used)) void* use69455 = (void*)&foo69455;
+__attribute__((used)) void* use69456 = (void*)&foo69456;
+__attribute__((used)) void* use69457 = (void*)&foo69457;
+__attribute__((used)) void* use69458 = (void*)&foo69458;
+__attribute__((used)) void* use69459 = (void*)&foo69459;
+__attribute__((used)) void* use69460 = (void*)&foo69460;
+__attribute__((used)) void* use69461 = (void*)&foo69461;
+__attribute__((used)) void* use69462 = (void*)&foo69462;
+__attribute__((used)) void* use69463 = (void*)&foo69463;
+__attribute__((used)) void* use69464 = (void*)&foo69464;
+__attribute__((used)) void* use69465 = (void*)&foo69465;
+__attribute__((used)) void* use69466 = (void*)&foo69466;
+__attribute__((used)) void* use69467 = (void*)&foo69467;
+__attribute__((used)) void* use69468 = (void*)&foo69468;
+__attribute__((used)) void* use69469 = (void*)&foo69469;
+__attribute__((used)) void* use69470 = (void*)&foo69470;
+__attribute__((used)) void* use69471 = (void*)&foo69471;
+__attribute__((used)) void* use69472 = (void*)&foo69472;
+__attribute__((used)) void* use69473 = (void*)&foo69473;
+__attribute__((used)) void* use69474 = (void*)&foo69474;
+__attribute__((used)) void* use69475 = (void*)&foo69475;
+__attribute__((used)) void* use69476 = (void*)&foo69476;
+__attribute__((used)) void* use69477 = (void*)&foo69477;
+__attribute__((used)) void* use69478 = (void*)&foo69478;
+__attribute__((used)) void* use69479 = (void*)&foo69479;
+__attribute__((used)) void* use69480 = (void*)&foo69480;
+__attribute__((used)) void* use69481 = (void*)&foo69481;
+__attribute__((used)) void* use69482 = (void*)&foo69482;
+__attribute__((used)) void* use69483 = (void*)&foo69483;
+__attribute__((used)) void* use69484 = (void*)&foo69484;
+__attribute__((used)) void* use69485 = (void*)&foo69485;
+__attribute__((used)) void* use69486 = (void*)&foo69486;
+__attribute__((used)) void* use69487 = (void*)&foo69487;
+__attribute__((used)) void* use69488 = (void*)&foo69488;
+__attribute__((used)) void* use69489 = (void*)&foo69489;
+__attribute__((used)) void* use69490 = (void*)&foo69490;
+__attribute__((used)) void* use69491 = (void*)&foo69491;
+__attribute__((used)) void* use69492 = (void*)&foo69492;
+__attribute__((used)) void* use69493 = (void*)&foo69493;
+__attribute__((used)) void* use69494 = (void*)&foo69494;
+__attribute__((used)) void* use69495 = (void*)&foo69495;
+__attribute__((used)) void* use69496 = (void*)&foo69496;
+__attribute__((used)) void* use69497 = (void*)&foo69497;
+__attribute__((used)) void* use69498 = (void*)&foo69498;
+__attribute__((used)) void* use69499 = (void*)&foo69499;
+__attribute__((used)) void* use69500 = (void*)&foo69500;
+__attribute__((used)) void* use69501 = (void*)&foo69501;
+__attribute__((used)) void* use69502 = (void*)&foo69502;
+__attribute__((used)) void* use69503 = (void*)&foo69503;
+__attribute__((used)) void* use69504 = (void*)&foo69504;
+__attribute__((used)) void* use69505 = (void*)&foo69505;
+__attribute__((used)) void* use69506 = (void*)&foo69506;
+__attribute__((used)) void* use69507 = (void*)&foo69507;
+__attribute__((used)) void* use69508 = (void*)&foo69508;
+__attribute__((used)) void* use69509 = (void*)&foo69509;
+__attribute__((used)) void* use69510 = (void*)&foo69510;
+__attribute__((used)) void* use69511 = (void*)&foo69511;
+__attribute__((used)) void* use69512 = (void*)&foo69512;
+__attribute__((used)) void* use69513 = (void*)&foo69513;
+__attribute__((used)) void* use69514 = (void*)&foo69514;
+__attribute__((used)) void* use69515 = (void*)&foo69515;
+__attribute__((used)) void* use69516 = (void*)&foo69516;
+__attribute__((used)) void* use69517 = (void*)&foo69517;
+__attribute__((used)) void* use69518 = (void*)&foo69518;
+__attribute__((used)) void* use69519 = (void*)&foo69519;
+__attribute__((used)) void* use69520 = (void*)&foo69520;
+__attribute__((used)) void* use69521 = (void*)&foo69521;
+__attribute__((used)) void* use69522 = (void*)&foo69522;
+__attribute__((used)) void* use69523 = (void*)&foo69523;
+__attribute__((used)) void* use69524 = (void*)&foo69524;
+__attribute__((used)) void* use69525 = (void*)&foo69525;
+__attribute__((used)) void* use69526 = (void*)&foo69526;
+__attribute__((used)) void* use69527 = (void*)&foo69527;
+__attribute__((used)) void* use69528 = (void*)&foo69528;
+__attribute__((used)) void* use69529 = (void*)&foo69529;
+__attribute__((used)) void* use69530 = (void*)&foo69530;
+__attribute__((used)) void* use69531 = (void*)&foo69531;
+__attribute__((used)) void* use69532 = (void*)&foo69532;
+__attribute__((used)) void* use69533 = (void*)&foo69533;
+__attribute__((used)) void* use69534 = (void*)&foo69534;
+__attribute__((used)) void* use69535 = (void*)&foo69535;
+__attribute__((used)) void* use69536 = (void*)&foo69536;
+__attribute__((used)) void* use69537 = (void*)&foo69537;
+__attribute__((used)) void* use69538 = (void*)&foo69538;
+__attribute__((used)) void* use69539 = (void*)&foo69539;
+__attribute__((used)) void* use69540 = (void*)&foo69540;
+__attribute__((used)) void* use69541 = (void*)&foo69541;
+__attribute__((used)) void* use69542 = (void*)&foo69542;
+__attribute__((used)) void* use69543 = (void*)&foo69543;
+__attribute__((used)) void* use69544 = (void*)&foo69544;
+__attribute__((used)) void* use69545 = (void*)&foo69545;
+__attribute__((used)) void* use69546 = (void*)&foo69546;
+__attribute__((used)) void* use69547 = (void*)&foo69547;
+__attribute__((used)) void* use69548 = (void*)&foo69548;
+__attribute__((used)) void* use69549 = (void*)&foo69549;
+__attribute__((used)) void* use69550 = (void*)&foo69550;
+__attribute__((used)) void* use69551 = (void*)&foo69551;
+__attribute__((used)) void* use69552 = (void*)&foo69552;
+__attribute__((used)) void* use69553 = (void*)&foo69553;
+__attribute__((used)) void* use69554 = (void*)&foo69554;
+__attribute__((used)) void* use69555 = (void*)&foo69555;
+__attribute__((used)) void* use69556 = (void*)&foo69556;
+__attribute__((used)) void* use69557 = (void*)&foo69557;
+__attribute__((used)) void* use69558 = (void*)&foo69558;
+__attribute__((used)) void* use69559 = (void*)&foo69559;
+__attribute__((used)) void* use69560 = (void*)&foo69560;
+__attribute__((used)) void* use69561 = (void*)&foo69561;
+__attribute__((used)) void* use69562 = (void*)&foo69562;
+__attribute__((used)) void* use69563 = (void*)&foo69563;
+__attribute__((used)) void* use69564 = (void*)&foo69564;
+__attribute__((used)) void* use69565 = (void*)&foo69565;
+__attribute__((used)) void* use69566 = (void*)&foo69566;
+__attribute__((used)) void* use69567 = (void*)&foo69567;
+__attribute__((used)) void* use69568 = (void*)&foo69568;
+__attribute__((used)) void* use69569 = (void*)&foo69569;
+__attribute__((used)) void* use69570 = (void*)&foo69570;
+__attribute__((used)) void* use69571 = (void*)&foo69571;
+__attribute__((used)) void* use69572 = (void*)&foo69572;
+__attribute__((used)) void* use69573 = (void*)&foo69573;
+__attribute__((used)) void* use69574 = (void*)&foo69574;
+__attribute__((used)) void* use69575 = (void*)&foo69575;
+__attribute__((used)) void* use69576 = (void*)&foo69576;
+__attribute__((used)) void* use69577 = (void*)&foo69577;
+__attribute__((used)) void* use69578 = (void*)&foo69578;
+__attribute__((used)) void* use69579 = (void*)&foo69579;
+__attribute__((used)) void* use69580 = (void*)&foo69580;
+__attribute__((used)) void* use69581 = (void*)&foo69581;
+__attribute__((used)) void* use69582 = (void*)&foo69582;
+__attribute__((used)) void* use69583 = (void*)&foo69583;
+__attribute__((used)) void* use69584 = (void*)&foo69584;
+__attribute__((used)) void* use69585 = (void*)&foo69585;
+__attribute__((used)) void* use69586 = (void*)&foo69586;
+__attribute__((used)) void* use69587 = (void*)&foo69587;
+__attribute__((used)) void* use69588 = (void*)&foo69588;
+__attribute__((used)) void* use69589 = (void*)&foo69589;
+__attribute__((used)) void* use69590 = (void*)&foo69590;
+__attribute__((used)) void* use69591 = (void*)&foo69591;
+__attribute__((used)) void* use69592 = (void*)&foo69592;
+__attribute__((used)) void* use69593 = (void*)&foo69593;
+__attribute__((used)) void* use69594 = (void*)&foo69594;
+__attribute__((used)) void* use69595 = (void*)&foo69595;
+__attribute__((used)) void* use69596 = (void*)&foo69596;
+__attribute__((used)) void* use69597 = (void*)&foo69597;
+__attribute__((used)) void* use69598 = (void*)&foo69598;
+__attribute__((used)) void* use69599 = (void*)&foo69599;
+__attribute__((used)) void* use69600 = (void*)&foo69600;
+__attribute__((used)) void* use69601 = (void*)&foo69601;
+__attribute__((used)) void* use69602 = (void*)&foo69602;
+__attribute__((used)) void* use69603 = (void*)&foo69603;
+__attribute__((used)) void* use69604 = (void*)&foo69604;
+__attribute__((used)) void* use69605 = (void*)&foo69605;
+__attribute__((used)) void* use69606 = (void*)&foo69606;
+__attribute__((used)) void* use69607 = (void*)&foo69607;
+__attribute__((used)) void* use69608 = (void*)&foo69608;
+__attribute__((used)) void* use69609 = (void*)&foo69609;
+__attribute__((used)) void* use69610 = (void*)&foo69610;
+__attribute__((used)) void* use69611 = (void*)&foo69611;
+__attribute__((used)) void* use69612 = (void*)&foo69612;
+__attribute__((used)) void* use69613 = (void*)&foo69613;
+__attribute__((used)) void* use69614 = (void*)&foo69614;
+__attribute__((used)) void* use69615 = (void*)&foo69615;
+__attribute__((used)) void* use69616 = (void*)&foo69616;
+__attribute__((used)) void* use69617 = (void*)&foo69617;
+__attribute__((used)) void* use69618 = (void*)&foo69618;
+__attribute__((used)) void* use69619 = (void*)&foo69619;
+__attribute__((used)) void* use69620 = (void*)&foo69620;
+__attribute__((used)) void* use69621 = (void*)&foo69621;
+__attribute__((used)) void* use69622 = (void*)&foo69622;
+__attribute__((used)) void* use69623 = (void*)&foo69623;
+__attribute__((used)) void* use69624 = (void*)&foo69624;
+__attribute__((used)) void* use69625 = (void*)&foo69625;
+__attribute__((used)) void* use69626 = (void*)&foo69626;
+__attribute__((used)) void* use69627 = (void*)&foo69627;
+__attribute__((used)) void* use69628 = (void*)&foo69628;
+__attribute__((used)) void* use69629 = (void*)&foo69629;
+__attribute__((used)) void* use69630 = (void*)&foo69630;
+__attribute__((used)) void* use69631 = (void*)&foo69631;
+__attribute__((used)) void* use69632 = (void*)&foo69632;
+__attribute__((used)) void* use69633 = (void*)&foo69633;
+__attribute__((used)) void* use69634 = (void*)&foo69634;
+__attribute__((used)) void* use69635 = (void*)&foo69635;
+__attribute__((used)) void* use69636 = (void*)&foo69636;
+__attribute__((used)) void* use69637 = (void*)&foo69637;
+__attribute__((used)) void* use69638 = (void*)&foo69638;
+__attribute__((used)) void* use69639 = (void*)&foo69639;
+__attribute__((used)) void* use69640 = (void*)&foo69640;
+__attribute__((used)) void* use69641 = (void*)&foo69641;
+__attribute__((used)) void* use69642 = (void*)&foo69642;
+__attribute__((used)) void* use69643 = (void*)&foo69643;
+__attribute__((used)) void* use69644 = (void*)&foo69644;
+__attribute__((used)) void* use69645 = (void*)&foo69645;
+__attribute__((used)) void* use69646 = (void*)&foo69646;
+__attribute__((used)) void* use69647 = (void*)&foo69647;
+__attribute__((used)) void* use69648 = (void*)&foo69648;
+__attribute__((used)) void* use69649 = (void*)&foo69649;
+__attribute__((used)) void* use69650 = (void*)&foo69650;
+__attribute__((used)) void* use69651 = (void*)&foo69651;
+__attribute__((used)) void* use69652 = (void*)&foo69652;
+__attribute__((used)) void* use69653 = (void*)&foo69653;
+__attribute__((used)) void* use69654 = (void*)&foo69654;
+__attribute__((used)) void* use69655 = (void*)&foo69655;
+__attribute__((used)) void* use69656 = (void*)&foo69656;
+__attribute__((used)) void* use69657 = (void*)&foo69657;
+__attribute__((used)) void* use69658 = (void*)&foo69658;
+__attribute__((used)) void* use69659 = (void*)&foo69659;
+__attribute__((used)) void* use69660 = (void*)&foo69660;
+__attribute__((used)) void* use69661 = (void*)&foo69661;
+__attribute__((used)) void* use69662 = (void*)&foo69662;
+__attribute__((used)) void* use69663 = (void*)&foo69663;
+__attribute__((used)) void* use69664 = (void*)&foo69664;
+__attribute__((used)) void* use69665 = (void*)&foo69665;
+__attribute__((used)) void* use69666 = (void*)&foo69666;
+__attribute__((used)) void* use69667 = (void*)&foo69667;
+__attribute__((used)) void* use69668 = (void*)&foo69668;
+__attribute__((used)) void* use69669 = (void*)&foo69669;
+__attribute__((used)) void* use69670 = (void*)&foo69670;
+__attribute__((used)) void* use69671 = (void*)&foo69671;
+__attribute__((used)) void* use69672 = (void*)&foo69672;
+__attribute__((used)) void* use69673 = (void*)&foo69673;
+__attribute__((used)) void* use69674 = (void*)&foo69674;
+__attribute__((used)) void* use69675 = (void*)&foo69675;
+__attribute__((used)) void* use69676 = (void*)&foo69676;
+__attribute__((used)) void* use69677 = (void*)&foo69677;
+__attribute__((used)) void* use69678 = (void*)&foo69678;
+__attribute__((used)) void* use69679 = (void*)&foo69679;
+__attribute__((used)) void* use69680 = (void*)&foo69680;
+__attribute__((used)) void* use69681 = (void*)&foo69681;
+__attribute__((used)) void* use69682 = (void*)&foo69682;
+__attribute__((used)) void* use69683 = (void*)&foo69683;
+__attribute__((used)) void* use69684 = (void*)&foo69684;
+__attribute__((used)) void* use69685 = (void*)&foo69685;
+__attribute__((used)) void* use69686 = (void*)&foo69686;
+__attribute__((used)) void* use69687 = (void*)&foo69687;
+__attribute__((used)) void* use69688 = (void*)&foo69688;
+__attribute__((used)) void* use69689 = (void*)&foo69689;
+__attribute__((used)) void* use69690 = (void*)&foo69690;
+__attribute__((used)) void* use69691 = (void*)&foo69691;
+__attribute__((used)) void* use69692 = (void*)&foo69692;
+__attribute__((used)) void* use69693 = (void*)&foo69693;
+__attribute__((used)) void* use69694 = (void*)&foo69694;
+__attribute__((used)) void* use69695 = (void*)&foo69695;
+__attribute__((used)) void* use69696 = (void*)&foo69696;
+__attribute__((used)) void* use69697 = (void*)&foo69697;
+__attribute__((used)) void* use69698 = (void*)&foo69698;
+__attribute__((used)) void* use69699 = (void*)&foo69699;
+__attribute__((used)) void* use69700 = (void*)&foo69700;
+__attribute__((used)) void* use69701 = (void*)&foo69701;
+__attribute__((used)) void* use69702 = (void*)&foo69702;
+__attribute__((used)) void* use69703 = (void*)&foo69703;
+__attribute__((used)) void* use69704 = (void*)&foo69704;
+__attribute__((used)) void* use69705 = (void*)&foo69705;
+__attribute__((used)) void* use69706 = (void*)&foo69706;
+__attribute__((used)) void* use69707 = (void*)&foo69707;
+__attribute__((used)) void* use69708 = (void*)&foo69708;
+__attribute__((used)) void* use69709 = (void*)&foo69709;
+__attribute__((used)) void* use69710 = (void*)&foo69710;
+__attribute__((used)) void* use69711 = (void*)&foo69711;
+__attribute__((used)) void* use69712 = (void*)&foo69712;
+__attribute__((used)) void* use69713 = (void*)&foo69713;
+__attribute__((used)) void* use69714 = (void*)&foo69714;
+__attribute__((used)) void* use69715 = (void*)&foo69715;
+__attribute__((used)) void* use69716 = (void*)&foo69716;
+__attribute__((used)) void* use69717 = (void*)&foo69717;
+__attribute__((used)) void* use69718 = (void*)&foo69718;
+__attribute__((used)) void* use69719 = (void*)&foo69719;
+__attribute__((used)) void* use69720 = (void*)&foo69720;
+__attribute__((used)) void* use69721 = (void*)&foo69721;
+__attribute__((used)) void* use69722 = (void*)&foo69722;
+__attribute__((used)) void* use69723 = (void*)&foo69723;
+__attribute__((used)) void* use69724 = (void*)&foo69724;
+__attribute__((used)) void* use69725 = (void*)&foo69725;
+__attribute__((used)) void* use69726 = (void*)&foo69726;
+__attribute__((used)) void* use69727 = (void*)&foo69727;
+__attribute__((used)) void* use69728 = (void*)&foo69728;
+__attribute__((used)) void* use69729 = (void*)&foo69729;
+__attribute__((used)) void* use69730 = (void*)&foo69730;
+__attribute__((used)) void* use69731 = (void*)&foo69731;
+__attribute__((used)) void* use69732 = (void*)&foo69732;
+__attribute__((used)) void* use69733 = (void*)&foo69733;
+__attribute__((used)) void* use69734 = (void*)&foo69734;
+__attribute__((used)) void* use69735 = (void*)&foo69735;
+__attribute__((used)) void* use69736 = (void*)&foo69736;
+__attribute__((used)) void* use69737 = (void*)&foo69737;
+__attribute__((used)) void* use69738 = (void*)&foo69738;
+__attribute__((used)) void* use69739 = (void*)&foo69739;
+__attribute__((used)) void* use69740 = (void*)&foo69740;
+__attribute__((used)) void* use69741 = (void*)&foo69741;
+__attribute__((used)) void* use69742 = (void*)&foo69742;
+__attribute__((used)) void* use69743 = (void*)&foo69743;
+__attribute__((used)) void* use69744 = (void*)&foo69744;
+__attribute__((used)) void* use69745 = (void*)&foo69745;
+__attribute__((used)) void* use69746 = (void*)&foo69746;
+__attribute__((used)) void* use69747 = (void*)&foo69747;
+__attribute__((used)) void* use69748 = (void*)&foo69748;
+__attribute__((used)) void* use69749 = (void*)&foo69749;
+__attribute__((used)) void* use69750 = (void*)&foo69750;
+__attribute__((used)) void* use69751 = (void*)&foo69751;
+__attribute__((used)) void* use69752 = (void*)&foo69752;
+__attribute__((used)) void* use69753 = (void*)&foo69753;
+__attribute__((used)) void* use69754 = (void*)&foo69754;
+__attribute__((used)) void* use69755 = (void*)&foo69755;
+__attribute__((used)) void* use69756 = (void*)&foo69756;
+__attribute__((used)) void* use69757 = (void*)&foo69757;
+__attribute__((used)) void* use69758 = (void*)&foo69758;
+__attribute__((used)) void* use69759 = (void*)&foo69759;
+__attribute__((used)) void* use69760 = (void*)&foo69760;
+__attribute__((used)) void* use69761 = (void*)&foo69761;
+__attribute__((used)) void* use69762 = (void*)&foo69762;
+__attribute__((used)) void* use69763 = (void*)&foo69763;
+__attribute__((used)) void* use69764 = (void*)&foo69764;
+__attribute__((used)) void* use69765 = (void*)&foo69765;
+__attribute__((used)) void* use69766 = (void*)&foo69766;
+__attribute__((used)) void* use69767 = (void*)&foo69767;
+__attribute__((used)) void* use69768 = (void*)&foo69768;
+__attribute__((used)) void* use69769 = (void*)&foo69769;
+__attribute__((used)) void* use69770 = (void*)&foo69770;
+__attribute__((used)) void* use69771 = (void*)&foo69771;
+__attribute__((used)) void* use69772 = (void*)&foo69772;
+__attribute__((used)) void* use69773 = (void*)&foo69773;
+__attribute__((used)) void* use69774 = (void*)&foo69774;
+__attribute__((used)) void* use69775 = (void*)&foo69775;
+__attribute__((used)) void* use69776 = (void*)&foo69776;
+__attribute__((used)) void* use69777 = (void*)&foo69777;
+__attribute__((used)) void* use69778 = (void*)&foo69778;
+__attribute__((used)) void* use69779 = (void*)&foo69779;
+__attribute__((used)) void* use69780 = (void*)&foo69780;
+__attribute__((used)) void* use69781 = (void*)&foo69781;
+__attribute__((used)) void* use69782 = (void*)&foo69782;
+__attribute__((used)) void* use69783 = (void*)&foo69783;
+__attribute__((used)) void* use69784 = (void*)&foo69784;
+__attribute__((used)) void* use69785 = (void*)&foo69785;
+__attribute__((used)) void* use69786 = (void*)&foo69786;
+__attribute__((used)) void* use69787 = (void*)&foo69787;
+__attribute__((used)) void* use69788 = (void*)&foo69788;
+__attribute__((used)) void* use69789 = (void*)&foo69789;
+__attribute__((used)) void* use69790 = (void*)&foo69790;
+__attribute__((used)) void* use69791 = (void*)&foo69791;
+__attribute__((used)) void* use69792 = (void*)&foo69792;
+__attribute__((used)) void* use69793 = (void*)&foo69793;
+__attribute__((used)) void* use69794 = (void*)&foo69794;
+__attribute__((used)) void* use69795 = (void*)&foo69795;
+__attribute__((used)) void* use69796 = (void*)&foo69796;
+__attribute__((used)) void* use69797 = (void*)&foo69797;
+__attribute__((used)) void* use69798 = (void*)&foo69798;
+__attribute__((used)) void* use69799 = (void*)&foo69799;
+__attribute__((used)) void* use69800 = (void*)&foo69800;
+__attribute__((used)) void* use69801 = (void*)&foo69801;
+__attribute__((used)) void* use69802 = (void*)&foo69802;
+__attribute__((used)) void* use69803 = (void*)&foo69803;
+__attribute__((used)) void* use69804 = (void*)&foo69804;
+__attribute__((used)) void* use69805 = (void*)&foo69805;
+__attribute__((used)) void* use69806 = (void*)&foo69806;
+__attribute__((used)) void* use69807 = (void*)&foo69807;
+__attribute__((used)) void* use69808 = (void*)&foo69808;
+__attribute__((used)) void* use69809 = (void*)&foo69809;
+__attribute__((used)) void* use69810 = (void*)&foo69810;
+__attribute__((used)) void* use69811 = (void*)&foo69811;
+__attribute__((used)) void* use69812 = (void*)&foo69812;
+__attribute__((used)) void* use69813 = (void*)&foo69813;
+__attribute__((used)) void* use69814 = (void*)&foo69814;
+__attribute__((used)) void* use69815 = (void*)&foo69815;
+__attribute__((used)) void* use69816 = (void*)&foo69816;
+__attribute__((used)) void* use69817 = (void*)&foo69817;
+__attribute__((used)) void* use69818 = (void*)&foo69818;
+__attribute__((used)) void* use69819 = (void*)&foo69819;
+__attribute__((used)) void* use69820 = (void*)&foo69820;
+__attribute__((used)) void* use69821 = (void*)&foo69821;
+__attribute__((used)) void* use69822 = (void*)&foo69822;
+__attribute__((used)) void* use69823 = (void*)&foo69823;
+__attribute__((used)) void* use69824 = (void*)&foo69824;
+__attribute__((used)) void* use69825 = (void*)&foo69825;
+__attribute__((used)) void* use69826 = (void*)&foo69826;
+__attribute__((used)) void* use69827 = (void*)&foo69827;
+__attribute__((used)) void* use69828 = (void*)&foo69828;
+__attribute__((used)) void* use69829 = (void*)&foo69829;
+__attribute__((used)) void* use69830 = (void*)&foo69830;
+__attribute__((used)) void* use69831 = (void*)&foo69831;
+__attribute__((used)) void* use69832 = (void*)&foo69832;
+__attribute__((used)) void* use69833 = (void*)&foo69833;
+__attribute__((used)) void* use69834 = (void*)&foo69834;
+__attribute__((used)) void* use69835 = (void*)&foo69835;
+__attribute__((used)) void* use69836 = (void*)&foo69836;
+__attribute__((used)) void* use69837 = (void*)&foo69837;
+__attribute__((used)) void* use69838 = (void*)&foo69838;
+__attribute__((used)) void* use69839 = (void*)&foo69839;
+__attribute__((used)) void* use69840 = (void*)&foo69840;
+__attribute__((used)) void* use69841 = (void*)&foo69841;
+__attribute__((used)) void* use69842 = (void*)&foo69842;
+__attribute__((used)) void* use69843 = (void*)&foo69843;
+__attribute__((used)) void* use69844 = (void*)&foo69844;
+__attribute__((used)) void* use69845 = (void*)&foo69845;
+__attribute__((used)) void* use69846 = (void*)&foo69846;
+__attribute__((used)) void* use69847 = (void*)&foo69847;
+__attribute__((used)) void* use69848 = (void*)&foo69848;
+__attribute__((used)) void* use69849 = (void*)&foo69849;
+__attribute__((used)) void* use69850 = (void*)&foo69850;
+__attribute__((used)) void* use69851 = (void*)&foo69851;
+__attribute__((used)) void* use69852 = (void*)&foo69852;
+__attribute__((used)) void* use69853 = (void*)&foo69853;
+__attribute__((used)) void* use69854 = (void*)&foo69854;
+__attribute__((used)) void* use69855 = (void*)&foo69855;
+__attribute__((used)) void* use69856 = (void*)&foo69856;
+__attribute__((used)) void* use69857 = (void*)&foo69857;
+__attribute__((used)) void* use69858 = (void*)&foo69858;
+__attribute__((used)) void* use69859 = (void*)&foo69859;
+__attribute__((used)) void* use69860 = (void*)&foo69860;
+__attribute__((used)) void* use69861 = (void*)&foo69861;
+__attribute__((used)) void* use69862 = (void*)&foo69862;
+__attribute__((used)) void* use69863 = (void*)&foo69863;
+__attribute__((used)) void* use69864 = (void*)&foo69864;
+__attribute__((used)) void* use69865 = (void*)&foo69865;
+__attribute__((used)) void* use69866 = (void*)&foo69866;
+__attribute__((used)) void* use69867 = (void*)&foo69867;
+__attribute__((used)) void* use69868 = (void*)&foo69868;
+__attribute__((used)) void* use69869 = (void*)&foo69869;
+__attribute__((used)) void* use69870 = (void*)&foo69870;
+__attribute__((used)) void* use69871 = (void*)&foo69871;
+__attribute__((used)) void* use69872 = (void*)&foo69872;
+__attribute__((used)) void* use69873 = (void*)&foo69873;
+__attribute__((used)) void* use69874 = (void*)&foo69874;
+__attribute__((used)) void* use69875 = (void*)&foo69875;
+__attribute__((used)) void* use69876 = (void*)&foo69876;
+__attribute__((used)) void* use69877 = (void*)&foo69877;
+__attribute__((used)) void* use69878 = (void*)&foo69878;
+__attribute__((used)) void* use69879 = (void*)&foo69879;
+__attribute__((used)) void* use69880 = (void*)&foo69880;
+__attribute__((used)) void* use69881 = (void*)&foo69881;
+__attribute__((used)) void* use69882 = (void*)&foo69882;
+__attribute__((used)) void* use69883 = (void*)&foo69883;
+__attribute__((used)) void* use69884 = (void*)&foo69884;
+__attribute__((used)) void* use69885 = (void*)&foo69885;
+__attribute__((used)) void* use69886 = (void*)&foo69886;
+__attribute__((used)) void* use69887 = (void*)&foo69887;
+__attribute__((used)) void* use69888 = (void*)&foo69888;
+__attribute__((used)) void* use69889 = (void*)&foo69889;
+__attribute__((used)) void* use69890 = (void*)&foo69890;
+__attribute__((used)) void* use69891 = (void*)&foo69891;
+__attribute__((used)) void* use69892 = (void*)&foo69892;
+__attribute__((used)) void* use69893 = (void*)&foo69893;
+__attribute__((used)) void* use69894 = (void*)&foo69894;
+__attribute__((used)) void* use69895 = (void*)&foo69895;
+__attribute__((used)) void* use69896 = (void*)&foo69896;
+__attribute__((used)) void* use69897 = (void*)&foo69897;
+__attribute__((used)) void* use69898 = (void*)&foo69898;
+__attribute__((used)) void* use69899 = (void*)&foo69899;
+__attribute__((used)) void* use69900 = (void*)&foo69900;
+__attribute__((used)) void* use69901 = (void*)&foo69901;
+__attribute__((used)) void* use69902 = (void*)&foo69902;
+__attribute__((used)) void* use69903 = (void*)&foo69903;
+__attribute__((used)) void* use69904 = (void*)&foo69904;
+__attribute__((used)) void* use69905 = (void*)&foo69905;
+__attribute__((used)) void* use69906 = (void*)&foo69906;
+__attribute__((used)) void* use69907 = (void*)&foo69907;
+__attribute__((used)) void* use69908 = (void*)&foo69908;
+__attribute__((used)) void* use69909 = (void*)&foo69909;
+__attribute__((used)) void* use69910 = (void*)&foo69910;
+__attribute__((used)) void* use69911 = (void*)&foo69911;
+__attribute__((used)) void* use69912 = (void*)&foo69912;
+__attribute__((used)) void* use69913 = (void*)&foo69913;
+__attribute__((used)) void* use69914 = (void*)&foo69914;
+__attribute__((used)) void* use69915 = (void*)&foo69915;
+__attribute__((used)) void* use69916 = (void*)&foo69916;
+__attribute__((used)) void* use69917 = (void*)&foo69917;
+__attribute__((used)) void* use69918 = (void*)&foo69918;
+__attribute__((used)) void* use69919 = (void*)&foo69919;
+__attribute__((used)) void* use69920 = (void*)&foo69920;
+__attribute__((used)) void* use69921 = (void*)&foo69921;
+__attribute__((used)) void* use69922 = (void*)&foo69922;
+__attribute__((used)) void* use69923 = (void*)&foo69923;
+__attribute__((used)) void* use69924 = (void*)&foo69924;
+__attribute__((used)) void* use69925 = (void*)&foo69925;
+__attribute__((used)) void* use69926 = (void*)&foo69926;
+__attribute__((used)) void* use69927 = (void*)&foo69927;
+__attribute__((used)) void* use69928 = (void*)&foo69928;
+__attribute__((used)) void* use69929 = (void*)&foo69929;
+__attribute__((used)) void* use69930 = (void*)&foo69930;
+__attribute__((used)) void* use69931 = (void*)&foo69931;
+__attribute__((used)) void* use69932 = (void*)&foo69932;
+__attribute__((used)) void* use69933 = (void*)&foo69933;
+__attribute__((used)) void* use69934 = (void*)&foo69934;
+__attribute__((used)) void* use69935 = (void*)&foo69935;
+__attribute__((used)) void* use69936 = (void*)&foo69936;
+__attribute__((used)) void* use69937 = (void*)&foo69937;
+__attribute__((used)) void* use69938 = (void*)&foo69938;
+__attribute__((used)) void* use69939 = (void*)&foo69939;
+__attribute__((used)) void* use69940 = (void*)&foo69940;
+__attribute__((used)) void* use69941 = (void*)&foo69941;
+__attribute__((used)) void* use69942 = (void*)&foo69942;
+__attribute__((used)) void* use69943 = (void*)&foo69943;
+__attribute__((used)) void* use69944 = (void*)&foo69944;
+__attribute__((used)) void* use69945 = (void*)&foo69945;
+__attribute__((used)) void* use69946 = (void*)&foo69946;
+__attribute__((used)) void* use69947 = (void*)&foo69947;
+__attribute__((used)) void* use69948 = (void*)&foo69948;
+__attribute__((used)) void* use69949 = (void*)&foo69949;
+__attribute__((used)) void* use69950 = (void*)&foo69950;
+__attribute__((used)) void* use69951 = (void*)&foo69951;
+__attribute__((used)) void* use69952 = (void*)&foo69952;
+__attribute__((used)) void* use69953 = (void*)&foo69953;
+__attribute__((used)) void* use69954 = (void*)&foo69954;
+__attribute__((used)) void* use69955 = (void*)&foo69955;
+__attribute__((used)) void* use69956 = (void*)&foo69956;
+__attribute__((used)) void* use69957 = (void*)&foo69957;
+__attribute__((used)) void* use69958 = (void*)&foo69958;
+__attribute__((used)) void* use69959 = (void*)&foo69959;
+__attribute__((used)) void* use69960 = (void*)&foo69960;
+__attribute__((used)) void* use69961 = (void*)&foo69961;
+__attribute__((used)) void* use69962 = (void*)&foo69962;
+__attribute__((used)) void* use69963 = (void*)&foo69963;
+__attribute__((used)) void* use69964 = (void*)&foo69964;
+__attribute__((used)) void* use69965 = (void*)&foo69965;
+__attribute__((used)) void* use69966 = (void*)&foo69966;
+__attribute__((used)) void* use69967 = (void*)&foo69967;
+__attribute__((used)) void* use69968 = (void*)&foo69968;
+__attribute__((used)) void* use69969 = (void*)&foo69969;
+__attribute__((used)) void* use69970 = (void*)&foo69970;
+__attribute__((used)) void* use69971 = (void*)&foo69971;
+__attribute__((used)) void* use69972 = (void*)&foo69972;
+__attribute__((used)) void* use69973 = (void*)&foo69973;
+__attribute__((used)) void* use69974 = (void*)&foo69974;
+__attribute__((used)) void* use69975 = (void*)&foo69975;
+__attribute__((used)) void* use69976 = (void*)&foo69976;
+__attribute__((used)) void* use69977 = (void*)&foo69977;
+__attribute__((used)) void* use69978 = (void*)&foo69978;
+__attribute__((used)) void* use69979 = (void*)&foo69979;
+__attribute__((used)) void* use69980 = (void*)&foo69980;
+__attribute__((used)) void* use69981 = (void*)&foo69981;
+__attribute__((used)) void* use69982 = (void*)&foo69982;
+__attribute__((used)) void* use69983 = (void*)&foo69983;
+__attribute__((used)) void* use69984 = (void*)&foo69984;
+__attribute__((used)) void* use69985 = (void*)&foo69985;
+__attribute__((used)) void* use69986 = (void*)&foo69986;
+__attribute__((used)) void* use69987 = (void*)&foo69987;
+__attribute__((used)) void* use69988 = (void*)&foo69988;
+__attribute__((used)) void* use69989 = (void*)&foo69989;
+__attribute__((used)) void* use69990 = (void*)&foo69990;
+__attribute__((used)) void* use69991 = (void*)&foo69991;
+__attribute__((used)) void* use69992 = (void*)&foo69992;
+__attribute__((used)) void* use69993 = (void*)&foo69993;
+__attribute__((used)) void* use69994 = (void*)&foo69994;
+__attribute__((used)) void* use69995 = (void*)&foo69995;
+__attribute__((used)) void* use69996 = (void*)&foo69996;
+__attribute__((used)) void* use69997 = (void*)&foo69997;
+__attribute__((used)) void* use69998 = (void*)&foo69998;
+__attribute__((used)) void* use69999 = (void*)&foo69999;
+__attribute__((used)) void* use70000 = (void*)&foo70000;
index 2d36cf917f57bd164b9a432e40baa07fc23051b2..efc78e156b2111798449b2265bb891d16dee1b15 100644 (file)
@@ -1,7 +1,6 @@
-// BUILD_ONLY:      MacOSX
-// BUILD_MIN_OS:    10.5
+// BUILD(macos|x86_64):  $CC main.c    -mmacosx-version-min=10.5 -o $BUILD_DIR/crt-vars10.5-libSystem.exe
 
-// BUILD:  $CC main.c            -o $BUILD_DIR/crt-vars10.5-libSystem.exe
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  ./crt-vars10.5-libSystem.exe
 
index ee3d8b3243c0b1a2d538dee3689bbad4c34f1e42..3ed21f7a34b867f4bf2f16f598c439ab6592320c 100644 (file)
@@ -1,7 +1,6 @@
-// BUILD_ONLY:      MacOSX
-// BUILD_MIN_OS:    10.6
+// BUILD(macos|x86_64):  $CC main.c    -mmacosx-version-min=10.6 -o $BUILD_DIR/crt-vars10.6-libSystem.exe
 
-// BUILD:  $CC main.c            -o $BUILD_DIR/crt-vars10.6-libSystem.exe
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  ./crt-vars10.6-libSystem.exe
 
index a4d11b59a0aa0f34ff2e4c1efb8f491221f6425c..10f71a4a55d92c7112be8b783ea89a6cd7ba7531 100644 (file)
@@ -38,14 +38,14 @@ int main(int argc, const char* argv[], const char* envp[], const char* apple[])
     /// 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 ) {
         FAIL("dlopen(\"libfoo1.dylib\", RTLD_GLOBAL) failed but it should have worked: %s", dlerror());
     }
+    void* handle2 = dlopen(RUN_DIR "/libfoo2.dylib", RTLD_LOCAL);
     if ( handle2 == NULL ) {
         FAIL("dlopen(\"libfoo2.dylib\", RTLD_LOCAL) failed but it should have worked: %s", dlerror());
     }
+    void* handle3 = dlopen(RUN_DIR "/libfoo3.dylib", RTLD_GLOBAL);
     if ( handle3 == NULL ) {
         FAIL("dlopen(\"libfoo3.dylib\", RTLD_GLOBAL) failed but it should have worked: %s", dlerror());
     }
index a49cf34a515a01a51e87e0aa748b8ee15d29ac22..cbd93fcf3b53541fd2dce56bdf78be908e210723 100644 (file)
@@ -1,21 +1,21 @@
-// BUILD_ONLY: MacOSX
+// BUILD(macos):  $CC bar.c -dynamiclib -o $BUILD_DIR/test1/libtest1.dylib -install_name @rpath/libtest1.dylib
+// BUILD(macos):  $CC foo.c -bundle -o $BUILD_DIR/test1.bundle -Wl,-rpath,@loader_path/test1/ $BUILD_DIR/test1/libtest1.dylib
 
-// 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(macos):  $CC bar.c -dynamiclib -o $BUILD_DIR/test2/libtest2.dylib -install_name @loader_path/test2/libtest2.dylib
+// BUILD(macos):  $CC foo.c -bundle -o $BUILD_DIR/test2.bundle $BUILD_DIR/test2/libtest2.dylib
 
-// 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(macos):  $CC bar.c -dynamiclib -o $BUILD_DIR/test3/libtest3.dylib -install_name @rpath/libtest3.dylib
+// BUILD(macos):  $CC foo.c -bundle -o $BUILD_DIR/test3.bundle -Wl,-rpath,$RUN_DIR/test3  $BUILD_DIR/test3/libtest3.dylib 
 
-// 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(macos):  $CC bar.c -dynamiclib -o $BUILD_DIR/test4/libtest4.dylib -install_name @rpath/libtest4.dylib
+// BUILD(macos):  $CC foo.c -bundle -o $BUILD_DIR/test4.bundle -Wl,-rpath,@executable_path/test4/ $BUILD_DIR/test4/libtest4.dylib
 
-// 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(macos):  $CC bar.c -dynamiclib -o $BUILD_DIR/test5/libtest5.dylib -install_name @executable_path/test5/libtest5.dylib
+// BUILD(macos):  $CC foo.c -bundle -o $BUILD_DIR/test5.bundle $BUILD_DIR/test5/libtest5.dylib
 
-// 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(macos):  $CC main.c -o $BUILD_DIR/dlopen-restricted.exe -DRUN_DIR="$RUN_DIR" -sectcreate __RESTRICT __restrict /dev/null
 
-// BUILD:  $CC main.c -o $BUILD_DIR/dlopen-restricted.exe -DRUN_DIR="$RUN_DIR" -sectcreate __RESTRICT __restrict /dev/null
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  ./dlopen-restricted.exe
 
diff --git a/testing/test-cases/dlopen-dyld.dtest/main.c b/testing/test-cases/dlopen-dyld.dtest/main.c
new file mode 100644 (file)
index 0000000..2d011d8
--- /dev/null
@@ -0,0 +1,21 @@
+
+// BUILD:  $CC main.c -o $BUILD_DIR/dlopen-dyld.exe
+
+// RUN:  ./dlopen-dyld.exe
+
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <dlfcn.h>
+
+#include "test_support.h"
+
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[])
+{
+    void* handle = dlopen("/usr/lib/dyld", RTLD_LAZY);
+    if ( handle == NULL )
+        PASS("Success");
+    else
+        FAIL("dlopen-dyld: dlopen() should have failed");
+}
index abb9139c193726720c6859f0528440a8ef1e62c9..6954db02bc3ab5106092a77a28b095f124347aab 100644 (file)
@@ -1,7 +1,7 @@
-// BUILD_ONLY: MacOSX
+// BUILD(macos):  $CC a.c -dynamiclib -arch x86_64h -o $BUILD_DIR/libHaswellCheck.dylib -install_name $RUN_DIR/libHaswellCheck.dylib
+// BUILD(macos):  $CC main.c -o $BUILD_DIR/dlopen-haswell.exe -DRUN_DIR="$RUN_DIR"
 
-// 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"
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  ./dlopen-haswell.exe
 
diff --git a/testing/test-cases/dlopen-iOSMac.dtest/cat.c b/testing/test-cases/dlopen-iOSMac.dtest/cat.c
new file mode 100644 (file)
index 0000000..8b61ebc
--- /dev/null
@@ -0,0 +1 @@
+void func() {}
diff --git a/testing/test-cases/dlopen-iOSMac.dtest/foo.c b/testing/test-cases/dlopen-iOSMac.dtest/foo.c
new file mode 100644 (file)
index 0000000..85e6cd8
--- /dev/null
@@ -0,0 +1 @@
+void foo() {}
diff --git a/testing/test-cases/dlopen-iOSMac.dtest/main.c b/testing/test-cases/dlopen-iOSMac.dtest/main.c
new file mode 100644 (file)
index 0000000..3ee86c2
--- /dev/null
@@ -0,0 +1,56 @@
+// BUILD(macos):  $CC main.c   -o $BUILD_DIR/dlopen-iOSMac.exe -DFOR_IOSMAC=1  -target x86_64-darwin-ios13.0-macabi
+// BUILD(macos):  $CC main.c   -o $BUILD_DIR/dlopen-macOS.exe -DRUN_DIR="$RUN_DIR"
+// BUILD(macos):  $CC cat.c -dynamiclib -install_name $RUN_DIR/libcat.dylib  -o $BUILD_DIR/libcat.dylib -target x86_64-darwin-ios13.0-macabi
+// BUILD(macos):  $CC foo.c -dynamiclib -install_name $RUN_DIR/libtestnotincache.dylib  -o $BUILD_DIR/libtestnotincache.dylib $BUILD_DIR/libcat.dylib
+// BUILD(macos):  $CC foo.c -dynamiclib -install_name $RUN_DIR/libtestincache.dylib  -o $BUILD_DIR/libtestincache.dylib -framework UIKit -F/System/iOSSupport/System/Library/Frameworks
+
+// BUILD(ios,tvos,watchos,bridgeos):
+
+// RUN:  ./dlopen-iOSMac.exe
+// RUN:  ./dlopen-macOS.exe
+
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+
+#include "test_support.h"
+
+int main(int arg, const char* argv[])
+{
+
+#if FOR_IOSMAC
+    void* handle = dlopen("/System/Library/Frameworks/UIKit.framework/UIKit", RTLD_LAZY);
+    if ( handle == NULL ) {
+        FAIL("dlopen-iOS-on-Mac, UIKit failed to dlopen(): %s", dlerror());
+        return 0;
+    }
+#else
+    void* handle = dlopen("/System/Library/Frameworks/AppKit.framework/AppKit", RTLD_LAZY);
+    if ( handle == NULL ) {
+        FAIL("dlopen-macOS, AppKit failed to dlopen(): %s", dlerror());
+        return 0;
+    }
+    // verify that can't load a macOS dylib that links with a catalyst dylib
+    handle = dlopen(RUN_DIR "/libtestnotincache.dylib", RTLD_LAZY);
+    if ( handle != NULL ) {
+        FAIL("dlopen-macOS, libtestnotincache.dylib should not be loaded");
+        return 0;
+    }
+    // verify that can't load a macOS dylib that links with a catalyst dylib which is in the dyld cache
+    handle = dlopen(RUN_DIR "/libtestincache.dylib", RTLD_LAZY);
+    if ( handle != NULL ) {
+        FAIL("dlopen-macOS, libtestincache.dylib should not be loaded");
+        return 0;
+    }
+#endif
+
+#if FOR_IOSMAC
+    PASS("dlopen-iOS-on-Mac");
+#else
+    PASS("dlopen-macOS");
+#endif
+
+       return 0;
+}
+
index 2e9069415c2e9a0dc4ceba4e9e8caa740e5a5bae..7c1e9daa164890c654f31440dd267dcd7b77dfea 100644 (file)
@@ -1,9 +1,9 @@
-// BUILD_ONLY: MacOSX
+// BUILD(macos):  $CC main.c  -o $BUILD_DIR/dlopen-indirect-groupNum.exe -Wno-deprecated-declarations
+// BUILD(macos):  $CC foo.c   -o $BUILD_DIR/foo.bundle -bundle
+// BUILD(macos):  $CC bar.c -dynamiclib  -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
+// BUILD(macos):  $CC baz.c -dynamiclib  -install_name $RUN_DIR/libbaz.dylib -o $BUILD_DIR/libbaz.dylib $BUILD_DIR/libbar.dylib
 
-// BUILD:  $CC main.c  -o $BUILD_DIR/dlopen-indirect-groupNum.exe -Wno-deprecated-declarations
-// BUILD:  $CC foo.c   -o $BUILD_DIR/foo.bundle -bundle
-// BUILD:  $CC bar.c -dynamiclib  -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
-// BUILD:  $CC baz.c -dynamiclib  -install_name $RUN_DIR/libbaz.dylib -o $BUILD_DIR/libbaz.dylib $BUILD_DIR/libbar.dylib
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  ./dlopen-indirect-groupNum.exe $RUN_DIR/foo.bundle $RUN_DIR/libbar.dylib $RUN_DIR/libbaz.dylib
 
diff --git a/testing/test-cases/dlopen-jna.dtest/foo.c b/testing/test-cases/dlopen-jna.dtest/foo.c
new file mode 100644 (file)
index 0000000..c62af36
--- /dev/null
@@ -0,0 +1,26 @@
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+
+#include "test_support.h"
+
+void foo() {
+       // foo can't see libFoundation.dylib as it's name isn't correct
+    void* handle = dlopen("libFoundation.dylib", RTLD_LAZY);
+    if ( handle != NULL ) {
+        FAIL("dlopen-jna, libfoo should not be able to dlopen()");
+    }
+
+    // foo can't see libSecurity.dylib as it's name isn't correct
+    void* handle2 = dlopen("libSecurity.dylib", RTLD_LAZY);
+    if ( handle2 != NULL ) {
+        FAIL("dlopen-jna, libfoo should not be able to dlopen()");
+    }
+
+    // foo can't see libCarbon.dylib as it's name isn't correct
+    void* handle2 = dlopen("libCarbon.dylib", RTLD_LAZY);
+    if ( handle2 != NULL ) {
+        FAIL("dlopen-jna, libfoo should not be able to dlopen()");
+    }
+}
diff --git a/testing/test-cases/dlopen-jna.dtest/jna.c b/testing/test-cases/dlopen-jna.dtest/jna.c
new file mode 100644 (file)
index 0000000..9768ece
--- /dev/null
@@ -0,0 +1,26 @@
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+
+#include "test_support.h"
+
+void jna() {
+    // jna should see libFoundation.dylib as it's name is correct
+    void* handle = dlopen("libFoundation.dylib", RTLD_LAZY);
+    if ( handle == NULL ) {
+        FAIL("dlopen-jna, libjna not be able to dlopen(): %s", dlerror());
+    }
+
+    // jna should see libSecurity.dylib as it's name is correct
+    void* handle2 = dlopen("libSecurity.dylib", RTLD_LAZY);
+    if ( handle2 == NULL ) {
+        FAIL("dlopen-jna, libjna not be able to dlopen(): %s", dlerror());
+    }
+
+    // jna should see libCarbon.dylib as it's name is correct
+    void* handle2 = dlopen("libCarbon.dylib", RTLD_LAZY);
+    if ( handle2 == NULL ) {
+        FAIL("dlopen-jna, libjna not be able to dlopen(): %s", dlerror());
+    }
+}
diff --git a/testing/test-cases/dlopen-jna.dtest/main.c b/testing/test-cases/dlopen-jna.dtest/main.c
new file mode 100644 (file)
index 0000000..d2748ae
--- /dev/null
@@ -0,0 +1,21 @@
+// BUILD(macos):  $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib  -o $BUILD_DIR/libfoo.dylib
+// BUILD(macos):  $CC jna.c -dynamiclib -install_name $RUN_DIR/jna.dylib  -o $BUILD_DIR/jna.dylib
+// BUILD(macos):  $CC main.c -o $BUILD_DIR/dlopen-jna.exe $BUILD_DIR/libfoo.dylib $BUILD_DIR/jna.dylib
+
+// BUILD(ios,tvos,watchos,bridgeos):
+
+// RUN:  ./dlopen-jna.exe
+
+#include "test_support.h"
+
+extern void foo();
+extern void jna();
+
+int main(int arg, const char* argv[])
+{
+    foo();
+    jna();
+    PASS("Success");
+       return 0;
+}
+
diff --git a/testing/test-cases/dlopen_from-basic.dtest/bar.c b/testing/test-cases/dlopen_from-basic.dtest/bar.c
new file mode 100644 (file)
index 0000000..320c246
--- /dev/null
@@ -0,0 +1,4 @@
+void bar()
+{
+}
+
diff --git a/testing/test-cases/dlopen_from-basic.dtest/main.c b/testing/test-cases/dlopen_from-basic.dtest/main.c
new file mode 100644 (file)
index 0000000..40c0d23
--- /dev/null
@@ -0,0 +1,45 @@
+
+// BUILD:  $CC bar.c  -dynamiclib -install_name @rpath/libbar.dylib -o $BUILD_DIR/dir/libbar.dylib
+// BUILD:  $CC bar.c  -dynamiclib -install_name @rpath/libbaz.dylib -o $BUILD_DIR/dir/libbaz.dylib
+// BUILD:  $CC test.c -dynamiclib -install_name $RUN_DIR/test/libtest.dylib -o $BUILD_DIR/test/libtest.dylib -rpath @loader_path/../dir
+// BUILD:  $CC main.c -o $BUILD_DIR/dlopen_from-basic.exe $BUILD_DIR/test/libtest.dylib
+
+// RUN:  ./dlopen_from-basic.exe
+
+#include <stdio.h>
+#include <dlfcn_private.h>
+#include <stdbool.h>
+
+#include "test_support.h"
+
+/// test dlopen_from() works for both @rpath and @loader_path
+
+extern void test();
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[])
+{
+    // verify the straight dlopen fails because libtest.dylib sets up the LC_RPATH
+    void* handle = dlopen("@rpath/libbar.dylib", RTLD_LAZY);
+    if ( handle != NULL ) {
+        FAIL("dlopen(\"@rpath/libbar.dylib\") should not have succeeded");
+    }
+    // verify dlopen_from() works becuase libtest.dylib sets up an LC_RPATH
+    handle = dlopen_from("@rpath/libbar.dylib", RTLD_LAZY, &test);
+    if ( handle == NULL ) {
+        FAIL("dlopen_from(\"@rpath/libbar.dylib\", &test) failed: %s", dlerror());
+    }
+
+    // verify the straight dlopen fails because main executables @loader_path is used
+    handle = dlopen("@loader_path/../dir/libbaz.dylib", RTLD_LAZY);
+    if ( handle != NULL ) {
+        FAIL("dlopen(\"@loader_path/../dir/libbaz.dylib\") should not have succeeded");
+    }
+    // verify dlopen_from() works with @loader_path resolving to where libtest.dylib is
+    handle = dlopen_from("@loader_path/../dir/libbaz.dylib", RTLD_LAZY, &test);
+    if ( handle == NULL ) {
+        FAIL("dlopen_from(\"@loader_path/../dir/libbaz.dylib\", &test) failed: %s", dlerror());
+    }
+
+    PASS("dlopen_from");
+}
+
diff --git a/testing/test-cases/dlopen_from-basic.dtest/test.c b/testing/test-cases/dlopen_from-basic.dtest/test.c
new file mode 100644 (file)
index 0000000..93f31e9
--- /dev/null
@@ -0,0 +1,11 @@
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdbool.h>
+
+#include "test_support.h"
+
+void test()
+{
+}
+
diff --git a/testing/test-cases/dlopen_preflight.dtest/main.c b/testing/test-cases/dlopen_preflight.dtest/main.c
new file mode 100644 (file)
index 0000000..2c99ba6
--- /dev/null
@@ -0,0 +1,25 @@
+
+// BUILD:  $CC main.c -DRUN_DIR="$RUN_DIR" -o $BUILD_DIR/dlopen_preflight.exe
+
+// RUN:  ./dlopen_preflight.exe
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+#include "test_support.h"
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[])
+{
+    if ( !dlopen_preflight("/System/Library/Frameworks/Foundation.framework/Foundation") ) {
+        FAIL("Foundation.framework cannot be found");
+    }
+
+#if TARGET_OS_OSX
+    if ( !dlopen_preflight("/System/Library/Frameworks/Foundation.framework/Versions/Current/Foundation") ) {
+        FAIL("Foundation.framework via symlink cannot be found");
+    }
+#endif
+
+    PASS("SUCCESS");
+}
+
index b3b79952313b272e1aea7dca4c34e1b984220f94..3223223f7b950d687aca3cf5aff13f4334145178 100644 (file)
@@ -1,10 +1,10 @@
-// BUILD_ONLY: MacOSX
-
 // if we ever re-enable this on iOS we will need to add // BOOT_ARGS: dtrace_dof_mode=1
 
-// BUILD:  $DTRACE -h -s main.d -o $BUILD_DIR/probes.h
-// BUILD:  $CC main.c -I$BUILD_DIR -o $BUILD_DIR/dtrace.exe $DEPENDS_ON $BUILD_DIR/probes.h
-// BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/dtrace.exe
+// BUILD(macos):  $DTRACE -h -s main.d -o $BUILD_DIR/probes.h
+// BUILD(macos):  $CC main.c -I$BUILD_DIR -o $BUILD_DIR/dtrace.exe $DEPENDS_ON $BUILD_DIR/probes.h
+// BUILD(macos):  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/dtrace.exe
+
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:    $SUDO /usr/sbin/dtrace -o /dev/null 2> /dev/null -n 'dyld_testing*:dtrace.exe:main:callback' -c $RUN_DIR/dtrace.exe
 
index 9dfdc34742724a0bfee273426ad449064f63985a..67abdf02c4ac1c906454bce90b07531f74b70c78 100644 (file)
@@ -5,7 +5,7 @@
 // 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:  $CXX main.cpp -o $BUILD_DIR/dyld_abort_tests.exe
+// BUILD:  $CXX main.cpp -o $BUILD_DIR/dyld_abort_tests.exe -DRUN_DIR="$RUN_DIR"
 
 // NO_CRASH_LOG: prog_missing_dylib.exe
 // NO_CRASH_LOG: prog_missing_symbol.exe
 
 
 void runTest(const char* prog, uint64_t dyldReason, const char* expectedDylibPath, const char* expectedSymbol) {
+    dispatch_block_t oneShotSemaphoreBlock = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS, ^{});
+    LOG("Running test for %s / %s / %s", prog, expectedDylibPath, expectedSymbol);
     _process process;
     process.set_executable_path(prog);
     process.set_crash_handler(^(task_t task) {
         LOG("Crash for task=%u", task);
-        vm_address_t corpse_data;
-        uint32_t corpse_size;
-        if (task_map_corpse_info(mach_task_self(), task, &corpse_data, &corpse_size) != KERN_SUCCESS) {
-            FAIL("Could not read corpse data");
+        mach_vm_address_t corpse_data;
+        mach_vm_size_t corpse_size;
+        kern_return_t kr = task_map_corpse_info_64(mach_task_self(), task, &corpse_data, &corpse_size);
+        if (kr != KERN_SUCCESS) {
+            FAIL("Could not read corpse data kr=(%d)", kr);
         }
+
         kcdata_iter_t autopsyData = kcdata_iter((void*)corpse_data, corpse_size);
         if (!kcdata_iter_valid(autopsyData)) {
             FAIL("Corpse Data Invalid");
         }
+
+        kcdata_iter_t pidIter = kcdata_iter_find_type(autopsyData, TASK_CRASHINFO_PID);
+        pid_t pid = *(pid_t *) (kcdata_iter_payload(pidIter));
+        LOG("Crash for pid=%u", pid);
+
         kcdata_iter_t exitReasonData = kcdata_iter_find_type(autopsyData, EXIT_REASON_SNAPSHOT);
         if (!kcdata_iter_valid(exitReasonData)) {
             FAIL("Could not find exit data");
@@ -58,6 +67,7 @@ void runTest(const char* prog, uint64_t dyldReason, const char* expectedDylibPat
             FAIL("eri_code (%llu) != dyldReason (%lld)", ers->ers_code, dyldReason);
         }
         kcdata_iter_t iter = kcdata_iter((void*)corpse_data, corpse_size);
+        bool foundOSReason = false;
 
         KCDATA_ITER_FOREACH(iter) {
             if (kcdata_iter_type(iter) == KCDATA_TYPE_NESTED_KCDATA) {
@@ -65,6 +75,7 @@ void runTest(const char* prog, uint64_t dyldReason, const char* expectedDylibPat
                 if ( kcdata_iter_type(nestedIter) != KCDATA_BUFFER_BEGIN_OS_REASON ){
                     return;
                 }
+                foundOSReason = true;
                 kcdata_iter_t payloadIter = kcdata_iter_find_type(nestedIter, EXIT_REASON_USER_PAYLOAD);
                 if ( !kcdata_iter_valid(payloadIter) ) {
                     FAIL("invalid kcdata payload iterator from payload data");
@@ -100,19 +111,28 @@ void runTest(const char* prog, uint64_t dyldReason, const char* expectedDylibPat
                         FAIL("symbol (%s) not provided by dyld", expectedSymbol);
                     }
                 }
-                PASS("Success");
             }
         }
-        FAIL("Did not find EXIT_REASON_USER_PAYLOAD");
+        if (!foundOSReason) {
+            FAIL("Did not find KCDATA_BUFFER_BEGIN_OS_REASON");
+        }
+        oneShotSemaphoreBlock();
     });
-    process.launch();
+
+    pid_t pid = process.launch();
+    LOG("Launch pid: %u", pid);
+    // We need to wait for the task to crash in before we call PASS(). Ideally we would return the block and wait
+    // in main(), that way we could run multiple processes simultaneously, but when we do that there are strange crashes
+    // and deadlocks, I suspect it has something to do with block capture of parameters sometimes being references.
+    // but it is not entirely clear what the intended semantics are.
+    dispatch_block_wait(oneShotSemaphoreBlock, DISPATCH_TIME_FOREVER);
 }
 
 
 int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // test launch program with missing library
-    runTest("./prog_missing_dylib.exe", DYLD_EXIT_REASON_DYLIB_MISSING, "/cant/find/me.dylib", NULL);
-//    runTest("./prog_missing_symbol.exe", DYLD_EXIT_REASON_SYMBOL_MISSING, "libMissingSymbols.dylib", "_slipperySymbol");
+    runTest(RUN_DIR "/prog_missing_dylib.exe", DYLD_EXIT_REASON_DYLIB_MISSING, "/cant/find/me.dylib", NULL);
+    runTest(RUN_DIR "/prog_missing_symbol.exe", DYLD_EXIT_REASON_SYMBOL_MISSING, "libMissingSymbols.dylib", "_slipperySymbol");
     PASS("Success");
 }
 
index 20b502962109e013af1487bd72e7fc215744facb..98001688bb5291484f803fe9c96e11ab3415d163 100644 (file)
 int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     // We only support trying to save to containerised paths, so anything not
     // of that form should fail
-    if ( !dyld_need_closure("./foo.exe", "/tmp/Containers/Data/") ) {
-        FAIL("Should have needed a closure for containerised path");
-    }
 
-    if ( dyld_need_closure("./foo.exe", "/tmp/Containers/Data2/") ) {
+// FIXME: dyld_need_closure() needs an existing directory structure, so we can't run this in BATS
+//    if ( !dyld_need_closure("./foo.exe", "/private/var/mobile/Containers/Data/Application") ) {
+//        FAIL("Should have needed a closure for containerised path");
+//    }
+
+    if ( dyld_need_closure("./foo.exe", "/private/var/mobile/Other/Stuff") ) {
         FAIL("Should have rejected a closure for non-containerised path");
     }
 
index 75d678946cc45cba74ae4a23671ada93018fee6b..3b726e69982dfebca3b897e6741190edcae7b267 100644 (file)
@@ -55,8 +55,8 @@ static void inspectProcess(task_t task, bool launchedSuspended, bool expectCF, b
         if (remotePlatform != 0)  {
             FAIL("_dyld_process_info_get_platform() should be 0 for launchSuspended processes");
         }
-    } else if (forceIOSMac && (remotePlatform != PLATFORM_IOSMAC)) {
-        FAIL("_dyld_process_info_get_platform(%u) should be PLATFORM_IOSMAC", remotePlatform);
+    } else if (forceIOSMac && (remotePlatform != PLATFORM_MACCATALYST)) {
+        FAIL("_dyld_process_info_get_platform(%u) should be PLATFORM_MACCATALYST", remotePlatform);
     } else if (!forceIOSMac && (remotePlatform != localPlatform)) {
         FAIL("_dyld_process_info_get_platform(%u) should be the same dyld_get_active_platform(%u)",
              remotePlatform, localPlatform);
diff --git a/testing/test-cases/dyld_usage_json.dtest/foo.c b/testing/test-cases/dyld_usage_json.dtest/foo.c
new file mode 100644 (file)
index 0000000..8eb2dc5
--- /dev/null
@@ -0,0 +1,4 @@
+int foo()
+{
+    return 0;
+}
diff --git a/testing/test-cases/dyld_usage_json.dtest/main.mm b/testing/test-cases/dyld_usage_json.dtest/main.mm
new file mode 100644 (file)
index 0000000..fe9143d
--- /dev/null
@@ -0,0 +1,236 @@
+// BUILD(macos):  $CC foo.c         -o $BUILD_DIR/libfoo.dylib -dynamiclib -install_name $RUN_DIR/lifoo.dylib
+// BUILD(macos):  $CC target.c      -o $BUILD_DIR/dyld_usage_target.exe  -DRUN_DIR="$RUN_DIR"
+// BUILD(macos):  $CXX main.mm     -o $BUILD_DIR/dyld_usage_json.exe -DRUN_DIR="$RUN_DIR" -std=c++14 -framework Foundation
+
+// BUILD(ios,tvos,watchos,bridgeos):
+
+// RUN:  $SUDO ./dyld_usage_json.exe
+
+#import <Foundation/Foundation.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <spawn.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "test_support.h"
+
+enum class NodeValueType {
+    Default,
+    String,
+    RawValue,
+};
+
+struct Node
+{
+    NodeValueType               type = NodeValueType::Default;
+    std::string                 value;
+    std::map<std::string, Node> map;
+    std::vector<Node>           array;
+
+    inline Node()
+    : type(NodeValueType::Default), value(), map(), array() { }
+
+    inline Node(std::string string)
+    : type(NodeValueType::String), value(string), map(), array() { }
+
+    inline Node(const char *string) : Node(std::string(string)) { }
+
+    inline Node(bool b)
+    : type(NodeValueType::RawValue), value(b ? "true" : "false")
+    , map(), array() { }
+
+    inline Node(int64_t i64)
+    : type(NodeValueType::RawValue), value(), map(), array()
+    {
+        std::ostringstream os;
+        os << i64;
+        value = os.str();
+    }
+
+    inline Node(uint64_t u64)
+    : type(NodeValueType::RawValue), value(), map(), array()
+    {
+        std::ostringstream os;
+        os << u64;
+        value = os.str();
+    }
+};
+
+static Node parseNode(id jsonObject) {
+    __block Node node;
+
+    // NSDictionary -> map
+    if ([jsonObject isKindOfClass:[NSDictionary class]]) {
+        NSDictionary* dict = (NSDictionary*)jsonObject;
+
+        [dict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL* stop) {
+            if (![key isKindOfClass:[NSString class]]) {
+                fprintf(stderr, "JSON map key is not of string type\n");
+                *stop = true;
+                return;
+            }
+            Node childNode = parseNode(value);
+
+            node.map[[key UTF8String]] = childNode;
+        }];
+
+        return node;
+    }
+
+    // NSArray -> array
+    if ([jsonObject isKindOfClass:[NSArray class]]) {
+        NSArray* array = (NSArray*)jsonObject;
+
+        [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) {
+            Node childNode = parseNode(obj);
+            node.array.push_back(childNode);
+        }];
+
+        return node;
+    }
+
+    // NSString -> value
+    if ([jsonObject isKindOfClass:[NSString class]]) {
+        node.value = [(NSString*)jsonObject UTF8String];
+        return node;
+    }
+
+    fprintf(stderr, "Unknown json deserialized type\n");
+    return Node();
+}
+
+Node readJSON(const void * contents, size_t length) {
+    NSData* data = [NSData dataWithBytes:contents length:length];
+    NSError* error = nil;
+    id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
+    if (!jsonObject) {
+        fprintf(stderr, "Could not deserialize json because '%s'",[[error localizedFailureReason] UTF8String]);
+        return Node();
+    }
+
+    return parseNode(jsonObject);
+}
+
+char* mergeJsonRoots(char* jsonBuffer, size_t size)
+{
+    char *mergedJson = (char*)malloc((size + 2) * sizeof(char));
+    mergedJson[0] = '[';
+    mergedJson[size+1] = '\0';
+    mergedJson[size+1] = ']';
+    for (size_t i = 0; i < size; i++) {
+        mergedJson[i+1] = jsonBuffer[i];
+        if (jsonBuffer[i] == '\n') {
+            if ( i > 0 && i < size - 1 ) {
+                if (jsonBuffer[i-1] == '}' && jsonBuffer[i+1] == '{')
+                    mergedJson[i+1] = ',';
+            }
+        }
+    }
+    return mergedJson;
+}
+
+void validateJson(Node json, pid_t pid)
+{
+    size_t expectedSize = 4;
+    if (json.array.size() != expectedSize)
+        FAIL("dyld_usage reported number of events is incorrect. Reported %lu instead of %lu", json.array.size(), expectedSize);
+
+    std::string handle = json.array[1].map["event"].map["result"].value;
+
+    for (size_t i = 0; i < json.array.size(); i++) {
+
+        if ( json.array[i].map["command"].value.compare("dyld_usage_target.exe") != 0 )
+            FAIL("Incorrect command name for event at index %lu", i);
+
+        int jpid = std::stoi(json.array[i].map["pid"].value);
+        if ( jpid != pid)
+            FAIL("Incorrect pid for event at index %lu. Reported %d intead of %d (%s)", i, jpid, pid, json.array[i].map["pid"].value.c_str());
+
+        if (i == 0) {
+            if ( json.array[i].map["event"].map["type"].value.compare("app_launch") != 0 )
+                FAIL("dyld_usage did not report app launch event");
+        }
+
+        if (i == 1) {
+            if ( json.array[1].map["event"].map["type"].value.compare("dlopen") != 0 )
+                FAIL("dyld_usage did not report dlopen event");
+            if ( json.array[1].map["event"].map["path"].value.compare(RUN_DIR "/libfoo.dylib") != 0 )
+                FAIL("Incorrect dlopen library path");
+        }
+
+        if (i == 2) {
+            if ( json.array[i].map["event"].map["type"].value.compare("dlsym") != 0 )
+                FAIL("dyld_usage did not report dlsym event");
+            if ( json.array[i].map["event"].map["symbol"].value.compare("foo") != 0 )
+                FAIL("incorrect dlsym symbol reported");
+            if ( json.array[i].map["event"].map["handle"].value.compare(handle) != 0 )
+                FAIL("dlsym handle does not match dlopen result");
+        }
+
+        if (i == 3) {
+            if ( json.array[i].map["event"].map["type"].value.compare("dlclose") != 0 )
+                FAIL("dyld_usage did not report dlclose event");
+            if ( json.array[i].map["event"].map["handle"].value.compare(handle) != 0 )
+                FAIL("dlclose handle does not match dlopen result");
+        }
+    }
+}
+
+int main(int argc, const char* argv[], char *env[])
+{
+    _process dyldUsage;
+    dyldUsage.set_executable_path("/usr/local/bin/dyld_usage");
+    dyldUsage.set_launch_async(true);
+    const char* args[] = { "-j", "dyld_usage_target.exe", NULL };
+    dyldUsage.set_args(args);
+    __block dispatch_data_t output = NULL;
+    dyldUsage.set_stdout_handler(^(int fd) {
+        ssize_t size = 0;
+        do {
+            char buffer[16384] = {0};
+            size = read(fd, buffer, 16384);
+            if ( size == -1 )
+                break;
+            dispatch_data_t data = dispatch_data_create(buffer, size, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
+            output = output ? dispatch_data_create_concat(output, data) : data;
+        } while ( size > 0 );
+    });
+
+    pid_t pid = dyldUsage.launch();
+    usleep(2000000);
+
+    // Launch target
+    _process target;
+    target.set_executable_path(RUN_DIR "/dyld_usage_target.exe");
+    pid_t tpid = target.launch();
+
+    usleep(2000000);
+    // Kill dyld_usage
+    kill(pid, SIGTERM);
+
+
+    int status;
+    if (waitpid(pid, &status, 0) == -1)
+        FAIL("waitpid failed");
+    if ( !output )
+        FAIL("No dyld_usage output");
+
+    const void* buffer;
+    size_t size;
+    (void)dispatch_data_create_map(output, &buffer, &size);
+    char* jsonBuffer = mergeJsonRoots((char*)buffer, size);
+    size += 2;
+    Node node = readJSON(jsonBuffer, size);
+    free(jsonBuffer);
+    validateJson(node, tpid);
+
+    PASS("Success");
+    return 0;
+}
diff --git a/testing/test-cases/dyld_usage_json.dtest/target.c b/testing/test-cases/dyld_usage_json.dtest/target.c
new file mode 100644 (file)
index 0000000..d6ad4f2
--- /dev/null
@@ -0,0 +1,11 @@
+#include <dlfcn.h>
+#include <unistd.h>
+
+#include "test_support.h"
+int main(int argc, const char* argv[])
+{
+    void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_NOW);
+    void *foo = dlsym(handle, "foo");
+    dlclose(handle);
+    return 0;
+}
index 3e738c9db6871bc447b872c3896482f5d410f21c..afd3b58961e8af253eddad213395bae1518741fb 100644 (file)
@@ -16,6 +16,9 @@ int main(int argc, const char* argv[], const char* envp[], const char* apple[])
     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 };
+    // We choose  high platform value that is unlikely to ever be used, and a non-zero version number
+    // If the platform number we choose here is ever used it will fail on that platform and this test will need to be fixed.
+    dyld_build_version_t bogusPlatformVersion = { .platform = 0xffff0000, .version = 1 };
 
 #if TARGET_OS_OSX
     if ( base != PLATFORM_MACOS ) {
@@ -71,6 +74,23 @@ int main(int argc, const char* argv[], const char* envp[], const char* apple[])
         FAIL("executable min version should not > 65536.0.0");
     }
 
+    if (dyld_minos_at_least(&__dso_handle, bogusPlatformVersion)) {
+        FAIL("dyld_minos_at_least should be false for bogus platform");
+    }
+
+    if (dyld_program_minos_at_least(bogusPlatformVersion)) {
+        FAIL("dyld_program_minos_at_least should be false for bogus platform");
+    }
+
+    if (dyld_sdk_at_least(&__dso_handle, bogusPlatformVersion)) {
+        FAIL("dyld_sdk_at_least should be false for bogus platform");
+    }
+
+    if (dyld_program_sdk_at_least(bogusPlatformVersion)) {
+        FAIL("dyld_program_sdk_at_least should be false for bogus platform");
+    }
+
+
     PASS("Success");
 }
 
diff --git a/testing/test-cases/dyld_version_spis.dtest/version_set.c b/testing/test-cases/dyld_version_spis.dtest/version_set.c
new file mode 100644 (file)
index 0000000..786dad4
--- /dev/null
@@ -0,0 +1,207 @@
+// FIXME:  -Wl,-platform_version triggers linker warnings, we need to find a way to stop clang from emitting -platform_version
+// BUILD(macos):  $CC version_set.c -Wl,-platform_version,macos,10.14,10.14     -o $BUILD_DIR/version_set_10.14.exe
+// BUILD(macos):  $CC version_set.c -Wl,-platform_version,macos,10.14.9,10.14.9 -o $BUILD_DIR/version_set_10.14.9.exe
+// BUILD(macos):  $CC version_set.c -Wl,-platform_version,macos,10.15,10.15     -o $BUILD_DIR/version_set_10.15.exe
+// BUILD(macos):  $CC version_set.c -Wl,-platform_version,macos,10.15.1,10.15.1 -o $BUILD_DIR/version_set_10.15.1.exe
+// BUILD(macos):  $CC version_set.c -Wl,-platform_version,macos,10.16,10.16     -o $BUILD_DIR/version_set_10.16.exe
+// BUILD(macos):  $CC version_set.c -Wl,-platform_version,macos,11.0,11.0     -o $BUILD_DIR/version_set_11.exe
+// RUN(macos):  ./version_set_10.14.exe
+// RUN(macos):  ./version_set_10.14.9.exe
+// RUN(macos):  ./version_set_10.15.exe
+// RUN(macos):  ./version_set_10.15.1.exe
+// RUN(macos):  ./version_set_10.16.exe
+// RUN(macos):  ./version_set_11.exe
+
+// BUILD(tvos):  $CC version_set.c -Wl,-platform_version,tvos,12.0,12.0 -o $BUILD_DIR/version_set_12.exe
+// BUILD(tvos):  $CC version_set.c -Wl,-platform_version,tvos,12.9,12.9 -o $BUILD_DIR/version_set_12.9.exe
+// BUILD(tvos):  $CC version_set.c -Wl,-platform_version,tvos,13.0,13.0 -o $BUILD_DIR/version_set_13.exe
+// BUILD(tvos):  $CC version_set.c -Wl,-platform_version,tvos,13.1,13.1 -o $BUILD_DIR/version_set_13.1.exe
+// BUILD(tvos):  $CC version_set.c -Wl,-platform_version,tvos,14.0,14.0 -o $BUILD_DIR/version_set_14.exe
+// RUN(tvos):  ./version_set_12.exe
+// RUN(tvos):  ./version_set_12.9.exe
+// RUN(tvos):  ./version_set_13.exe
+// RUN(tvos):  ./version_set_13.1.exe
+// RUN(tvos):  ./version_set_14.exe
+
+// BUILD(ios):  $CC version_set.c -Wl,-platform_version,ios,12.0,12.0 -o $BUILD_DIR/version_set_12.exe
+// BUILD(ios):  $CC version_set.c -Wl,-platform_version,ios,12.9,12.9 -o $BUILD_DIR/version_set_12.9.exe
+// BUILD(ios):  $CC version_set.c -Wl,-platform_version,ios,13.0,13.0 -o $BUILD_DIR/version_set_13.exe
+// BUILD(ios):  $CC version_set.c -Wl,-platform_version,ios,13.1,13.1 -o $BUILD_DIR/version_set_13.1.exe
+// BUILD(ios):  $CC version_set.c -Wl,-platform_version,ios,14.0,14.0 -o $BUILD_DIR/version_set_14.exe
+// RUN(ios):  ./version_set_12.exe
+// RUN(ios):  ./version_set_12.9.exe
+// RUN(ios):  ./version_set_13.exe
+// RUN(ios):  ./version_set_13.1.exe
+// RUN(ios):  ./version_set_14.exe
+
+// BUILD(watchos):  $CC version_set.c -Wl,-platform_version,watchos,5.0,5.0 -o $BUILD_DIR/version_set_5.exe
+// BUILD(watchos):  $CC version_set.c -Wl,-platform_version,watchos,5.9,5.9 -o $BUILD_DIR/version_set_5.9.exe
+// BUILD(watchos):  $CC version_set.c -Wl,-platform_version,watchos,6.0,6.0 -o $BUILD_DIR/version_set_6.exe
+// BUILD(watchos):  $CC version_set.c -Wl,-platform_version,watchos,6.1,6.1 -o $BUILD_DIR/version_set_6.1.exe
+// BUILD(watchos):  $CC version_set.c -Wl,-platform_version,watchos,7.0,7.0 -o $BUILD_DIR/version_set_7.exe
+// RUN(watchos):  ./version_set_5.exe
+// RUN(watchos):  ./version_set_5.9.exe
+// RUN(watchos):  ./version_set_6.exe
+// RUN(watchos):  ./version_set_6.1.exe
+// RUN(watchos):  ./version_set_7.exe
+
+// BUILD(bridgeos):
+
+#include <stdio.h>
+#include <string.h>
+#include <mach-o/dyld_priv.h>
+#include <dyld/for_dyld_priv.inc>
+
+#include "test_support.h"
+
+extern struct mach_header __dso_handle;
+
+#if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ == 101400)
+#define FALL_2018 true
+#define FALL_2019 false
+#define FALL_2020 false
+#define VERSION_NAME "macOS 10.14"
+#elif (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ == 101409)
+#define FALL_2018 true
+#define FALL_2019 false
+#define FALL_2020 false
+#define VERSION_NAME "macOS 10.14.9"
+#elif (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ == 101500)
+#define FALL_2018 true
+#define FALL_2019 true
+#define FALL_2020 false
+#define VERSION_NAME "macOS 10.15"
+#elif (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ == 101501)
+#define FALL_2018 true
+#define FALL_2019 true
+#define FALL_2020 false
+#define VERSION_NAME "macOS 10.15.1"
+#elif (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ == 101600)
+#define FALL_2018 true
+#define FALL_2019 true
+#define FALL_2020 true
+#define VERSION_NAME "macOS 10.16"
+#elif (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ == 110000)
+#define FALL_2018 true
+#define FALL_2019 true
+#define FALL_2020 true
+#define VERSION_NAME "macOS 11"
+#elif (__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ == 120000)
+#define FALL_2018 true
+#define FALL_2019 false
+#define FALL_2020 false
+#define VERSION_NAME "tvOS 12"
+#elif (__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ == 120900)
+#define FALL_2018 true
+#define FALL_2019 false
+#define FALL_2020 false
+#define VERSION_NAME "tvOS 12.9"
+#elif (__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ == 130000)
+#define FALL_2018 true
+#define FALL_2019 true
+#define FALL_2020 false
+#define VERSION_NAME "tvOS 13"
+#elif (__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ == 130100)
+#define FALL_2018 true
+#define FALL_2019 true
+#define FALL_2020 false
+#define VERSION_NAME "tvOS 13.1"
+#elif (__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ == 140000)
+#define FALL_2018 true
+#define FALL_2019 true
+#define FALL_2020 true
+#define VERSION_NAME "tvOS 14"
+#elif (__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ == 120000)
+#define FALL_2018 true
+#define FALL_2019 false
+#define FALL_2020 false
+#define VERSION_NAME "iOS 12"
+#elif (__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ == 120900)
+#define FALL_2018 true
+#define FALL_2019 false
+#define FALL_2020 false
+#define VERSION_NAME "iOS 12.9"
+#elif (__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ == 130000)
+#define FALL_2018 true
+#define FALL_2019 true
+#define FALL_2020 false
+#define VERSION_NAME "iOS 13"
+#elif (__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ == 130100)
+#define FALL_2018 true
+#define FALL_2019 true
+#define FALL_2020 false
+#define VERSION_NAME "iOS 13.1"
+#elif (__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ == 140000)
+#define FALL_2018 true
+#define FALL_2019 true
+#define FALL_2020 true
+#define VERSION_NAME "iOS 14"
+#elif (__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ == 50000)
+#define FALL_2018 true
+#define FALL_2019 false
+#define FALL_2020 false
+#define VERSION_NAME "watchOS 5"
+#elif (__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ == 50900)
+#define FALL_2018 true
+#define FALL_2019 false
+#define FALL_2020 false
+#define VERSION_NAME "watchOS 5.9"
+#elif (__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ == 60000)
+#define FALL_2018 true
+#define FALL_2019 true
+#define FALL_2020 false
+#define VERSION_NAME "watchOS 6"
+#elif (__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ == 60100)
+#define FALL_2018 true
+#define FALL_2019 true
+#define FALL_2020 false
+#define VERSION_NAME "watchOS 6.1"
+#elif (__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ == 70000)
+#define FALL_2018 true
+#define FALL_2019 true
+#define FALL_2020 true
+#define VERSION_NAME "watchOS 7"
+#else
+#error Unknown version
+#endif
+
+void testVersionChecks(const char* versionName, dyld_build_version_t testVersion, bool expected) {
+    if (expected != dyld_minos_at_least(&__dso_handle, testVersion)) {
+        FAIL(VERSION_NAME "should be %s than %s for dyld_minos_at_least()", versionName, expected ? "newer" : "older");
+    }
+    if (expected != dyld_sdk_at_least(&__dso_handle, testVersion)) {
+        FAIL(VERSION_NAME "should be %s than %s for dyld_sdk_at_least()", versionName, expected ? "newer" : "older");
+    }
+    if (expected != dyld_program_minos_at_least(testVersion)) {
+        FAIL(VERSION_NAME "should be %s than %s for dyld_program_minos_at_least()", versionName, expected ? "newer" : "older");
+    }
+    if (expected != dyld_program_sdk_at_least(testVersion)) {
+        FAIL(VERSION_NAME "should be %s than %s for dyld_program_sdk_at_least()", versionName, expected ? "newer" : "older");
+    }
+}
+
+int main(void) {
+    testVersionChecks("dyld_fall_2018_os_versions", dyld_fall_2018_os_versions, FALL_2018);
+    testVersionChecks("dyld_fall_2019_os_versions", dyld_fall_2019_os_versions, FALL_2019);
+    testVersionChecks("dyld_fall_2020_os_versions", dyld_fall_2020_os_versions, FALL_2020);
+
+#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
+    testVersionChecks("dyld_platform_version_macOS_10_14", dyld_platform_version_macOS_10_14, FALL_2018);
+    testVersionChecks("dyld_platform_version_macOS_10_15", dyld_platform_version_macOS_10_15, FALL_2019);
+    testVersionChecks("dyld_platform_version_macOS_10_16", dyld_platform_version_macOS_10_16, FALL_2020);
+#elif defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__)
+    testVersionChecks("dyld_platform_version_tvOS_12_0", dyld_platform_version_tvOS_12_0, FALL_2018);
+    testVersionChecks("dyld_platform_version_tvOS_13_0", dyld_platform_version_tvOS_13_0, FALL_2019);
+    testVersionChecks("dyld_platform_version_tvOS_14_0", dyld_platform_version_tvOS_14_0, FALL_2020);
+#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
+    testVersionChecks("dyld_platform_version_iOS_12_0", dyld_platform_version_iOS_12_0, FALL_2018);
+    testVersionChecks("dyld_platform_version_iOS_13_0", dyld_platform_version_iOS_13_0, FALL_2019);
+    testVersionChecks("dyld_platform_version_iOS_14_0", dyld_platform_version_iOS_14_0, FALL_2020);
+#elif defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__)
+    testVersionChecks("dyld_platform_version_watchOS_5_0", dyld_platform_version_watchOS_5_0, FALL_2018);
+    testVersionChecks("dyld_platform_version_watchOS_6_0", dyld_platform_version_watchOS_6_0, FALL_2019);
+    testVersionChecks("dyld_platform_version_watchOS_7_0", dyld_platform_version_watchOS_7_0, FALL_2020);
+#endif
+
+    PASS("Success");
+};
index 80ce72f22075a8d17e8ab963c0e107aa5dd48f23..f98ed0e115a3cc08916158c164c7fbc51c0259f7 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
+// BUILD(macos|x86_64):           $CC bar.c -mmacosx-version-min=10.5 -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
+// BUILD(macos|x86_64):           $CC foo.c -mmacosx-version-min=10.5 -dynamiclib $BUILD_DIR/libbar.dylib -sub_library libbar -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD(macos|x86_64):           $CC main.c -mmacosx-version-min=10.5 -o $BUILD_DIR/dylib-re-export.exe $BUILD_DIR/libfoo.dylib
+
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  ./dylib-re-export.exe
 
@@ -15,10 +15,13 @@ extern int bar();
 
 
 int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    PASS("Success");
+#if 0
     if ( bar() == 42 )
         PASS("Success");
     else
         FAIL("Wrong value");
+#endif
 }
 
 
index 7411281b7255e51af4308754ee0b236db7de0a07..3cf8ec32c47fbb4e561c52c3990e48357b451592 100644 (file)
@@ -1,9 +1,9 @@
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_FORCE_PLATFORM.exe -DENABLE_ALT_PLATFORMS=1
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_FORCE_PLATFORM-fail.exe
+// BUILD(macos):  $TASK_FOR_PID_ENABLE  $BUILD_DIR/env-DYLD_FORCE_PLATFORM.exe
+// BUILD(macos):  $TASK_FOR_PID_ENABLE  $BUILD_DIR/env-DYLD_FORCE_PLATFORM-fail.exe
 
-// BUILD_ONLY: MacOSX
-// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_FORCE_PLATFORM.exe -DENABLE_ALT_PLATFORMS=1
-// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_FORCE_PLATFORM-fail.exe
-// BUILD:  $TASK_FOR_PID_ENABLE  $BUILD_DIR/env-DYLD_FORCE_PLATFORM.exe
-// BUILD:  $TASK_FOR_PID_ENABLE  $BUILD_DIR/env-DYLD_FORCE_PLATFORM-fail.exe
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  DYLD_FORCE_PLATFORM=6 ./env-DYLD_FORCE_PLATFORM.exe
 // RUN:  DYLD_FORCE_PLATFORM=6 ./env-DYLD_FORCE_PLATFORM-fail.exe
diff --git a/testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/main.c b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/main.c
new file mode 100644 (file)
index 0000000..d367fee
--- /dev/null
@@ -0,0 +1,34 @@
+// BUILD(macos):  $CC myzlib.c -dynamiclib -o $BUILD_DIR/override/libz.1.dylib -install_name /usr/lib/libz.1.dylib -compatibility_version 1.0 -framework CoreFoundation -target x86_64-apple-macos10.16 -target-variant x86_64-apple-ios14.0-macabi
+// BUILD(macos):  $CC reexported-myzlib.c -dynamiclib -o $BUILD_DIR/re-export-override/reexported.dylib -compatibility_version 1.0 -framework CoreFoundation -install_name $RUN_DIR/re-export-override/reexported.dylib -target x86_64-apple-macos10.16 -target-variant x86_64-apple-ios14.0-macabi
+// BUILD(macos):  $CC reexporter.c -dynamiclib -o $BUILD_DIR/re-export-override/libz.1.dylib -install_name /usr/lib/libz.1.dylib -compatibility_version 1.0 -Wl,-reexport_library,$BUILD_DIR/re-export-override/reexported.dylib -Wl,-debug_variant -target x86_64-apple-macos10.16 -target-variant x86_64-apple-ios14.0-macabi
+// BUILD(macos):  $CC main.c  -o $BUILD_DIR/env-DYLD_LIBRARY_PATH-cache-iOSMac.exe -lz -target x86_64-apple-ios14.0-macabi
+// BUILD(macos):  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_LIBRARY_PATH-cache-iOSMac.exe
+
+// BUILD(ios,tvos,watchos,bridgeos):
+
+// RUN:  ./env-DYLD_LIBRARY_PATH-cache-iOSMac.exe
+// RUN:  DYLD_LIBRARY_PATH=$RUN_DIR/override/ ./env-DYLD_LIBRARY_PATH-cache-iOSMac.exe
+// RUN:  DYLD_LIBRARY_PATH=$RUN_DIR/re-export-override/ ./env-DYLD_LIBRARY_PATH-cache-iOSMac.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+#include <stdbool.h>
+#include <mach-o/dyld_priv.h>
+
+#include "test_support.h"
+
+// The test here is to override libz.1.dylib which is in the dyld cache with our own implementation.
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
+    bool expectMyDylib = (getenv("DYLD_LIBRARY_PATH") != NULL) && !_dyld_shared_cache_optimized();
+
+    bool usingMyDylib = (strcmp(zlibVersion(), "my") == 0);
+
+       if ( usingMyDylib == expectMyDylib )
+        PASS("Succes");
+    else
+        FAIL("Expected %s, got %s", expectMyDylib ? "my" : "os", expectMyDylib ? "os" : "my");
+}
+
diff --git a/testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/myzlib.c b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/myzlib.c
new file mode 100644 (file)
index 0000000..5bd0c77
--- /dev/null
@@ -0,0 +1,4 @@
+const char* zlibVersion()
+{
+    return "my";
+}
diff --git a/testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/reexported-myzlib.c b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/reexported-myzlib.c
new file mode 100644 (file)
index 0000000..5bd0c77
--- /dev/null
@@ -0,0 +1,4 @@
+const char* zlibVersion()
+{
+    return "my";
+}
diff --git a/testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/reexporter.c b/testing/test-cases/env-DYLD_LIBRARY_PATH-cache-iOSMac.dtest/reexporter.c
new file mode 100644 (file)
index 0000000..f5f664f
--- /dev/null
@@ -0,0 +1,4 @@
+
+int foo() {
+    return 0;
+}
index 3010022f33eb2224ee6192bdb8cfa62a4d14963c..a4112782c9b9351932be53bf5843b2ce50f8c8bd 100644 (file)
@@ -1,19 +1,19 @@
 
-// BUILD_ONLY: MacOSX
+// BUILD(macos):  $CC foo.c -dynamiclib -DRESULT=9  -current_version 9  -install_name $RUN_DIR/Foo.framework/Foo -o $BUILD_DIR/alt9/Foo.framework/Foo
+// BUILD(macos):  $CC foo.c -dynamiclib -DRESULT=10 -current_version 10 -install_name $RUN_DIR/Foo.framework/Foo -o $BUILD_DIR/Foo.framework/Foo
+// BUILD(macos):  $CC foo.c -dynamiclib -DRESULT=11 -current_version 11 -install_name $RUN_DIR/Foo.framework/Foo -o $BUILD_DIR/alt11/Foo.framework/Versions/A/Foo
+// BUILD(macos):  $CC foo.c -dynamiclib -DRESULT=12 -current_version 12 -install_name $RUN_DIR/Foo.framework/Foo -o $BUILD_DIR/alt12/Foo.framework/Foo
 
-// BUILD:  $CC foo.c -dynamiclib -DRESULT=9  -current_version 9  -install_name $RUN_DIR/Foo.framework/Foo -o $BUILD_DIR/alt9/Foo.framework/Foo
-// BUILD:  $CC foo.c -dynamiclib -DRESULT=10 -current_version 10 -install_name $RUN_DIR/Foo.framework/Foo -o $BUILD_DIR/Foo.framework/Foo
-// BUILD:  $CC foo.c -dynamiclib -DRESULT=11 -current_version 11 -install_name $RUN_DIR/Foo.framework/Foo -o $BUILD_DIR/alt11/Foo.framework/Versions/A/Foo
-// BUILD:  $CC foo.c -dynamiclib -DRESULT=12 -current_version 12 -install_name $RUN_DIR/Foo.framework/Foo -o $BUILD_DIR/alt12/Foo.framework/Foo
+// BUILD(macos):  $CC foo.c -dynamiclib -DRESULT=10 -current_version 10 -install_name $RUN_DIR/Foo2.framework/Foo2 -o $BUILD_DIR/Foo2.framework/Foo2
+// BUILD(macos):  $CC foo.c -dynamiclib -DRESULT=12 -current_version 12 -install_name $RUN_DIR/Foo2.framework/Foo2 -o $BUILD_DIR/alt12/Foo2.framework/Foo2
 
-// BUILD:  $CC foo.c -dynamiclib -DRESULT=10 -current_version 10 -install_name $RUN_DIR/Foo2.framework/Foo2 -o $BUILD_DIR/Foo2.framework/Foo2
-// BUILD:  $CC foo.c -dynamiclib -DRESULT=12 -current_version 12 -install_name $RUN_DIR/Foo2.framework/Foo2 -o $BUILD_DIR/alt12/Foo2.framework/Foo2
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_FRAMEWORK_PATH.exe $BUILD_DIR/Foo.framework/Foo
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_FRAMEWORK_PATH-missing.exe -Wl,-dyld_env,DYLD_VERSIONED_FRAMEWORK_PATH=@loader_path/alt12 $BUILD_DIR/Foo2.framework/Foo2
 
-// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_FRAMEWORK_PATH.exe $BUILD_DIR/Foo.framework/Foo
-// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_FRAMEWORK_PATH-missing.exe -Wl,-dyld_env,DYLD_VERSIONED_FRAMEWORK_PATH=@loader_path/alt12 $BUILD_DIR/Foo2.framework/Foo2
+// BUILD(macos):  $SYMLINK Versions/A/Foo  $BUILD_DIR/alt11/Foo.framework/Foo $DEPENDS_ON $BUILD_DIR/alt11/Foo.framework/Versions/A/Foo
+// BUILD(macos):  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_VERSIONED_FRAMEWORK_PATH.exe
 
-// BUILD:  $SYMLINK Versions/A/Foo  $BUILD_DIR/alt11/Foo.framework/Foo $DEPENDS_ON $BUILD_DIR/alt11/Foo.framework/Versions/A/Foo
-// BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_VERSIONED_FRAMEWORK_PATH.exe
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN: ./env-DYLD_VERSIONED_FRAMEWORK_PATH.exe 10
 // RUN: DYLD_VERSIONED_FRAMEWORK_PATH=$RUN_DIR/alt11 ./env-DYLD_VERSIONED_FRAMEWORK_PATH.exe 11 "alt11/Foo.framework/Versions/A/Foo"
diff --git a/testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.dtest/foo.c b/testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.dtest/foo.c
new file mode 100644 (file)
index 0000000..ee21326
--- /dev/null
@@ -0,0 +1,5 @@
+
+const char* zlibVersion()
+{
+       return RESULT;
+}
diff --git a/testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.dtest/main.c b/testing/test-cases/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.dtest/main.c
new file mode 100644 (file)
index 0000000..c03c0d8
--- /dev/null
@@ -0,0 +1,83 @@
+// This tests DYLD_VERSIONED_LIBRARY_PATH where the library we are doing a versioned check against is only in the shared
+// cache and not on disk.  This is the case when macOS moves to MRM.
+
+// libz.1.dylib was chosen as (on 10.15) it had a current version of 1.2.11 so we can write test dylibs with higer/lower versions
+
+// BUILD(macos):  $CC foo.c -dynamiclib -DRESULT="1.0.0"  -current_version 1.0.0  -install_name /usr/lib/libz.1.dylib -o $BUILD_DIR/alt-1.0.0/libz.1.dylib
+// BUILD(macos):  $CC foo.c -dynamiclib -DRESULT="2000.0.0"  -current_version 2000.0.0  -install_name /usr/lib/libz.1.dylib -o $BUILD_DIR/alt-2000.0.0/libz.1.dylib
+
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.exe -lz
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-1.0.0.exe -lz -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt-1.0.0
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-2000.0.0.exe -lz -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt-2000.0.0
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs-dlopen.exe -DUSE_DLOPEN
+
+// BUILD(macos):  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.exe
+// BUILD(macos):  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs-dlopen.exe
+
+// BUILD(ios,tvos,watchos,bridgeos):
+
+// Use the host when we have no ENV variable, or we check against the 1.0.0 test dylib which is lower than the host
+// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.exe "host"
+// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt-1.0.0 ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.exe "host"
+// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH-1.0.0.exe "host"
+// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs-dlopen.exe "host"
+// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt-1.0.0 ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs-dlopen.exe "host"
+
+// Use the 2000.0.0 dylib when its present
+// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt-2000.0.0 ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.exe "2000.0.0"
+// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt-2000.0.0 ./env-DYLD_VERSIONED_LIBRARY_PATH-1.0.0.exe "2000.0.0"
+// RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH-2000.0.0.exe "2000.0.0"
+// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt-2000.0.0 ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs-dlopen.exe "2000.0.0"
+
+// 2000.0.0 should also override the 1.0.0 version if we specify both (in either order)
+// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt-1.0.0:$RUN_DIR/alt-2000.0.0 ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.exe "2000.0.0"
+// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt-2000.0.0:$RUN_DIR/alt-1.0.0 ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs.exe "2000.0.0"
+// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt-1.0.0:$RUN_DIR/alt-2000.0.0 ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs-dlopen.exe "2000.0.0"
+// RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt-2000.0.0:$RUN_DIR/alt-1.0.0 ./env-DYLD_VERSIONED_LIBRARY_PATH-missing-dylibs-dlopen.exe "2000.0.0"
+
+#include <stdio.h>  // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <string.h>
+#include <stdlib.h> // for atoi()
+
+#include "test_support.h"
+
+#if USE_DLOPEN
+#include <dlfcn.h>
+#else
+extern const char* zlibVersion(); // returns "1.2.11" on the host dylib
+#endif
+
+int main(int argc, const char* argv[])
+{
+       const char* expectedResult = argv[1];
+
+#if USE_DLOPEN
+    void * handle = dlopen("/usr/lib/libz.1.dylib", RTLD_LAZY);
+    if (!handle) {
+        FAIL("dlopen(\"%s\") failed with error \"%s\"", "/usr/lib/libz.1.dylib", dlerror());
+    }
+    const char* (*zlibVersion)() = dlsym(handle, "zlibVersion");
+    if (!zlibVersion) {
+        FAIL("dlsym(\"zlibVersion\") failed with error \"%s\"", dlerror());
+    }
+#endif
+
+    const char* actualResult = zlibVersion();
+    if ( !strcmp(expectedResult, "host") ) {
+        // We don't know what the host version is, but we know we wanted it instead of the test dylibs
+        if ( !strcmp(actualResult, "1.0.0") ) {
+            FAIL("Using wrong dylib. zlibVersion() returned %s, expected host version", actualResult);
+        } else {
+            PASS("Success");
+        }
+    } else {
+        if ( strcmp(actualResult, expectedResult) ) {
+            FAIL("Using wrong dylib. zlibVersion() returned %s, expected %s", actualResult, expectedResult);
+        } else {
+            PASS("Success");
+        }
+    }
+       return 0;
+}
+
index 2554358b94351d77560026afc464f88768071ed5..bb800b07a0773ea88291324c7525c567d9541ec0 100644 (file)
@@ -1,31 +1,31 @@
-// BUILD_ONLY: MacOSX
+// BUILD(macos):  $CC foo.c -dynamiclib -DRESULT=9  -current_version 9  -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/alt9/libfoo.dylib
+// BUILD(macos):  $CC foo.c -dynamiclib -DRESULT=10 -current_version 10 -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD(macos):  $CC foo.c -dynamiclib -DRESULT=11 -current_version 11 -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/alt11/libfoo.dylib
+// BUILD(macos):  $CC foo.c -dynamiclib -DRESULT=12 -current_version 12 -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/alt12/libfoo.dylib
 
-// BUILD:  $CC foo.c -dynamiclib -DRESULT=9  -current_version 9  -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/alt9/libfoo.dylib
-// BUILD:  $CC foo.c -dynamiclib -DRESULT=10 -current_version 10 -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
-// BUILD:  $CC foo.c -dynamiclib -DRESULT=11 -current_version 11 -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/alt11/libfoo.dylib
-// BUILD:  $CC foo.c -dynamiclib -DRESULT=12 -current_version 12 -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/alt12/libfoo.dylib
+// BUILD(macos):  $CC foo.c -dynamiclib -DRESULT=10 -current_version 10 -install_name $RUN_DIR/libfoo2.dylib -o $BUILD_DIR/libfoo2.dylib
+// BUILD(macos):  $CC foo.c -dynamiclib -DRESULT=12 -current_version 12 -install_name $RUN_DIR/libfoo2.dylib -o $BUILD_DIR/alt12/libfoo2.dylib
 
-// BUILD:  $CC foo.c -dynamiclib -DRESULT=10 -current_version 10 -install_name $RUN_DIR/libfoo2.dylib -o $BUILD_DIR/libfoo2.dylib
-// BUILD:  $CC foo.c -dynamiclib -DRESULT=12 -current_version 12 -install_name $RUN_DIR/libfoo2.dylib -o $BUILD_DIR/alt12/libfoo2.dylib
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH.exe $BUILD_DIR/libfoo.dylib
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-10.exe $BUILD_DIR/libfoo.dylib
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-11.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-911.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt9 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-911b.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt9:@loader_path/alt11
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-911c.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@executable_path/alt9:@executable_path/alt11
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-1112.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-1112b.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11:@loader_path/alt12
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-1211.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-1211b.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12:@loader_path/alt11
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-missing.exe $BUILD_DIR/libfoo2.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-dlopen.exe -DUSE_DLOPEN -DRUN_DIR="$RUN_DIR" -DDYLIB_NAME="libfoo.dylib"
+// BUILD(macos):  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dlopen.exe -DUSE_DLOPEN -DRUN_DIR="$RUN_DIR" -DDYLIB_NAME="libfoo2.dylib"
 
-// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH.exe $BUILD_DIR/libfoo.dylib
-// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-10.exe $BUILD_DIR/libfoo.dylib
-// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-11.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11
-// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-911.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt9 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11
-// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-911b.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt9:@loader_path/alt11
-// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-911c.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@executable_path/alt9:@executable_path/alt11
-// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-1112.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12
-// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-1112b.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11:@loader_path/alt12
-// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-1211.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11
-// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-1211b.exe $BUILD_DIR/libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12:@loader_path/alt11
-// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-missing.exe $BUILD_DIR/libfoo2.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12
-// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-dlopen.exe -DUSE_DLOPEN -DRUN_DIR="$RUN_DIR" -DDYLIB_NAME="libfoo.dylib"
-// BUILD:  $CC main.c            -o $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-missing-dlopen.exe -DUSE_DLOPEN -DRUN_DIR="$RUN_DIR" -DDYLIB_NAME="libfoo2.dylib"
+// BUILD(macos):  $SKIP_INSTALL $BUILD_DIR/libfoo2.dylib
 
-// BUILD:  $SKIP_INSTALL $BUILD_DIR/libfoo2.dylib
+// BUILD(macos):  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH.exe
+// BUILD(macos):  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-11.exe
 
-// BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH.exe
-// BUILD:  $DYLD_ENV_VARS_ENABLE $BUILD_DIR/env-DYLD_VERSIONED_LIBRARY_PATH-11.exe
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN: ./env-DYLD_VERSIONED_LIBRARY_PATH.exe 10
 // RUN: DYLD_VERSIONED_LIBRARY_PATH=$RUN_DIR/alt11 ./env-DYLD_VERSIONED_LIBRARY_PATH.exe 11
index 87670c438b74b7ee68de86029f491387811c65e8..c07fe631007b3dcc055d7e9c44ca505feff587ce 100644 (file)
@@ -1,7 +1,7 @@
-// BUILD_ONLY: MacOSX
+// BUILD(macos):  $CC foo.s -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib
+// BUILD(macos):  $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/flat-namespace.exe -flat_namespace
 
-// BUILD:  $CC foo.s -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib
-// BUILD:  $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/flat-namespace.exe -flat_namespace
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:    ./flat-namespace.exe
 
index 306bd7e7ee5c7125066f02d057ee01856e61c0b5..878709156f9d896dd44f2f35e0c8c1185bfae82c 100644 (file)
@@ -1,7 +1,7 @@
-// BUILD_ONLY: MacOSX
+// BUILD(macos):  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib
+// BUILD(macos):  $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/flat-namespace.exe -flat_namespace
 
-// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib
-// BUILD:  $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/flat-namespace.exe -flat_namespace
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:    ./flat-namespace.exe
 
diff --git a/testing/test-cases/kernel-auxkc-fixups.dtest/bar.c b/testing/test-cases/kernel-auxkc-fixups.dtest/bar.c
new file mode 100644 (file)
index 0000000..c5635c0
--- /dev/null
@@ -0,0 +1,25 @@
+
+#include "../kernel-test-runner.h"
+
+extern int kernelExport();
+__typeof(&kernelExport) kernelExportPtr = &kernelExport;
+
+int bar() {
+       return kernelExportPtr() + 2;
+}
+
+extern int kernelExportDirect();
+
+// Test direct pointer fixups to the kernel.  On x86_64 these would be emitted as just
+// a branch relocation so we needed to synthesize a stub
+__attribute__((constructor))
+int testDirectToKernel(const TestRunnerFunctions* funcs) {
+    LOG("testDirectToKernel(): start");
+    // The kernel returned 42
+    int v = kernelExportDirect();
+    if ( v != 42 ) {
+        FAIL("kernelExportDirect() returned %d vs expected 42", v);
+    }
+    LOG("testDirectToKernel(): end");
+    return 0;
+}
diff --git a/testing/test-cases/kernel-auxkc-fixups.dtest/extensions/bar.kext/Info.plist b/testing/test-cases/kernel-auxkc-fixups.dtest/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..e470d05
--- /dev/null
@@ -0,0 +1,23 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.kernel.export</key>
+               <string>1.0</string>
+       </dict>
+       <key>CFBundleVersion</key>
+       <string>1.0.0</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0.0</string>
+</dict>
+</plist>
diff --git a/testing/test-cases/kernel-auxkc-fixups.dtest/extensions/foo.kext/Info.plist b/testing/test-cases/kernel-auxkc-fixups.dtest/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..d7245d1
--- /dev/null
@@ -0,0 +1,23 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>apfs</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.bar</key>
+               <string>1.0</string>
+       </dict>
+       <key>CFBundleVersion</key>
+       <string>1.0.0</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0.0</string>
+</dict>
+</plist>
diff --git a/testing/test-cases/kernel-auxkc-fixups.dtest/extensions/kernel-export.kext/Info.plist b/testing/test-cases/kernel-auxkc-fixups.dtest/extensions/kernel-export.kext/Info.plist
new file mode 100644 (file)
index 0000000..3376dea
--- /dev/null
@@ -0,0 +1,18 @@
+<?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>CFBundleExecutable</key>
+       <string>kernel-export</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.kernel.export</string>
+       <key>CFBundleName</key>
+       <string>kernel-export</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>CFBundleVersion</key>
+       <string>1.0.0</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0.0</string>
+</dict>
+</plist>
diff --git a/testing/test-cases/kernel-auxkc-fixups.dtest/foo.c b/testing/test-cases/kernel-auxkc-fixups.dtest/foo.c
new file mode 100644 (file)
index 0000000..3e5b437
--- /dev/null
@@ -0,0 +1,38 @@
+
+#include "../kernel-test-runner.h"
+
+extern int bar();
+__typeof(&bar) barPtr = &bar;
+
+int foo() {
+       return barPtr() + 4;
+}
+
+__attribute__((constructor))
+int test(const TestRunnerFunctions* funcs) {
+    LOG("test(): start");
+    // kernel, bar, and foo each added 1, 2, 4, so we need to return 7 to know this worked
+       int v = foo();
+    if ( v != 7 ) {
+        FAIL("foo() returned %d vs expected 7", v);
+    }
+    LOG("test(): end");
+    return 0;
+}
+
+int fooDirect() {
+    return bar() + 4;
+}
+
+// Test direct pointer fixups, ie, not via a GOT
+__attribute__((constructor))
+int testDirect(const TestRunnerFunctions* funcs) {
+    LOG("testDirect(): start");
+    // kernel, bar, and foo each added 1, 2, 4, so we need to return 7 to know this worked
+    int v = fooDirect();
+    if ( v != 7 ) {
+        FAIL("fooDirect() returned %d vs expected 7", v);
+    }
+    LOG("testDirect(): end");
+    return 0;
+}
diff --git a/testing/test-cases/kernel-auxkc-fixups.dtest/kernel-export.c b/testing/test-cases/kernel-auxkc-fixups.dtest/kernel-export.c
new file mode 100644 (file)
index 0000000..3313ca8
--- /dev/null
@@ -0,0 +1,10 @@
+
+#include "../kernel-test-runner.h"
+
+int kernelExport() {
+       return 1;
+}
+
+int kernelExportDirect() {
+    return 42;
+}
diff --git a/testing/test-cases/kernel-auxkc-fixups.dtest/main.c b/testing/test-cases/kernel-auxkc-fixups.dtest/main.c
new file mode 100644 (file)
index 0000000..d942c19
--- /dev/null
@@ -0,0 +1,90 @@
+
+// BOOT_ARGS: amfi=3 cs_enforcement_disable=1
+
+// Create the base kernel collection
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CP extensions/kernel-export.kext/Info.plist $BUILD_DIR/extensions/kernel-export-kext/Info.plist
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CC main.c -o $BUILD_DIR/kernel-auxkc-fixups.exe -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie -Wl,-pagezero_size,0x0 -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-stack-protector -fno-builtin -ffreestanding -Wl,-segprot,__HIB,rx,rx -Wl,-image_base,0x8000 -Wl,-segaddr,__HIB,0x4000 -fno-ptrauth-function-pointer-type-discrimination
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CC kernel-export.c -o $BUILD_DIR/extensions/kernel-export-kext/kernel-export -Wl,-kext -Wl,-kext_objects_dir,$BUILD_DIR/KextObjects -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-ptrauth-function-pointer-type-discrimination
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $APP_CACHE_UTIL -create-kernel-collection $BUILD_DIR/kernel.kc -kernel $BUILD_DIR/kernel-auxkc-fixups.exe -extensions $BUILD_DIR/extensions -bundle-id com.apple.kernel.export $DEPENDS_ON $BUILD_DIR/extensions/kernel-export-kext/Info.plist $DEPENDS_ON $BUILD_DIR/extensions/kernel-export-kext/kernel-export
+
+// Create the aux kernel collection
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CP extensions/foo.kext/Info.plist $BUILD_DIR/extensions/foo-kext/Info.plist
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CP extensions/bar.kext/Info.plist $BUILD_DIR/extensions/bar-kext/Info.plist
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CC foo.c -o $BUILD_DIR/extensions/foo-kext/foo -Wl,-kext -Wl,-kext_objects_dir,$BUILD_DIR/KextObjects -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-ptrauth-function-pointer-type-discrimination
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CC bar.c -o $BUILD_DIR/extensions/bar-kext/bar -Wl,-kext -Wl,-kext_objects_dir,$BUILD_DIR/KextObjects -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-ptrauth-function-pointer-type-discrimination
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $APP_CACHE_UTIL -create-aux-kernel-collection $BUILD_DIR/aux.kc -kernel-collection $BUILD_DIR/kernel.kc -extensions $BUILD_DIR/extensions  -bundle-id com.apple.foo $DEPENDS_ON $BUILD_DIR/extensions/foo-kext/Info.plist $DEPENDS_ON $BUILD_DIR/extensions/bar-kext/Info.plist $DEPENDS_ON $BUILD_DIR/extensions/foo-kext/foo $DEPENDS_ON $BUILD_DIR/extensions/bar-kext/bar
+
+// BUILD(watchos):
+
+// RUN_STATIC:    $RUN_STATIC $RUN_DIR/kernel.kc - - $RUN_DIR/aux.kc
+
+#include "../kernel-test-runner.h"
+#include "../kernel-fixups.h"
+#include "../kernel-classic-relocs.h"
+#include "../kernel-helpers.h"
+
+#define printf(...) funcs->printf(__VA_ARGS__)
+
+int x = 1;
+int *g = &x;
+
+#if __x86_64__
+__attribute__((section(("__HIB, __text"))))
+#else
+__attribute__((section(("__TEXT_EXEC, __text"))))
+#endif
+int _start(const TestRunnerFunctions* funcs)
+{
+    setFuncs(funcs);
+
+    const void* slideBasePointers[4];
+    slideBasePointers[0] = funcs->basePointers[0];
+    slideBasePointers[1] = funcs->basePointers[1];
+    slideBasePointers[2] = funcs->basePointers[2];
+    slideBasePointers[3] = funcs->basePointers[3];
+    int slideReturnCode = slide(funcs->mhs[0], slideBasePointers, funcs->printf);
+    if ( slideReturnCode != 0 ) {
+        FAIL("mhs[0] slide = %d\n", slideReturnCode);
+        return 0;
+    }
+
+    int slideClassicReturnCode = slideClassic(funcs->mhs[0], funcs->printf);
+    if ( slideClassicReturnCode != 0 ) {
+        FAIL("mhs[0] slide classic = %d\n", slideClassicReturnCode);
+        return 0;
+    }
+
+    if ( g[0] != x ) {
+       FAIL("g[0] != x, %d != %d\n", g[0], x);
+       return 0;
+    }
+
+    // First slide the auxKC using the top level fixups.  These handle the branch GOTs
+    slideReturnCode = slide(funcs->mhs[3], slideBasePointers, funcs->printf);
+    if ( slideReturnCode != 0 ) {
+        FAIL("mhs[3] slide = %d\n", slideReturnCode);
+        return 0;
+    }
+
+#if __x86_64__
+    // On x86 only, slide the auxKC individually
+    // Then slide pageable using the fixups attached to the kexts own mach headers
+    slideReturnCode = slideKextsInsideKernelCollection(funcs->mhs[3], slideBasePointers, funcs->printf, funcs);
+    if ( slideReturnCode != 0 ) {
+        FAIL("mhs[3] slide = %d\n", slideReturnCode);
+        return 0;
+    }
+#endif
+
+    // If we have any mod init funcs, then lets run them now
+    int runModInitFuncs = runAllModInitFunctionsForAppCache(funcs->mhs[3], funcs->printf, funcs);
+    if ( runModInitFuncs != 0 ) {
+        FAIL("runModInitFuncs = %d\n", runModInitFuncs);
+        return 0;
+    }
+
+    PASS("Success");
+    return 0;
+}
+
+
diff --git a/testing/test-cases/kernel-classic-relocs.h b/testing/test-cases/kernel-classic-relocs.h
new file mode 100644 (file)
index 0000000..639ead7
--- /dev/null
@@ -0,0 +1,157 @@
+
+#include <mach-o/reloc.h>
+#include <mach-o/loader.h>
+
+typedef int (*FixupsLogFunc)(const char*, ...);
+static const int LogFixupsClassic = 0;
+
+// We may not have strcmp, so make our own
+#if __x86_64__
+__attribute__((section(("__HIB, __text"))))
+#else
+__attribute__((section(("__TEXT_EXEC, __text"))))
+#endif
+static int areEqualClassic(const char* a, const char* b) {
+       while (*a && *b) {
+               if (*a != *b)
+                       return 0;
+        ++a;
+        ++b;
+       }
+       return *a == *b;
+}
+
+// Temporary until we have <rdar://problem/57025372>
+#if __x86_64__
+__attribute__((section(("__HIB, __text"))))
+#else
+__attribute__((section(("__TEXT_EXEC, __text"))))
+#endif
+int slideClassic(const struct mach_header* mh, FixupsLogFunc logFunc) {
+
+#if !__x86_64__
+    return 0;
+#endif
+
+       // First find the slide and dysymtab fixups load command
+    uint64_t textVMAddr                             = 0;
+    uint64_t firstWritableVMAddr                    = ~0ULL;
+       const struct dysymtab_command* dynSymbolTable   = 0;
+       uint64_t linkeditVMAddr                             = 0;
+       uint64_t linkeditFileOffset                     = 0;
+
+    if (LogFixupsClassic) {
+        logFunc("[LOG] kernel-classic-relocs: mh %p\n", mh);
+    }
+
+    if (LogFixupsClassic) {
+        logFunc("[LOG] kernel-classic-relocs: parsing load commands\n");
+    }
+
+       const struct load_command* startCmds = 0;
+    if ( mh->magic == MH_MAGIC_64 )
+        startCmds = (struct load_command*)((char *)mh + sizeof(struct mach_header_64));
+    else if ( mh->magic == MH_MAGIC )
+        startCmds = (struct load_command*)((char *)mh + sizeof(struct mach_header));
+    else {
+        const uint32_t* h = (uint32_t*)mh;
+        //diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]);
+        return 1;  // not a mach-o file
+    }
+    const struct load_command* const cmdsEnd = (struct load_command*)((char*)startCmds + mh->sizeofcmds);
+    const struct load_command* cmd = startCmds;
+    for (uint32_t i = 0; i < mh->ncmds; ++i) {
+        if (LogFixupsClassic) {
+            logFunc("[LOG] kernel-classic-relocs: parsing load command %d with cmd=0x%x\n", i, cmd->cmd);
+        }
+        const struct load_command* nextCmd = (struct load_command*)((char *)cmd + cmd->cmdsize);
+        if ( cmd->cmdsize < 8 ) {
+            //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, this->ncmds, cmd, this, cmd->cmdsize);
+            return 1;
+        }
+        if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
+            //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, this->ncmds, cmd, this, cmd->cmdsize, cmdsEnd);
+            return 1;
+        }
+        if ( cmd->cmd == LC_DYSYMTAB ) {
+               dynSymbolTable = (const struct dysymtab_command*)cmd;
+        } else if ( cmd->cmd == LC_SEGMENT_64 ) {
+               const struct segment_command_64* seg = (const struct segment_command_64*)cmd;
+            if ( areEqualClassic(seg->segname, "__TEXT") ) {
+                textVMAddr = seg->vmaddr;
+            } else if ( areEqualClassic(seg->segname, "__LINKEDIT") ) {
+                       linkeditVMAddr = seg->vmaddr;
+                       linkeditFileOffset = seg->fileoff;
+               }
+            if ( (seg->initprot & VM_PROT_WRITE) && (firstWritableVMAddr == ~0ULL) ) {
+                firstWritableVMAddr = seg->vmaddr;
+                if (LogFixupsClassic) {
+                    logFunc("[LOG] kernel-classic-relocs: first writable segment %s = 0x%llx\n", seg->segname, seg->vmaddr);
+                }
+            }
+        }
+        cmd = nextCmd;
+    }
+
+    uintptr_t slide = (uintptr_t)mh - textVMAddr;
+
+    if (LogFixupsClassic) {
+        logFunc("[LOG] kernel-classic-relocs: slide 0x%llx\n", slide);
+    }
+
+    if ( dynSymbolTable == 0 )
+       return 0;
+
+    if ( dynSymbolTable->nlocrel == 0 )
+        return 0;
+
+    if (LogFixupsClassic) {
+        logFunc("[LOG] kernel-classic-relocs: found dynamic symbol table %p\n", dynSymbolTable);
+        logFunc("[LOG] kernel-classic-relocs: found linkeditVMAddr %p\n", (void*)linkeditVMAddr);
+        logFunc("[LOG] kernel-classic-relocs: found linkeditFileOffset %p\n", (void*)linkeditFileOffset);
+    }
+
+    // Now we have the dynamic symbol table, walk it to apply all the rebases
+    uint32_t offsetInLinkedit   = dynSymbolTable->locreloff - linkeditFileOffset;
+    uintptr_t linkeditStartAddr = linkeditVMAddr + slide;
+    if (LogFixupsClassic) {
+        logFunc("[LOG] kernel-classic-relocs: offsetInLinkedit 0x%x\n", offsetInLinkedit);
+        logFunc("[LOG] kernel-classic-relocs: linkeditStartAddr %p\n", (void*)linkeditStartAddr);
+    }
+
+    const uint64_t                  relocsStartAddress = firstWritableVMAddr;
+    const struct relocation_info* const    relocsStart = (const struct relocation_info*)(linkeditStartAddr + offsetInLinkedit);
+    const struct relocation_info* const    relocsEnd   = &relocsStart[dynSymbolTable->nlocrel];
+    for (const struct relocation_info* reloc = relocsStart; reloc < relocsEnd; ++reloc) {
+        if ( reloc->r_length == 2 ) {
+            uint32_t* fixupLoc = (uint32_t*)(relocsStartAddress + reloc->r_address + slide);
+            uint32_t slidValue = *fixupLoc + slide;
+            if (LogFixupsClassic) {
+                logFunc("[LOG] kernel-classic-relocs: fixupLoc %p = 0x%x + 0x%x + 0x%x\n", fixupLoc, relocsStartAddress, reloc->r_address, slide);
+                logFunc("[LOG] kernel-classic-relocs: slidValue *%p = 0x%x\n", fixupLoc, slidValue);
+            }
+            *fixupLoc = slidValue;
+            continue;
+        }
+        if ( reloc->r_length == 3 ) {
+            uint64_t* fixupLoc = (uint64_t*)(relocsStartAddress + reloc->r_address + slide);
+            uint64_t slidValue = *fixupLoc + slide;
+            if (LogFixupsClassic) {
+                logFunc("[LOG] kernel-classic-relocs: fixupLoc %p = 0x%x + 0x%x + 0x%x\n", fixupLoc, relocsStartAddress, reloc->r_address, slide);
+                logFunc("[LOG] kernel-classic-relocs: slidValue *%p = 0x%llx\n", fixupLoc, slidValue);
+            }
+            *fixupLoc = slidValue;
+            continue;
+        }
+        logFunc("[LOG] kernel-fixups: unknown reloc size\n", reloc->r_length);
+        return 1;
+    }
+
+    if (LogFixupsClassic) {
+        logFunc("[LOG] kernel-classic-relocs: Done\n");
+    }
+
+    return 0;
+}
+
+
diff --git a/testing/test-cases/kernel-fixups-x86_64.dtest/main.c b/testing/test-cases/kernel-fixups-x86_64.dtest/main.c
new file mode 100644 (file)
index 0000000..f5bc351
--- /dev/null
@@ -0,0 +1,74 @@
+
+// BOOT_ARGS: amfi=3 cs_enforcement_disable=1
+
+// BUILD(macos|x86_64):  $CC main.c -o $BUILD_DIR/kernel-fixups.exe -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie -Wl,-pagezero_size,0x0 -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-stack-protector -fno-builtin -ffreestanding -Wl,-segprot,__HIB,rx,rx -Wl,-image_base,0x8000 -Wl,-segaddr,__HIB,0x4000
+// BUILD(macos|x86_64):  $APP_CACHE_UTIL -create-kernel-collection $BUILD_DIR/kernel.kc -kernel $BUILD_DIR/kernel-fixups.exe
+
+// BUILD(ios,tvos,watchos,bridgeos):
+
+// RUN_STATIC:    $RUN_STATIC ./kernel.kc
+
+// This tests that unaligned fixups work in x86_64
+
+#include "../kernel-test-runner.h"
+#include "../kernel-fixups.h"
+#include "../kernel-classic-relocs.h"
+
+#define printf(...) funcs->printf(__VA_ARGS__)
+
+int x = 1;
+
+struct __attribute__((packed)) __attribute__((aligned((4096)))) PackedS {
+    int     i0; // aligned to 8
+    int*    p0; // aligned to 4
+    int     i1; // aligned to 4
+    int*    p1; // aligned to 8
+    char    i2; // aligned to 8
+    int*    p2; // aligned to 1
+};
+struct PackedS ps = { 0, &x, 0, &x, 0, &x };
+
+__attribute__((section(("__HIB, __text"))))
+int _start(const TestRunnerFunctions* funcs)
+{
+    setFuncs(funcs);
+
+    const void* slideBasePointers[4];
+    slideBasePointers[0] = funcs->basePointers[0];
+    slideBasePointers[1] = funcs->basePointers[1];
+    slideBasePointers[2] = funcs->basePointers[2];
+    slideBasePointers[3] = funcs->basePointers[3];
+    int slideReturnCode = slide(funcs->mhs[0], slideBasePointers, funcs->printf);
+    if ( slideReturnCode != 0 ) {
+        FAIL("slide = %d\n", slideReturnCode);
+        return 0;
+    }
+
+    int slideClassicReturnCode = slideClassic(funcs->mhs[0], funcs->printf);
+    if ( slideClassicReturnCode != 0 ) {
+        FAIL("mhs[0] slide classic = %d\n", slideClassicReturnCode);
+        return 0;
+    }
+
+    LOG("Done sliding");
+
+    if ( ps.p0[0] != x ) {
+       FAIL("ps.p1[0] != x, %d != %d\n", ps.p0[0], x);
+       return 0;
+    }
+
+    if ( ps.p1[0] != x ) {
+        FAIL("ps.p1[0] != x, %d != %d\n", ps.p1[0], x);
+        return 0;
+    }
+
+    if ( ps.p2[0] != x ) {
+        FAIL("ps.p2[0] != x, %d != %d\n", ps.p2[0], x);
+        return 0;
+    }
+
+    PASS("Success");
+    return 0;
+}
+
+
diff --git a/testing/test-cases/kernel-fixups.dtest/main.c b/testing/test-cases/kernel-fixups.dtest/main.c
new file mode 100644 (file)
index 0000000..c153e4d
--- /dev/null
@@ -0,0 +1,58 @@
+
+// BOOT_ARGS: amfi=3 cs_enforcement_disable=1
+
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CC main.c -o $BUILD_DIR/kernel-fixups.exe -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie -Wl,-pagezero_size,0x0 -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-stack-protector -fno-builtin -ffreestanding -Wl,-segprot,__HIB,rx,rx -Wl,-image_base,0x8000 -Wl,-segaddr,__HIB,0x4000  -fno-ptrauth-function-pointer-type-discrimination
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $APP_CACHE_UTIL -create-kernel-collection $BUILD_DIR/kernel.kc -kernel $BUILD_DIR/kernel-fixups.exe
+
+// BUILD(watchos):
+
+
+// RUN_STATIC:    $RUN_STATIC ./kernel.kc
+
+#include "../kernel-test-runner.h"
+#include "../kernel-fixups.h"
+#include "../kernel-classic-relocs.h"
+
+#define printf(...) funcs->printf(__VA_ARGS__)
+
+int x = 1;
+int *g = &x;
+
+#if __x86_64__
+__attribute__((section(("__HIB, __text"))))
+#else
+__attribute__((section(("__TEXT_EXEC, __text"))))
+#endif
+int _start(const TestRunnerFunctions* funcs)
+{
+    setFuncs(funcs);
+
+    const void* slideBasePointers[4];
+    slideBasePointers[0] = funcs->basePointers[0];
+    slideBasePointers[1] = funcs->basePointers[1];
+    slideBasePointers[2] = funcs->basePointers[2];
+    slideBasePointers[3] = funcs->basePointers[3];
+    int slideReturnCode = slide(funcs->mhs[0], slideBasePointers, funcs->printf);
+    if ( slideReturnCode != 0 ) {
+        FAIL("slide = %d\n", slideReturnCode);
+        return 0;
+    }
+
+    int slideClassicReturnCode = slideClassic(funcs->mhs[0], funcs->printf);
+    if ( slideClassicReturnCode != 0 ) {
+        FAIL("mhs[0] slide classic = %d\n", slideClassicReturnCode);
+        return 0;
+    }
+
+    LOG("Done sliding");
+
+    if ( g[0] != x ) {
+       FAIL("g[0] != x, %d != %d\n", g[0], x);
+       return 0;
+    }
+
+    PASS("Success");
+    return 0;
+}
+
+
diff --git a/testing/test-cases/kernel-fixups.h b/testing/test-cases/kernel-fixups.h
new file mode 100644 (file)
index 0000000..6cbf483
--- /dev/null
@@ -0,0 +1,288 @@
+
+#ifndef KERNEL_FIXUPS_H
+#define KERNEL_FIXUPS_H
+
+#include <mach-o/fixup-chains.h>
+#include <mach-o/loader.h>
+
+typedef int (*FixupsLogFunc)(const char*, ...);
+static const int LogFixups = 0;
+
+// We may not have strcmp, so make our own
+#if __x86_64__
+__attribute__((section(("__HIB, __text"))))
+#else
+__attribute__((section(("__TEXT_EXEC, __text"))))
+#endif
+static int areEqual(const char* a, const char* b) {
+       while (*a && *b) {
+               if (*a != *b)
+                       return 0;
+        ++a;
+        ++b;
+       }
+       return *a == *b;
+}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-declarations"
+union ChainedFixupPointerOnDisk
+{
+    uint64_t                                                                                   raw64;
+    struct dyld_chained_ptr_64_kernel_cache_rebase             fixup64;
+};
+#pragma clang diagnostic pop
+
+// Temporary until we have <rdar://problem/57025372>
+#if __x86_64__
+__attribute__((section(("__HIB, __text"))))
+#else
+__attribute__((section(("__TEXT_EXEC, __text"))))
+#endif
+static uint64_t signPointer(struct dyld_chained_ptr_64_kernel_cache_rebase pointer,
+                                                       void* loc,
+                                                       uint64_t target)
+{
+#if __has_feature(ptrauth_calls)
+    uint64_t discriminator = pointer.diversity;
+    if ( pointer.addrDiv ) {
+        if ( discriminator != 0 ) {
+            discriminator = __builtin_ptrauth_blend_discriminator(loc, discriminator);
+        } else {
+            discriminator = (uint64_t)(uintptr_t)loc;
+        }
+    }
+    switch ( pointer.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;
+}
+
+// Temporary until we have <rdar://problem/57025372>
+#if __x86_64__
+__attribute__((section(("__HIB, __text"))))
+#else
+__attribute__((section(("__TEXT_EXEC, __text"))))
+#endif
+void fixupValue(union ChainedFixupPointerOnDisk* fixupLoc,
+                               const struct dyld_chained_starts_in_segment* segInfo,
+                               uintptr_t slide,
+                               const void* basePointers[4],
+                               int* stop,
+                               FixupsLogFunc logFunc) {
+       if (LogFixups) {
+        logFunc("[LOG] kernel-fixups: fixupValue %p\n", fixupLoc);
+    }
+    switch (segInfo->pointer_format) {
+#if __LP64__
+        case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
+        case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE: {
+               const void* baseAddress = basePointers[fixupLoc->fixup64.cacheLevel];
+               if ( baseAddress == 0 ) {
+                       logFunc("Invalid cache level: %d\n", fixupLoc->fixup64.cacheLevel);
+                       *stop = 1;
+                       return;
+               }
+               uintptr_t slidValue = (uintptr_t)baseAddress + fixupLoc->fixup64.target;
+               if (LogFixups) {
+                       logFunc("[LOG] kernel-fixups: slidValue %p = %p + %p\n", (void*)slidValue, (void*)baseAddress, (void*)fixupLoc->fixup64.target);
+                   }
+#if __has_feature(ptrauth_calls)
+               if ( fixupLoc->fixup64.isAuth ) {
+                       slidValue = signPointer(fixupLoc->fixup64, fixupLoc, slidValue);
+               }
+#else
+               if ( fixupLoc->fixup64.isAuth ) {
+                       logFunc("Unexpected authenticated fixup\n");
+                       *stop = 1;
+                       return;
+               }
+#endif // __has_feature(ptrauth_calls)
+            fixupLoc->raw64 = slidValue;
+            break;
+        }
+#endif // __LP64__
+        default:
+            logFunc("unsupported pointer chain format: 0x%04X", segInfo->pointer_format);
+            *stop = 1;
+            break;
+    }
+}
+
+// Temporary until we have <rdar://problem/57025372>
+#if __x86_64__
+__attribute__((section(("__HIB, __text"))))
+#else
+__attribute__((section(("__TEXT_EXEC, __text"))))
+#endif
+int walkChain(const struct mach_header* mh,
+                         const struct dyld_chained_starts_in_segment* segInfo,
+                         uint32_t pageIndex,
+                         uint16_t offsetInPage,
+                         uintptr_t slide,
+                         const void* basePointers[4],
+                         FixupsLogFunc logFunc)
+{
+       if (LogFixups) {
+        logFunc("[LOG] kernel-fixups: walkChain page[%d]\n", pageIndex);
+    }
+    int                        stop = 0;
+    uint8_t*                   pageContentStart = (uint8_t*)mh + segInfo->segment_offset + (pageIndex * segInfo->page_size);
+    union ChainedFixupPointerOnDisk* chain = (union ChainedFixupPointerOnDisk*)(pageContentStart+offsetInPage);
+    int                       chainEnd = 0;
+    while (!stop && !chainEnd) {
+        // copy chain content, in case handler modifies location to final value
+        union ChainedFixupPointerOnDisk chainContent = *chain;
+        fixupValue(chain, segInfo, slide, basePointers, &stop, logFunc);
+        if ( !stop ) {
+            switch (segInfo->pointer_format) {
+#if __LP64__
+                case DYLD_CHAINED_PTR_64_KERNEL_CACHE:
+                    if ( chainContent.fixup64.next == 0 )
+                        chainEnd = 1;
+                    else
+                        chain = (union ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.fixup64.next*4);
+                    break;
+                case DYLD_CHAINED_PTR_X86_64_KERNEL_CACHE:
+                    if ( chainContent.fixup64.next == 0 )
+                        chainEnd = 1;
+                    else
+                        chain = (union ChainedFixupPointerOnDisk*)((uint8_t*)chain + chainContent.fixup64.next);
+                    break;
+#endif // __LP64__
+                default:
+                    logFunc("unknown pointer format 0x%04X", segInfo->pointer_format);
+                    stop = 1;
+            }
+        }
+    }
+    return stop;
+}
+
+// Temporary until we have <rdar://problem/57025372>
+#if __x86_64__
+__attribute__((section(("__HIB, __text"))))
+#else
+__attribute__((section(("__TEXT_EXEC, __text"))))
+#endif
+int slide(const struct mach_header* mh, const void* basePointers[4], FixupsLogFunc logFunc) {
+       // First find the slide and chained fixups load command
+    uint64_t textVMAddr     = 0;
+       const struct linkedit_data_command* chainedFixups = 0;
+       uint64_t linkeditVMAddr         = 0;
+       uint64_t linkeditFileOffset = 0;
+
+    if (LogFixups) {
+        logFunc("[LOG] kernel-fixups: mh %p\n", mh);
+    }
+
+    if (LogFixups) {
+        logFunc("[LOG] kernel-fixups: parsing load commands\n");
+    }
+
+       const struct load_command* startCmds = 0;
+    if ( mh->magic == MH_MAGIC_64 )
+        startCmds = (struct load_command*)((char *)mh + sizeof(struct mach_header_64));
+    else if ( mh->magic == MH_MAGIC )
+        startCmds = (struct load_command*)((char *)mh + sizeof(struct mach_header));
+    else {
+        const uint32_t* h = (uint32_t*)mh;
+        logFunc("[LOG] kernel-fixups: file does not start with MH_MAGIC[_64] 0x%08X 0x%08X\n", h[0], h [1]);
+        //diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]);
+        return 1;  // not a mach-o file
+    }
+    const struct load_command* const cmdsEnd = (struct load_command*)((char*)startCmds + mh->sizeofcmds);
+    const struct load_command* cmd = startCmds;
+    for (uint32_t i = 0; i < mh->ncmds; ++i) {
+        if (LogFixups) {
+            logFunc("[LOG] kernel-fixups: parsing load command %d with cmd=0x%x\n", i, cmd->cmd);
+        }
+        const struct load_command* nextCmd = (struct load_command*)((char *)cmd + cmd->cmdsize);
+        if ( cmd->cmdsize < 8 ) {
+            //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, this->ncmds, cmd, this, cmd->cmdsize);
+            return 1;
+        }
+        if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
+            //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, this->ncmds, cmd, this, cmd->cmdsize, cmdsEnd);
+            return 1;
+        }
+        if ( cmd->cmd == LC_DYLD_CHAINED_FIXUPS ) {
+               chainedFixups = (const struct linkedit_data_command*)cmd;
+        } else if ( cmd->cmd == LC_SEGMENT_64 ) {
+               const struct segment_command_64* seg = (const struct segment_command_64*)cmd;
+            if ( areEqual(seg->segname, "__TEXT") ) {
+                textVMAddr = seg->vmaddr;
+            } else if ( areEqual(seg->segname, "__LINKEDIT") ) {
+                       linkeditVMAddr = seg->vmaddr;
+                       linkeditFileOffset = seg->fileoff;
+               }
+        }
+        cmd = nextCmd;
+    }
+
+    uintptr_t slide = (uintptr_t)mh - textVMAddr;
+
+    if (LogFixups) {
+        logFunc("[LOG] kernel-fixups: slide 0x%llx\n", slide);
+    }
+
+    if ( chainedFixups == 0 )
+       return 0;
+
+    if (LogFixups) {
+        logFunc("[LOG] kernel-fixups: found chained fixups %p\n", chainedFixups);
+        logFunc("[LOG] kernel-fixups: found linkeditVMAddr %p\n", (void*)linkeditVMAddr);
+        logFunc("[LOG] kernel-fixups: found linkeditFileOffset %p\n", (void*)linkeditFileOffset);
+    }
+
+    // Now we have the chained fixups, walk it to apply all the rebases
+    uint32_t offsetInLinkedit   = chainedFixups->dataoff - linkeditFileOffset;
+    uintptr_t linkeditStartAddr = linkeditVMAddr + slide;
+    if (LogFixups) {
+        logFunc("[LOG] kernel-fixups: offsetInLinkedit 0x%x\n", offsetInLinkedit);
+        logFunc("[LOG] kernel-fixups: linkeditStartAddr %p\n", (void*)linkeditStartAddr);
+    }
+
+    const struct dyld_chained_fixups_header* fixupsHeader = (const struct dyld_chained_fixups_header*)(linkeditStartAddr + offsetInLinkedit);
+    const struct dyld_chained_starts_in_image* fixupStarts = (const struct dyld_chained_starts_in_image*)((uint8_t*)fixupsHeader + fixupsHeader->starts_offset);
+       if (LogFixups) {
+        logFunc("[LOG] kernel-fixups: fixupsHeader %p\n", fixupsHeader);
+        logFunc("[LOG] kernel-fixups: fixupStarts %p\n", fixupStarts);
+    }
+
+    int stopped = 0;
+    for (uint32_t segIndex=0; segIndex < fixupStarts->seg_count && !stopped; ++segIndex) {
+       if (LogFixups) {
+               logFunc("[LOG] kernel-fixups: segment %d\n", segIndex);
+           }
+        if ( fixupStarts->seg_info_offset[segIndex] == 0 )
+            continue;
+        const struct dyld_chained_starts_in_segment* segInfo = (const struct dyld_chained_starts_in_segment*)((uint8_t*)fixupStarts + fixupStarts->seg_info_offset[segIndex]);
+        for (uint32_t pageIndex=0; pageIndex < segInfo->page_count && !stopped; ++pageIndex) {
+            uint16_t offsetInPage = segInfo->page_start[pageIndex];
+            if ( offsetInPage == DYLD_CHAINED_PTR_START_NONE )
+                continue;
+            if ( offsetInPage & DYLD_CHAINED_PTR_START_MULTI ) {
+               // FIXME: Implement this
+                return 1;
+            }
+            else {
+                // one chain per page
+                if ( walkChain(mh, segInfo, pageIndex, offsetInPage, slide, basePointers, logFunc) )
+                    stopped = 1;
+            }
+        }
+    }
+
+    return stopped;
+}
+
+#endif /* KERNEL_FIXUPS_H */
diff --git a/testing/test-cases/kernel-hello-world.dtest/main.c b/testing/test-cases/kernel-hello-world.dtest/main.c
new file mode 100644 (file)
index 0000000..af2f7e1
--- /dev/null
@@ -0,0 +1,30 @@
+
+// BOOT_ARGS: amfi=3 cs_enforcement_disable=1
+
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CC main.c -o $BUILD_DIR/kernel-hello-world.exe -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie -Wl,-pagezero_size,0x0 -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-stack-protector -fno-builtin -ffreestanding -Wl,-segprot,__HIB,rx,rx -Wl,-image_base,0x8000 -Wl,-segaddr,__HIB,0x4000  -fno-ptrauth-function-pointer-type-discrimination
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $APP_CACHE_UTIL -create-kernel-collection $BUILD_DIR/kernel-hello-world.kc -kernel $BUILD_DIR/kernel-hello-world.exe -platform kernel
+
+// BUILD(watchos):
+
+// RUN_STATIC:    $RUN_STATIC ./kernel-hello-world.kc
+
+#include "../kernel-test-runner.h"
+
+typedef unsigned long long uint64_t;
+extern int printf(const char*, ...);
+
+#define printf(...) funcs->printf(__VA_ARGS__)
+
+#if __x86_64__
+__attribute__((section(("__HIB, __text"))))
+#else
+__attribute__((section(("__TEXT_EXEC, __text"))))
+#endif
+int _start(const TestRunnerFunctions* funcs)
+{
+    setFuncs(funcs);
+    PASS("Success");
+    return 0;
+}
+
+
diff --git a/testing/test-cases/kernel-helpers.h b/testing/test-cases/kernel-helpers.h
new file mode 100644 (file)
index 0000000..7b944d1
--- /dev/null
@@ -0,0 +1,284 @@
+
+
+#include <mach-o/fixup-chains.h>
+#include <mach-o/loader.h>
+
+#include "kernel-fixups.h"
+
+#ifndef LC_FILESET_ENTRY
+#define LC_FILESET_ENTRY      (0x35 | LC_REQ_DYLD) /* used with fileset_entry_command */
+struct fileset_entry_command {
+    uint32_t        cmd;        /* LC_FILESET_ENTRY */
+    uint32_t        cmdsize;    /* includes id string */
+    uint64_t        vmaddr;     /* memory address of the dylib */
+    uint64_t        fileoff;    /* file offset of the dylib */
+    union lc_str    entry_id;   /* contained entry id */
+    uint32_t        reserved;   /* entry_id is 32-bits long, so this is the reserved padding */
+};
+#endif
+
+typedef int (*ModInitLogFunc)(const char*, ...);
+static const int LogModInits = 0;
+
+struct TestRunnerFunctions;
+typedef int (*InitializerFunc)(const TestRunnerFunctions*);
+
+#if __x86_64__
+__attribute__((section(("__HIB, __text"))))
+#else
+__attribute__((section(("__TEXT_EXEC, __text"))))
+#endif
+static int getSlide(const struct mach_header* mh, ModInitLogFunc logFunc,
+                                       uintptr_t* slide) {
+    uint64_t textVMAddr     = 0;
+
+    if (LogFixups) {
+        logFunc("[LOG] kernel-slide: mh %p\n", mh);
+    }
+
+    if (LogFixups) {
+        logFunc("[LOG] kernel-slide: parsing load commands\n");
+    }
+
+       const struct load_command* startCmds = 0;
+    if ( mh->magic == MH_MAGIC_64 )
+        startCmds = (struct load_command*)((char *)mh + sizeof(struct mach_header_64));
+    else if ( mh->magic == MH_MAGIC )
+        startCmds = (struct load_command*)((char *)mh + sizeof(struct mach_header));
+    else {
+        const uint32_t* h = (uint32_t*)mh;
+        //diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]);
+        return 1;  // not a mach-o file
+    }
+    const struct load_command* const cmdsEnd = (struct load_command*)((char*)startCmds + mh->sizeofcmds);
+    const struct load_command* cmd = startCmds;
+    for (uint32_t i = 0; i < mh->ncmds; ++i) {
+        if (LogFixups) {
+            logFunc("[LOG] kernel-slide: parsing load command %d with cmd=0x%x\n", i, cmd->cmd);
+        }
+        const struct load_command* nextCmd = (struct load_command*)((char *)cmd + cmd->cmdsize);
+        if ( cmd->cmdsize < 8 ) {
+            //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, this->ncmds, cmd, this, cmd->cmdsize);
+            return 1;
+        }
+        if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
+            //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, this->ncmds, cmd, this, cmd->cmdsize, cmdsEnd);
+            return 1;
+        }
+        if ( cmd->cmd == LC_SEGMENT_64 ) {
+               const struct segment_command_64* seg = (const struct segment_command_64*)cmd;
+            if ( areEqual(seg->segname, "__TEXT") ) {
+                textVMAddr = seg->vmaddr;
+            }
+        }
+        cmd = nextCmd;
+    }
+
+    *slide = (uintptr_t)mh - textVMAddr;
+    return 0;
+}
+
+#if __x86_64__
+__attribute__((section(("__HIB, __text"))))
+#else
+__attribute__((section(("__TEXT_EXEC, __text"))))
+#endif
+static int runAllModInitFunctions(const struct mach_header* mh, ModInitLogFunc logFunc,
+                                  const TestRunnerFunctions* funcs)  {
+       uintptr_t slide = 0;
+       if ( getSlide(mh, logFunc, &slide) != 0 ) {
+               return 1;
+       }
+
+       const struct load_command* startCmds = 0;
+    if ( mh->magic == MH_MAGIC_64 )
+        startCmds = (struct load_command*)((char *)mh + sizeof(struct mach_header_64));
+    else if ( mh->magic == MH_MAGIC )
+        startCmds = (struct load_command*)((char *)mh + sizeof(struct mach_header));
+    else {
+        const uint32_t* h = (uint32_t*)mh;
+        //diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]);
+        return 1;  // not a mach-o file
+    }
+
+    const struct load_command* const cmdsEnd = (struct load_command*)((char*)startCmds + mh->sizeofcmds);
+    const struct load_command* cmd = startCmds;
+    for (uint32_t i = 0; i < mh->ncmds; ++i) {
+        if (LogModInits) {
+            logFunc("[LOG] kernel-mod-inits: parsing load command %d with cmd=0x%x\n", i, cmd->cmd);
+        }
+        const struct load_command* nextCmd = (struct load_command*)((char *)cmd + cmd->cmdsize);
+        if ( cmd->cmdsize < 8 ) {
+            //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, this->ncmds, cmd, this, cmd->cmdsize);
+            return 1;
+        }
+        if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
+            //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, this->ncmds, cmd, this, cmd->cmdsize, cmdsEnd);
+            return 1;
+        }
+        if ( cmd->cmd == LC_SEGMENT_64 ) {
+               const struct segment_command_64* seg = (const struct segment_command_64*)cmd;
+            const struct section_64* const sectionsStart = (struct section_64*)((char*)seg + sizeof(struct segment_command_64));
+                       const struct section_64* const sectionsEnd = &sectionsStart[seg->nsects];
+                       for (const struct section_64* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+                               const uint8_t type = sect->flags & SECTION_TYPE;
+                               if ( LogModInits ) {
+                                       logFunc("[LOG] kernel-mod-inits: section: %s %s\n", sect->segname, sect->sectname);
+                               }
+                               if ( type == S_MOD_INIT_FUNC_POINTERS ) {
+                                       InitializerFunc* inits = (InitializerFunc*)(sect->addr + slide);
+                                       const uintptr_t count = sect->size / sizeof(uintptr_t);
+                                       // Ensure __mod_init_func section is within segment
+                                       if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) ) {
+                                               logFunc("[LOG] kernel-mod-inits: __mod_init_funcs section has malformed address range\n");
+                                               return 1;
+                                       }
+                                       for (uintptr_t j = 0; j < count; ++j) {
+                                               InitializerFunc func = inits[j];
+#if __has_feature(ptrauth_calls)
+                                               func = (InitializerFunc)__builtin_ptrauth_sign_unauthenticated((void*)func, ptrauth_key_asia, 0);
+#endif
+                                               if ( LogModInits ) {
+                                                       logFunc("[LOG] kernel-mod-inits: running mod init %p\n", (const void*)func);
+                                               }
+                                               int initResult = func(funcs);
+                                               if ( initResult != 0 ) {
+                                                       logFunc("[LOG] kernel-mod-inits: mod init %p, result = %d\n", (const void*)func, initResult);
+                                                       return 1;
+                                               }
+                                       }
+                               }
+                       }
+        }
+        cmd = nextCmd;
+    }
+
+    return 0;
+}
+
+#if __x86_64__
+__attribute__((section(("__HIB, __text"))))
+#else
+__attribute__((section(("__TEXT_EXEC, __text"))))
+#endif
+static int runAllModInitFunctionsForAppCache(const struct mach_header* appCacheMH, ModInitLogFunc logFunc,
+                                             const TestRunnerFunctions* funcs) {
+       uintptr_t slide = 0;
+       if ( getSlide(appCacheMH, logFunc, &slide) != 0 ) {
+               return 1;
+       }
+
+    if (LogFixups) {
+        logFunc("[LOG] mod-init: appCacheMH %p\n", appCacheMH);
+    }
+
+    if (LogFixups) {
+        logFunc("[LOG] mod-init: parsing load commands\n");
+    }
+
+       const struct load_command* startCmds = 0;
+    if ( appCacheMH->magic == MH_MAGIC_64 )
+        startCmds = (struct load_command*)((char *)appCacheMH + sizeof(struct mach_header_64));
+    else if ( appCacheMH->magic == MH_MAGIC )
+        startCmds = (struct load_command*)((char *)appCacheMH + sizeof(struct mach_header));
+    else {
+        const uint32_t* h = (uint32_t*)appCacheMH;
+        //diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]);
+        return 1;  // not a mach-o file
+    }
+    const struct load_command* const cmdsEnd = (struct load_command*)((char*)startCmds + appCacheMH->sizeofcmds);
+    const struct load_command* cmd = startCmds;
+    for (uint32_t i = 0; i < appCacheMH->ncmds; ++i) {
+        if (LogFixups) {
+            logFunc("[LOG] mod-init: parsing load command %d with cmd=0x%x\n", i, cmd->cmd);
+        }
+        const struct load_command* nextCmd = (struct load_command*)((char *)cmd + cmd->cmdsize);
+        if ( cmd->cmdsize < 8 ) {
+            //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, this->ncmds, cmd, this, cmd->cmdsize);
+            return 1;
+        }
+        if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
+            //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, this->ncmds, cmd, this, cmd->cmdsize, cmdsEnd);
+            return 1;
+        }
+        if ( cmd->cmd == LC_FILESET_ENTRY ) {
+            const struct fileset_entry_command* app_cache_cmd = (const struct fileset_entry_command*)cmd;
+            const char* name = (char*)app_cache_cmd + app_cache_cmd->entry_id.offset;
+            const struct mach_header* mh = (const struct mach_header*)(app_cache_cmd->vmaddr + slide);
+            if ( LogModInits ) {
+                logFunc("[LOG] mod-init: Running mod inits for %p: %s\n", mh, name);
+            }
+            int result = runAllModInitFunctions(mh, logFunc, funcs);
+               if (result != 0) {
+                       return 1;
+               }
+        }
+        cmd = nextCmd;
+    }
+
+    return 0;
+}
+
+#if __x86_64__
+__attribute__((section(("__HIB, __text"))))
+#else
+__attribute__((section(("__TEXT_EXEC, __text"))))
+#endif
+static int slideKextsInsideKernelCollection(const struct mach_header* appCacheMH, const void* basePointers[4],
+                                            FixupsLogFunc logFunc, const TestRunnerFunctions* funcs) {
+    uintptr_t slideAmount = 0;
+    if ( getSlide(appCacheMH, logFunc, &slideAmount) != 0 ) {
+        return 1;
+    }
+
+    if (LogFixups) {
+        logFunc("[LOG] slide-pageable: appCacheMH %p\n", appCacheMH);
+    }
+
+    if (LogFixups) {
+        logFunc("[LOG] slide-pageable: parsing load commands\n");
+    }
+
+       const struct load_command* startCmds = 0;
+    if ( appCacheMH->magic == MH_MAGIC_64 )
+        startCmds = (struct load_command*)((char *)appCacheMH + sizeof(struct mach_header_64));
+    else if ( appCacheMH->magic == MH_MAGIC )
+        startCmds = (struct load_command*)((char *)appCacheMH + sizeof(struct mach_header));
+    else {
+        const uint32_t* h = (uint32_t*)appCacheMH;
+        //diag.error("file does not start with MH_MAGIC[_64]: 0x%08X 0x%08X", h[0], h [1]);
+        return 1;  // not a mach-o file
+    }
+    const struct load_command* const cmdsEnd = (struct load_command*)((char*)startCmds + appCacheMH->sizeofcmds);
+    const struct load_command* cmd = startCmds;
+    for (uint32_t i = 0; i < appCacheMH->ncmds; ++i) {
+        if (LogFixups) {
+            logFunc("[LOG] slide-pageable: parsing load command %d with cmd=0x%x\n", i, cmd->cmd);
+        }
+        const struct load_command* nextCmd = (struct load_command*)((char *)cmd + cmd->cmdsize);
+        if ( cmd->cmdsize < 8 ) {
+            //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) too small", i, this->ncmds, cmd, this, cmd->cmdsize);
+            return 1;
+        }
+        if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) {
+            //diag.error("malformed load command #%d of %d at %p with mh=%p, size (0x%X) is too large, load commands end at %p", i, this->ncmds, cmd, this, cmd->cmdsize, cmdsEnd);
+            return 1;
+        }
+        if ( cmd->cmd == LC_FILESET_ENTRY ) {
+            const struct fileset_entry_command* app_cache_cmd = (const struct fileset_entry_command*)cmd;
+            const char* name = (char*)app_cache_cmd + app_cache_cmd->entry_id.offset;
+            const struct mach_header* mh = (const struct mach_header*)(app_cache_cmd->vmaddr + slideAmount);
+            if ( LogModInits ) {
+                logFunc("[LOG] slide-pageable: Sliding %p: %s\n", mh, name);
+            }
+            int slideReturnCode = slide(mh, basePointers, logFunc);
+            if ( slideReturnCode != 0 ) {
+                FAIL("mh slide = %d\n", slideReturnCode);
+                return 1;
+            }
+        }
+        cmd = nextCmd;
+    }
+
+    return 0;
+}
diff --git a/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/bar.c b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/bar.c
new file mode 100644 (file)
index 0000000..87fe853
--- /dev/null
@@ -0,0 +1,25 @@
+
+#include "../kernel-test-runner.h"
+
+extern int pageableExport();
+__typeof(&pageableExport) pageableExportPtr = &pageableExport;
+
+int bar() {
+       return pageableExportPtr() + 2;
+}
+
+extern int pageableExportDirect();
+
+// Test direct pointer fixups to the pageable KC.  On x86_64 these would be emitted as just
+// a branch relocation so we needed to synthesize a stub
+__attribute__((constructor))
+int testDirectToPageable(const TestRunnerFunctions* funcs) {
+    LOG("testDirectToPageable(): start");
+    // The pageable returned 42
+    int v = pageableExportDirect();
+    if ( v != 42 ) {
+        FAIL("pageableExportDirect() returned %d vs expected 42", v);
+    }
+    LOG("testDirectToPageable(): end");
+    return 0;
+}
diff --git a/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/extensions/bar.kext/Info.plist b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..f699ffc
--- /dev/null
@@ -0,0 +1,23 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.pageable</key>
+               <string>1.0</string>
+       </dict>
+       <key>CFBundleVersion</key>
+       <string>1.0.0</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0.0</string>
+</dict>
+</plist>
diff --git a/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/extensions/foo.kext/Info.plist b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..d7245d1
--- /dev/null
@@ -0,0 +1,23 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>apfs</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.bar</key>
+               <string>1.0</string>
+       </dict>
+       <key>CFBundleVersion</key>
+       <string>1.0.0</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0.0</string>
+</dict>
+</plist>
diff --git a/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/extensions/pageable.kext/Info.plist b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/extensions/pageable.kext/Info.plist
new file mode 100644 (file)
index 0000000..8264e75
--- /dev/null
@@ -0,0 +1,18 @@
+<?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>CFBundleExecutable</key>
+       <string>pageable</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.pageable</string>
+       <key>CFBundleName</key>
+       <string>pageable</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>CFBundleVersion</key>
+       <string>1.0.0</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0.0</string>
+</dict>
+</plist>
diff --git a/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/foo.c b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/foo.c
new file mode 100644 (file)
index 0000000..254aab5
--- /dev/null
@@ -0,0 +1,38 @@
+
+#include "../kernel-test-runner.h"
+
+extern int bar();
+__typeof(&bar) barPtr = &bar;
+
+int foo() {
+       return barPtr() + 4;
+}
+
+__attribute__((constructor))
+int test(const TestRunnerFunctions* funcs) {
+    LOG("test(): start");
+    // pageable, bar, and foo each added 1, 2, 4, so we need to return 7 to know this worked
+       int v = foo();
+    if ( v != 7 ) {
+        FAIL("foo() returned %d vs expected 7", v);
+    }
+    LOG("test(): end");
+    return 0;
+}
+
+int fooDirect() {
+    return bar() + 4;
+}
+
+// Test direct pointer fixups, ie, not via a GOT
+__attribute__((constructor))
+int testDirect(const TestRunnerFunctions* funcs) {
+    LOG("testDirect(): start");
+    // pageable, bar, and foo each added 1, 2, 4, so we need to return 7 to know this worked
+    int v = fooDirect();
+    if ( v != 7 ) {
+        FAIL("fooDirect() returned %d vs expected 7", v);
+    }
+    LOG("testDirect(): end");
+    return 0;
+}
diff --git a/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/main.c b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/main.c
new file mode 100644 (file)
index 0000000..dc1834d
--- /dev/null
@@ -0,0 +1,108 @@
+
+// BOOT_ARGS: amfi=3 cs_enforcement_disable=1
+
+// Create the base kernel collection
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CC main.c -o $BUILD_DIR/kernel-auxkc-fixups.exe -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie -Wl,-pagezero_size,0x0 -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-stack-protector -fno-builtin -ffreestanding -Wl,-segprot,__HIB,rx,rx -Wl,-image_base,0x8000 -Wl,-segaddr,__HIB,0x4000 -fno-ptrauth-function-pointer-type-discrimination
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $APP_CACHE_UTIL -create-kernel-collection $BUILD_DIR/kernel.kc -kernel $BUILD_DIR/kernel-auxkc-fixups.exe
+
+// Create the pageable kernel collection
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CP extensions/pageable.kext/Info.plist $BUILD_DIR/extensions/pageable-kext/Info.plist
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CC pageable.c -o $BUILD_DIR/extensions/pageable-kext/pageable -Wl,-kext -Wl,-kext_objects_dir,$BUILD_DIR/KextObjects -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-ptrauth-function-pointer-type-discrimination
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $APP_CACHE_UTIL -create-pageable-kernel-collection $BUILD_DIR/pageable.kc -kernel-collection $BUILD_DIR/kernel.kc -extensions $BUILD_DIR/extensions -bundle-id com.apple.pageable $DEPENDS_ON $BUILD_DIR/extensions/pageable-kext/Info.plist $DEPENDS_ON $BUILD_DIR/extensions/pageable-kext/pageable
+
+// Create the aux kernel collection
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CP extensions/foo.kext/Info.plist $BUILD_DIR/extensions/foo-kext/Info.plist
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CP extensions/bar.kext/Info.plist $BUILD_DIR/extensions/bar-kext/Info.plist
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CC foo.c -o $BUILD_DIR/extensions/foo-kext/foo -Wl,-kext -Wl,-kext_objects_dir,$BUILD_DIR/KextObjects -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-ptrauth-function-pointer-type-discrimination
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CC bar.c -o $BUILD_DIR/extensions/bar-kext/bar -Wl,-kext -Wl,-kext_objects_dir,$BUILD_DIR/KextObjects -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-ptrauth-function-pointer-type-discrimination
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $APP_CACHE_UTIL -create-aux-kernel-collection $BUILD_DIR/aux.kc -kernel-collection $BUILD_DIR/kernel.kc -pageable-collection $BUILD_DIR/pageable.kc -extensions $BUILD_DIR/extensions  -bundle-id com.apple.foo $DEPENDS_ON $BUILD_DIR/extensions/foo-kext/Info.plist $DEPENDS_ON $BUILD_DIR/extensions/bar-kext/Info.plist $DEPENDS_ON $BUILD_DIR/extensions/foo-kext/foo $DEPENDS_ON $BUILD_DIR/extensions/bar-kext/bar
+
+// BUILD(watchos):
+
+// RUN_STATIC:    $RUN_STATIC $RUN_DIR/kernel.kc $RUN_DIR/pageable.kc - $RUN_DIR/aux.kc
+
+#include "../kernel-test-runner.h"
+#include "../kernel-fixups.h"
+#include "../kernel-classic-relocs.h"
+#include "../kernel-helpers.h"
+
+#define printf(...) funcs->printf(__VA_ARGS__)
+
+int x = 1;
+int *g = &x;
+
+#if __x86_64__
+__attribute__((section(("__HIB, __text"))))
+#else
+__attribute__((section(("__TEXT_EXEC, __text"))))
+#endif
+int _start(const TestRunnerFunctions* funcs)
+{
+    setFuncs(funcs);
+
+    const void* slideBasePointers[4];
+    slideBasePointers[0] = funcs->basePointers[0];
+    slideBasePointers[1] = funcs->basePointers[1];
+    slideBasePointers[2] = funcs->basePointers[2];
+    slideBasePointers[3] = funcs->basePointers[3];
+    int slideReturnCode = slide(funcs->mhs[0], slideBasePointers, funcs->printf);
+    if ( slideReturnCode != 0 ) {
+        FAIL("mhs[0] slide = %d\n", slideReturnCode);
+        return 0;
+    }
+
+    int slideClassicReturnCode = slideClassic(funcs->mhs[0], funcs->printf);
+    if ( slideClassicReturnCode != 0 ) {
+        FAIL("mhs[0] slide classic = %d\n", slideClassicReturnCode);
+        return 0;
+    }
+
+    if ( g[0] != x ) {
+       FAIL("g[0] != x, %d != %d\n", g[0], x);
+       return 0;
+    }
+
+    // First slide the pageableKC using the top level fixups.  These handle the branch GOTs
+    slideReturnCode = slide(funcs->mhs[1], slideBasePointers, funcs->printf);
+    if ( slideReturnCode != 0 ) {
+        FAIL("mhs[1] slide = %d\n", slideReturnCode);
+        return 0;
+    }
+
+    // Then slide pageable using the fixups attached to the kexts own mach headers
+    slideReturnCode = slideKextsInsideKernelCollection(funcs->mhs[1], slideBasePointers, funcs->printf, funcs);
+    if ( slideReturnCode != 0 ) {
+        FAIL("mhs[1] slide = %d\n", slideReturnCode);
+        return 0;
+    }
+
+    // Slide the auc KC
+    slideReturnCode = slide(funcs->mhs[3], slideBasePointers, funcs->printf);
+    if ( slideReturnCode != 0 ) {
+        FAIL("mhs[3] slide = %d\n", slideReturnCode);
+        return 0;
+    }
+
+  #if __x86_64__
+      // On x86 only, slide the auxKC individually
+      // Then slide pageable using the fixups attached to the kexts own mach headers
+      slideReturnCode = slideKextsInsideKernelCollection(funcs->mhs[3], slideBasePointers, funcs->printf, funcs);
+      if ( slideReturnCode != 0 ) {
+          FAIL("mhs[3] slide = %d\n", slideReturnCode);
+          return 0;
+      }
+  #endif
+
+    // If we have any mod init funcs, then lets run them now
+    // These are the tests inside the auxKC kexts
+    int runModInitFuncs = runAllModInitFunctionsForAppCache(funcs->mhs[3], funcs->printf, funcs);
+    if ( runModInitFuncs != 0 ) {
+        FAIL("runModInitFuncs = %d\n", runModInitFuncs);
+        return 0;
+    }
+
+    PASS("Success");
+    return 0;
+}
+
+
diff --git a/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/pageable.c b/testing/test-cases/kernel-pageablekc-auxkc-fixups.dtest/pageable.c
new file mode 100644 (file)
index 0000000..f5638a4
--- /dev/null
@@ -0,0 +1,10 @@
+
+#include "../kernel-test-runner.h"
+
+int pageableExport() {
+       return 1;
+}
+
+int pageableExportDirect() {
+    return 42;
+}
diff --git a/testing/test-cases/kernel-pageablekc-fixups.dtest/bar.c b/testing/test-cases/kernel-pageablekc-fixups.dtest/bar.c
new file mode 100644 (file)
index 0000000..c5635c0
--- /dev/null
@@ -0,0 +1,25 @@
+
+#include "../kernel-test-runner.h"
+
+extern int kernelExport();
+__typeof(&kernelExport) kernelExportPtr = &kernelExport;
+
+int bar() {
+       return kernelExportPtr() + 2;
+}
+
+extern int kernelExportDirect();
+
+// Test direct pointer fixups to the kernel.  On x86_64 these would be emitted as just
+// a branch relocation so we needed to synthesize a stub
+__attribute__((constructor))
+int testDirectToKernel(const TestRunnerFunctions* funcs) {
+    LOG("testDirectToKernel(): start");
+    // The kernel returned 42
+    int v = kernelExportDirect();
+    if ( v != 42 ) {
+        FAIL("kernelExportDirect() returned %d vs expected 42", v);
+    }
+    LOG("testDirectToKernel(): end");
+    return 0;
+}
diff --git a/testing/test-cases/kernel-pageablekc-fixups.dtest/extensions/bar.kext/Info.plist b/testing/test-cases/kernel-pageablekc-fixups.dtest/extensions/bar.kext/Info.plist
new file mode 100644 (file)
index 0000000..e470d05
--- /dev/null
@@ -0,0 +1,23 @@
+<?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>CFBundleExecutable</key>
+       <string>bar</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.bar</string>
+       <key>CFBundleName</key>
+       <string>bar</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.kernel.export</key>
+               <string>1.0</string>
+       </dict>
+       <key>CFBundleVersion</key>
+       <string>1.0.0</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0.0</string>
+</dict>
+</plist>
diff --git a/testing/test-cases/kernel-pageablekc-fixups.dtest/extensions/foo.kext/Info.plist b/testing/test-cases/kernel-pageablekc-fixups.dtest/extensions/foo.kext/Info.plist
new file mode 100644 (file)
index 0000000..d7245d1
--- /dev/null
@@ -0,0 +1,23 @@
+<?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>CFBundleExecutable</key>
+       <string>foo</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.foo</string>
+       <key>CFBundleName</key>
+       <string>apfs</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>OSBundleLibraries</key>
+       <dict>
+               <key>com.apple.bar</key>
+               <string>1.0</string>
+       </dict>
+       <key>CFBundleVersion</key>
+       <string>1.0.0</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0.0</string>
+</dict>
+</plist>
diff --git a/testing/test-cases/kernel-pageablekc-fixups.dtest/extensions/kernel-export.kext/Info.plist b/testing/test-cases/kernel-pageablekc-fixups.dtest/extensions/kernel-export.kext/Info.plist
new file mode 100644 (file)
index 0000000..3376dea
--- /dev/null
@@ -0,0 +1,18 @@
+<?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>CFBundleExecutable</key>
+       <string>kernel-export</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.kernel.export</string>
+       <key>CFBundleName</key>
+       <string>kernel-export</string>
+       <key>CFBundlePackageType</key>
+       <string>KEXT</string>
+       <key>CFBundleVersion</key>
+       <string>1.0.0</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0.0</string>
+</dict>
+</plist>
diff --git a/testing/test-cases/kernel-pageablekc-fixups.dtest/foo.c b/testing/test-cases/kernel-pageablekc-fixups.dtest/foo.c
new file mode 100644 (file)
index 0000000..3e5b437
--- /dev/null
@@ -0,0 +1,38 @@
+
+#include "../kernel-test-runner.h"
+
+extern int bar();
+__typeof(&bar) barPtr = &bar;
+
+int foo() {
+       return barPtr() + 4;
+}
+
+__attribute__((constructor))
+int test(const TestRunnerFunctions* funcs) {
+    LOG("test(): start");
+    // kernel, bar, and foo each added 1, 2, 4, so we need to return 7 to know this worked
+       int v = foo();
+    if ( v != 7 ) {
+        FAIL("foo() returned %d vs expected 7", v);
+    }
+    LOG("test(): end");
+    return 0;
+}
+
+int fooDirect() {
+    return bar() + 4;
+}
+
+// Test direct pointer fixups, ie, not via a GOT
+__attribute__((constructor))
+int testDirect(const TestRunnerFunctions* funcs) {
+    LOG("testDirect(): start");
+    // kernel, bar, and foo each added 1, 2, 4, so we need to return 7 to know this worked
+    int v = fooDirect();
+    if ( v != 7 ) {
+        FAIL("fooDirect() returned %d vs expected 7", v);
+    }
+    LOG("testDirect(): end");
+    return 0;
+}
diff --git a/testing/test-cases/kernel-pageablekc-fixups.dtest/kernel-export.c b/testing/test-cases/kernel-pageablekc-fixups.dtest/kernel-export.c
new file mode 100644 (file)
index 0000000..3313ca8
--- /dev/null
@@ -0,0 +1,10 @@
+
+#include "../kernel-test-runner.h"
+
+int kernelExport() {
+       return 1;
+}
+
+int kernelExportDirect() {
+    return 42;
+}
diff --git a/testing/test-cases/kernel-pageablekc-fixups.dtest/main.c b/testing/test-cases/kernel-pageablekc-fixups.dtest/main.c
new file mode 100644 (file)
index 0000000..de80410
--- /dev/null
@@ -0,0 +1,87 @@
+
+// BOOT_ARGS: amfi=3 cs_enforcement_disable=1
+
+// Create the base kernel collection
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CP extensions/kernel-export.kext/Info.plist $BUILD_DIR/extensions/kernel-export-kext/Info.plist
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CC main.c -o $BUILD_DIR/kernel-auxkc-fixups.exe -Wl,-static -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-e,__start -Wl,-pie -Wl,-pagezero_size,0x0 -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-stack-protector -fno-builtin -ffreestanding -Wl,-segprot,__HIB,rx,rx -Wl,-image_base,0x8000 -Wl,-segaddr,__HIB,0x4000  -fno-ptrauth-function-pointer-type-discrimination
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CC kernel-export.c -o $BUILD_DIR/extensions/kernel-export-kext/kernel-export -Wl,-kext -Wl,-kext_objects_dir,$BUILD_DIR/KextObjects -mkernel -nostdlib -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack  -fno-ptrauth-function-pointer-type-discrimination
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $APP_CACHE_UTIL -create-kernel-collection $BUILD_DIR/kernel.kc -kernel $BUILD_DIR/kernel-auxkc-fixups.exe -extensions $BUILD_DIR/extensions -bundle-id com.apple.kernel.export $DEPENDS_ON $BUILD_DIR/extensions/kernel-export-kext/Info.plist $DEPENDS_ON $BUILD_DIR/extensions/kernel-export-kext/kernel-export
+
+// Create the pageable kernel collection
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CP extensions/foo.kext/Info.plist $BUILD_DIR/extensions/foo-kext/Info.plist
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CP extensions/bar.kext/Info.plist $BUILD_DIR/extensions/bar-kext/Info.plist
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CC foo.c -o $BUILD_DIR/extensions/foo-kext/foo -Wl,-kext -mkernel -Wl,-kext_objects_dir,$BUILD_DIR/KextObjects -nostdlib -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-ptrauth-function-pointer-type-discrimination
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $CC bar.c -o $BUILD_DIR/extensions/bar-kext/bar -Wl,-kext -mkernel -Wl,-kext_objects_dir,$BUILD_DIR/KextObjects -nostdlib -Wl,-add_split_seg_info -Wl,-install_name,/usr/lib/swift/split.seg.v2.hack -fno-ptrauth-function-pointer-type-discrimination
+// BUILD(macos,ios,tvos,bridgeos|x86_64,arm64,arm64e):  $APP_CACHE_UTIL -create-pageable-kernel-collection $BUILD_DIR/pageable.kc -kernel-collection $BUILD_DIR/kernel.kc -extensions $BUILD_DIR/extensions  -bundle-id com.apple.foo $DEPENDS_ON $BUILD_DIR/extensions/foo-kext/Info.plist $DEPENDS_ON $BUILD_DIR/extensions/bar-kext/Info.plist $DEPENDS_ON $BUILD_DIR/extensions/foo-kext/foo $DEPENDS_ON $BUILD_DIR/extensions/bar-kext/bar
+
+// BUILD(watchos):
+
+// RUN_STATIC:    $RUN_STATIC $RUN_DIR/kernel.kc $RUN_DIR/pageable.kc - -
+
+#include "../kernel-test-runner.h"
+#include "../kernel-fixups.h"
+#include "../kernel-classic-relocs.h"
+#include "../kernel-helpers.h"
+
+#define printf(...) funcs->printf(__VA_ARGS__)
+
+int x = 1;
+int *g = &x;
+
+#if __x86_64__
+__attribute__((section(("__HIB, __text"))))
+#else
+__attribute__((section(("__TEXT_EXEC, __text"))))
+#endif
+int _start(const TestRunnerFunctions* funcs)
+{
+    setFuncs(funcs);
+
+    const void* slideBasePointers[4];
+    slideBasePointers[0] = funcs->basePointers[0];
+    slideBasePointers[1] = funcs->basePointers[1];
+    slideBasePointers[2] = funcs->basePointers[2];
+    slideBasePointers[3] = funcs->basePointers[3];
+    int slideReturnCode = slide(funcs->mhs[0], slideBasePointers, funcs->printf);
+    if ( slideReturnCode != 0 ) {
+        FAIL("mhs[0] slide = %d\n", slideReturnCode);
+        return 0;
+    }
+
+    int slideClassicReturnCode = slideClassic(funcs->mhs[0], funcs->printf);
+    if ( slideClassicReturnCode != 0 ) {
+        FAIL("mhs[0] slide classic = %d\n", slideClassicReturnCode);
+        return 0;
+    }
+
+    if ( g[0] != x ) {
+       FAIL("g[0] != x, %d != %d\n", g[0], x);
+       return 0;
+    }
+
+    // First slide the pageableKC using the top level fixups.  These handle the branch GOTs
+    slideReturnCode = slide(funcs->mhs[1], slideBasePointers, funcs->printf);
+    if ( slideReturnCode != 0 ) {
+        FAIL("mhs[1] slide = %d\n", slideReturnCode);
+        return 0;
+    }
+
+    // Then slide pageable using the fixups attached to the kexts own mach headers
+    slideReturnCode = slideKextsInsideKernelCollection(funcs->mhs[1], slideBasePointers, funcs->printf, funcs);
+    if ( slideReturnCode != 0 ) {
+        FAIL("mhs[1] slide = %d\n", slideReturnCode);
+        return 0;
+    }
+
+    // If we have any mod init funcs, then lets run them now
+    int runModInitFuncs = runAllModInitFunctionsForAppCache(funcs->mhs[1], funcs->printf, funcs);
+    if ( runModInitFuncs != 0 ) {
+        FAIL("runModInitFuncs = %d\n", runModInitFuncs);
+        return 0;
+    }
+
+    PASS("Success");
+    return 0;
+}
+
+
diff --git a/testing/test-cases/kernel-test-runner.h b/testing/test-cases/kernel-test-runner.h
new file mode 100644 (file)
index 0000000..c5858d3
--- /dev/null
@@ -0,0 +1,63 @@
+
+#if __has_feature(ptrauth_calls)
+#include <ptrauth.h>
+#endif
+
+typedef unsigned long long uint64_t;
+extern int printf(const char*, ...);
+extern void exit(int);
+
+#if __cplusplus
+extern "C" {
+#endif  /* __cplusplus */
+__attribute__((format(printf, 3, 4)))
+__attribute__ ((noreturn))
+extern void _PASS(const char* file, unsigned line, const char* format, ...);
+
+__attribute__((format(printf, 3, 4)))
+__attribute__ ((noreturn))
+extern void _FAIL(const char* file, unsigned line, const char* format, ...);
+
+__attribute__((format(printf, 3, 4)))
+extern void _LOG(const char* file, unsigned line, const char* format, ...);
+
+extern void _TIMEOUT(const char* file, unsigned line, uint64_t seconds);
+#if __cplusplus
+};
+#endif  /* __cplusplus */
+
+// The arm64e kernel/userland ABIs sign C function pointers differently.
+// We aren't trying to test that ABI, so lets just force a different signature
+#if __has_feature(ptrauth_calls)
+#   define PtrAuth __ptrauth(ptrauth_key_function_pointer, 1, 0xc671)
+#else
+#   define PtrAuth
+#endif
+
+typedef struct TestRunnerFunctions {
+    uint64_t                            version;
+    const struct mach_header*           mhs[4];
+    const void*                         basePointers[4];
+    PtrAuth __typeof(&printf)           printf;
+    PtrAuth __typeof(&exit)             exit;
+    PtrAuth __typeof(&_PASS)            testPass;
+    PtrAuth __typeof(&_FAIL)            testFail;
+    PtrAuth __typeof(&_LOG)             testLog;
+    PtrAuth __typeof(&_TIMEOUT)         testTimeout;
+} TestRunnerFunctions;
+
+static const TestRunnerFunctions* funcs = 0;
+
+#define PASS(...)           funcs->testPass(__FILE__,__LINE__,__VA_ARGS__)
+#define FAIL(...)           funcs->testFail(__FILE__,__LINE__,__VA_ARGS__)
+#define LOG(...)            funcs->testLog(__FILE__,__LINE__,__VA_ARGS__)
+#define TIMEOUT(seconds)    funcs->testTimeout(__FILE__,__LINE__,seconds)
+
+#if __x86_64__
+__attribute__((section(("__HIB, __text"))))
+#else
+__attribute__((section(("__TEXT_EXEC, __text"))))
+#endif
+static inline void setFuncs(const TestRunnerFunctions* v) {
+    funcs = v;
+}
diff --git a/testing/test-cases/large-load-commands.dtest/extra.cmds b/testing/test-cases/large-load-commands.dtest/extra.cmds
new file mode 100644 (file)
index 0000000..e4b7bce
--- /dev/null
@@ -0,0 +1,202 @@
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/0
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/1
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/2
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/3
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/4
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/5
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/6
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/7
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/8
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/9
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/10
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/11
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/12
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/13
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/14
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/15
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/16
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/17
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/18
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/19
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/20
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/21
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/22
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/23
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/24
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/25
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/26
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/27
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/28
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/29
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/30
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/31
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/32
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/33
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/34
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/35
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/36
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/37
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/38
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/39
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/40
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/41
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/42
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/43
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/44
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/45
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/46
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/47
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/48
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/49
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/50
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/51
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/52
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/53
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/54
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/55
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/56
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/57
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/58
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/59
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/60
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/61
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/62
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/63
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/64
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/65
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/66
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/67
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/68
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/69
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/70
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/71
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/72
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/73
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/74
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/75
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/76
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/77
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/78
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/79
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/80
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/81
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/82
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/83
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/84
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/85
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/86
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/87
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/88
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/89
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/80
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/91
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/92
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/93
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/94
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/95
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/96
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/97
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/98
+-rpath /usr/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/99
+
+
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/0
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/1
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/2
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/3
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/4
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/5
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/6
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/7
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/8
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/9
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/10
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/11
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/12
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/13
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/14
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/15
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/16
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/17
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/18
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/19
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/20
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/21
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/22
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/23
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/24
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/25
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/26
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/27
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/28
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/29
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/30
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/31
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/32
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/33
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/34
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/35
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/36
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/37
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/38
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/39
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/40
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/41
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/42
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/43
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/44
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/45
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/46
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/47
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/48
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/49
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/50
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/51
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/52
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/53
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/54
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/55
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/56
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/57
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/58
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/59
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/60
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/61
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/62
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/63
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/64
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/65
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/66
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/67
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/68
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/69
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/70
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/71
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/72
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/73
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/74
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/75
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/76
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/77
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/78
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/79
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/80
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/81
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/82
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/83
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/84
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/85
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/86
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/87
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/88
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/89
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/80
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/91
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/92
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/93
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/94
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/95
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/96
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/97
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/98
+-rpath /usr/200000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/100000000000000/99
diff --git a/testing/test-cases/large-load-commands.dtest/foo.c b/testing/test-cases/large-load-commands.dtest/foo.c
new file mode 100644 (file)
index 0000000..e06aa42
--- /dev/null
@@ -0,0 +1,7 @@
+
+#include <stdlib.h>
+#include <string.h>
+
+void foo() {}
+
+
diff --git a/testing/test-cases/large-load-commands.dtest/main.c b/testing/test-cases/large-load-commands.dtest/main.c
new file mode 100644 (file)
index 0000000..017057e
--- /dev/null
@@ -0,0 +1,17 @@
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -Wl,@$SRC_DIR/extra.cmds
+// BUILD:  $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/large-load-commands.exe
+
+// RUN:    ./large-load-commands.exe
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "test_support.h"
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[])
+{
+    PASS("Success");
+}
index 6648a1281801c8e740233aef369e2127639986da..d2959c3a9c66209b8f6700c20b18a001885671ed 100644 (file)
@@ -2,8 +2,12 @@
 
 #include <stdio.h>
 
-
+#ifdef WEAK
+__attribute__((weak_import))
+extern int slipperySymbol();
+#else
 extern int slipperySymbol();
+#endif
 
 
 int main(int argc, const char* argv[])
index 0c74e996df70dbca770661d5c11c9a825236ecaf..0bb65406c1bb8de04d1b55df578935207be7a1c9 100644 (file)
@@ -2,18 +2,24 @@
 
 // BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib         -install_name $RUN_DIR/libfoo.dylib
 // BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo-present.dylib  -install_name $RUN_DIR/libfoo.dylib -DHAS_SYMBOL=1
+// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/libbar-missing.dylib  -install_name $RUN_DIR/libbar-missing.dylib -DHAS_SYMBOL=1
 // BUILD:  $CC main.c            -o $BUILD_DIR/lazy-symbol-missing.exe        $BUILD_DIR/libfoo-present.dylib -Os
 // BUILD:  $CC main.c            -o $BUILD_DIR/lazy-symbol-missing-flat.exe   -undefined dynamic_lookup      -Os -DFLAT=1
 // BUILD:  $CC main-call.c       -o $BUILD_DIR/lazy-symbol-missing-called.exe $BUILD_DIR/libfoo-present.dylib -Os
+// BUILD:  $CC main-call.c       -o $BUILD_DIR/lazy-symbol-missing-called-weak-lib.exe $BUILD_DIR/libbar-missing.dylib -Os -DWEAK=1
 // BUILD:  $CXX runner.cpp         -o $BUILD_DIR/lazy-symbol-runner.exe -DRUN_DIR="$RUN_DIR"
+// BUILD:  $CXX runner.cpp         -o $BUILD_DIR/lazy-symbol-runner-weak-lib.exe -DRUN_DIR="$RUN_DIR" -DWEAK=1
 
 // BUILD: $SKIP_INSTALL $BUILD_DIR/libfoo-present.dylib
+// BUILD: $SKIP_INSTALL $BUILD_DIR/libbar-missing.dylib
 
 // NO_CRASH_LOG: lazy-symbol-missing-called.exe
+// NO_CRASH_LOG: lazy-symbol-missing-called-weak-lib.exe
 
 // RUN:  ./lazy-symbol-missing.exe
 // RUN:  ./lazy-symbol-runner.exe
 // RUN:  ./lazy-symbol-missing-flat.exe
+// RUN:  ./lazy-symbol-runner-weak-lib.exe
 
 
 #include <stdio.h>
index aadd53f3ee724cadc1edaa5864c946587a8ce091..d7c15a37a06bf9463fe341eaa95a093c985649b1 100644 (file)
 
 #include "test_support.h"
 
+static const char* getUserDescription(struct proc_exitreasoninfo& info) {
+    kcdata_iter_t iter =  kcdata_iter((void*)info.eri_kcd_buf, info.eri_reason_buf_size);
+    if ( !kcdata_iter_valid(iter) )
+        return NULL;
+    if ( kcdata_iter_type(iter) != KCDATA_BUFFER_BEGIN_OS_REASON )
+        return NULL;
+    iter = kcdata_iter_find_type(iter, EXIT_REASON_USER_DESC);
+    if ( !kcdata_iter_valid(iter) ) {
+        return NULL;
+    }
+    return kcdata_iter_string(iter, 0);
+}
+
 int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
 #if SUPPORTS_LAZY_BINDING
     _process process;
+#ifdef WEAK
+    process.set_executable_path(RUN_DIR "/lazy-symbol-missing-called-weak-lib.exe");
+#else
     process.set_executable_path(RUN_DIR "/lazy-symbol-missing-called.exe");
+#endif
     const char* env[] = { "TEST_OUTPUT=None", NULL};
     process.set_env(env);
     process.set_exit_handler(^(pid_t pid) {
@@ -46,6 +63,18 @@ int main(int argc, const char* argv[], const char* envp[], const char* apple[])
         if ( info.eri_namespace != OS_REASON_DYLD ) {
             FAIL("eri_namespace (%d) != OS_REASON_DYLD", info.eri_namespace);
         }
+        const char* userDesc = getUserDescription(info);
+        if ( userDesc != NULL ) {
+            LOG("user desc=%s", (const char*)userDesc);
+        }
+#ifdef WEAK
+        // Make sure we print a dylib name, not a dependency library # when referencing a symbol from a missing library
+        if ( userDesc != NULL ) {
+            if ( strstr(userDesc, "libbar-missing.dylib") == NULL ) {
+                FAIL("Expected name of missing dylib");
+            }
+        }
+#endif
         PASS("Success");
     });
     (void)process.launch();
diff --git a/testing/test-cases/libdsc.dtest/main.c b/testing/test-cases/libdsc.dtest/main.c
new file mode 100644 (file)
index 0000000..9fecb97
--- /dev/null
@@ -0,0 +1,40 @@
+
+// BUILD:  $CC main.c      -o $BUILD_DIR/libdsc-test.exe -ldsc
+
+// RUN: ./libdsc-test.exe
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h> 
+#include <mach-o/dyld_priv.h>
+#include <dlfcn.h>
+
+#include "test_support.h"
+#include "shared-cache/dsc_iterator.h"
+
+// This program links libdsc.a and verifies that dyld_shared_cache_iterate() works
+
+int main(int argc, const char* argv[], const char* envp[], const char* apple[])
+{
+    size_t cacheLen;
+    const void* cacheStart = _dyld_get_shared_cache_range(&cacheLen);
+
+    if ( cacheStart != NULL ) {
+        dyld_shared_cache_iterate(cacheStart, cacheLen, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) {
+            if ( false ) {
+                printf("%p %s\n", dylibInfo->machHeader, dylibInfo->path);
+                printf("    dylib.version=%d\n", dylibInfo->version);
+                printf("    dylib.isAlias=%d\n", dylibInfo->isAlias);
+                printf("    dylib.inode=%lld\n",   dylibInfo->inode);
+                printf("    dylib.modTime=%lld\n", dylibInfo->modTime);
+                printf("    segment.name=         %s\n", segInfo->name);
+                printf("    segment.fileOffset=   0x%08llX\n", segInfo->fileOffset);
+                printf("    segment.fileSize=     0x%08llX\n", segInfo->fileSize);
+                printf("    segment.address=      0x%08llX\n", segInfo->address);
+                printf("    segment.addressOffset=0x%08llX\n", segInfo->addressOffset);
+            }
+        });
+    }
+
+    PASS("Success");
+}
index 9d3ea90842a28f688b0ae18105b1164e49b5e2ab..adff7a5f2d597d5469f4ddc41062922e5e03cc3f 100644 (file)
@@ -1,6 +1,6 @@
-// BUILD_ONLY: MacOSX
+// BUILD(macos):  $CXX main.cpp          -o $BUILD_DIR/rebuild-dyld-cache.exe
 
-// BUILD:  $CXX main.cpp          -o $BUILD_DIR/rebuild-dyld-cache.exe
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // FIXME: This test will not make sense when remove update_dyld_shared_cache, and the functionality will be subsummed by root testing
 // ./rebuild-dyld-cache.exe
index 55c56b11e0e6cc9f4b0683db3741c38e49ef1793..92c8403deef4371e2a2c4ae5f6d5078876e92def 100644 (file)
@@ -1,4 +1,5 @@
 
+__attribute__((weak))
 int bar() {
        return 0;
-}
\ No newline at end of file
+}
index f677581dc4edda97727e1303527db68630500d35..c6b06d71cd2539419b87d705ba1c48809e892106 100644 (file)
@@ -1,6 +1,6 @@
 
 // BUILD:  $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/missing/libbar.dylib
-// BUILD:  $CC bar-empty.c -dynamiclib -install_name $BUILD_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
+// BUILD:  $CC bar-empty.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
 // BUILD:  $CC main.c $BUILD_DIR/missing/libbar.dylib -o $BUILD_DIR/missing-weak-def.exe
 
 // BUILD: $SKIP_INSTALL $BUILD_DIR/missing/libbar.dylib
 
 #include "test_support.h"
 
-__attribute__((weak))
 __attribute__((weak_import))
 int bar();
 
 int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
     if (&bar) {
-        FAIL("bar from libbar not bound");
+         bar();
+       FAIL("bar from libbar not bound");
     }
 
     PASS("Success");
diff --git a/testing/test-cases/no-shared-cache.dtest/main.c b/testing/test-cases/no-shared-cache.dtest/main.c
deleted file mode 100644 (file)
index 1ce992e..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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>
-
-#include "test_support.h"
-
-// This program links with AppKit which in dyld3 mode stress tests building closures when there is no dyld shared cache
-
-int main(int argc, const char* argv[], const char* envp[], const char* apple[]) {
-
-    size_t cacheLen;
-    const void* cacheStart = _dyld_get_shared_cache_range(&cacheLen);
-
-    if ( cacheStart != NULL ) {
-        FAIL("_dyld_get_shared_cache_range() returned %p even though we are not using a dyld cache", cacheStart);
-    }
-
-    PASS("Success");
-}
-
index a00a77b4c47cb7880027aae074be54aca1fdfdbb..3994076842e78594e446bbb2fb8905a3774674b7 100644 (file)
@@ -1,12 +1,11 @@
-// BUILD_ONLY: MacOSX
-
-// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/lc/libfoo.dylib -install_name /blah/libfoo.dylib
-// BUILD:  $CC main.c $BUILD_DIR/lc/libfoo.dylib -o $BUILD_DIR/restrict-search-lc-find.exe    -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/lc -DMODE=lc-find -DSHOULD_BE_FOUND=1
-// BUILD:  $CC main.c $BUILD_DIR/lc/libfoo.dylib -o $BUILD_DIR/restrict-search-lc-no-find.exe -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/lc -DMODE=lc-no-find -sectcreate __RESTRICT __restrict /dev/null
-// BUILD:  $CC foo.c -dynamiclib -o $BUILD_DIR/rpath/libfoo.dylib -install_name @rpath/libfoo.dylib
-// BUILD:  $CC main.c $BUILD_DIR/rpath/libfoo.dylib -o $BUILD_DIR/restrict-search-rpath-find.exe    -Wl,-rpath,@loader_path/rpath/ -DMODE=rpath-find -DSHOULD_BE_FOUND=1
-// BUILD:  $CC main.c $BUILD_DIR/rpath/libfoo.dylib -o $BUILD_DIR/restrict-search-rpath-no-find.exe -Wl,-rpath,@loader_path/rpath/ -DMODE=rpath-no-find -sectcreate __RESTRICT __restrict /dev/null
-
+// BUILD(macos):  $CC foo.c -dynamiclib -o $BUILD_DIR/lc/libfoo.dylib -install_name /blah/libfoo.dylib
+// BUILD(macos):  $CC main.c $BUILD_DIR/lc/libfoo.dylib -o $BUILD_DIR/restrict-search-lc-find.exe    -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/lc -DMODE=lc-find -DSHOULD_BE_FOUND=1
+// BUILD(macos):  $CC main.c $BUILD_DIR/lc/libfoo.dylib -o $BUILD_DIR/restrict-search-lc-no-find.exe -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/lc -DMODE=lc-no-find -sectcreate __RESTRICT __restrict /dev/null
+// BUILD(macos):  $CC foo.c -dynamiclib -o $BUILD_DIR/rpath/libfoo.dylib -install_name @rpath/libfoo.dylib
+// BUILD(macos):  $CC main.c $BUILD_DIR/rpath/libfoo.dylib -o $BUILD_DIR/restrict-search-rpath-find.exe    -Wl,-rpath,@loader_path/rpath/ -DMODE=rpath-find -DSHOULD_BE_FOUND=1
+// BUILD(macos):  $CC main.c $BUILD_DIR/rpath/libfoo.dylib -o $BUILD_DIR/restrict-search-rpath-no-find.exe -Wl,-rpath,@loader_path/rpath/ -DMODE=rpath-no-find -sectcreate __RESTRICT __restrict /dev/null
+
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  ./restrict-search-lc-find.exe
 // RUN:  ./restrict-search-lc-no-find.exe
index dcc3c40b3766a76b5a38cf3c8111aafb84754be0..e2319bf9b992e4822a8e9e4b33e74c0a0a23bce4 100644 (file)
@@ -1,6 +1,6 @@
-// BUILD_ONLY: MacOSX
+// BUILD(macos):  $CXX main.cpp -std=c++11 -o $BUILD_DIR/thread-local-atexit-macOS.exe
 
-// BUILD:  $CXX main.cpp -std=c++11 -o $BUILD_DIR/thread-local-atexit-macOS.exe
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  ./thread-local-atexit-macOS.exe
 
index 48da96e2d4d656eeeddaee705bf940a45a7550d3..d5994b2489e1072dfbbf0049da20f26cedd6fc3c 100644 (file)
@@ -1,12 +1,11 @@
 
 // This tests that our header such as dlfcn.h pass unix conformance.
 
-// BUILD_ONLY: MacOSX
+// BUILD(macos):  $CC main.c -o $BUILD_DIR/unix-conformance.exe -D_XOPEN_SOURCE=600
+// BUILD(macos):  $CC main.c -o $BUILD_DIR/scratch.exe -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112
+// BUILD(macos): $SKIP_INSTALL $BUILD_DIR/scratch.exe
 
-// BUILD:  $CC main.c -o $BUILD_DIR/unix-conformance.exe -D_XOPEN_SOURCE=600
-// BUILD:  $CC main.c -o $BUILD_DIR/scratch.exe -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112
-
-// BUILD: $SKIP_INSTALL $BUILD_DIR/scratch.exe
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  ./unix-conformance.exe
 
diff --git a/testing/test-cases/upward-links-initializers.dtest/init1.c b/testing/test-cases/upward-links-initializers.dtest/init1.c
new file mode 100644 (file)
index 0000000..b555a48
--- /dev/null
@@ -0,0 +1,7 @@
+extern void checkInitOrder(int expected);
+
+__attribute__((constructor))
+static void myInit()
+{
+    checkInitOrder(1);
+}
diff --git a/testing/test-cases/upward-links-initializers.dtest/init2.c b/testing/test-cases/upward-links-initializers.dtest/init2.c
new file mode 100644 (file)
index 0000000..fa408d7
--- /dev/null
@@ -0,0 +1,7 @@
+extern void checkInitOrder(int expected);
+
+__attribute__((constructor))
+static void myInit()
+{
+    checkInitOrder(2);
+}
diff --git a/testing/test-cases/upward-links-initializers.dtest/init3.c b/testing/test-cases/upward-links-initializers.dtest/init3.c
new file mode 100644 (file)
index 0000000..9ce9776
--- /dev/null
@@ -0,0 +1,7 @@
+extern void checkInitOrder(int expected);
+
+__attribute__((constructor))
+static void myInit()
+{
+    checkInitOrder(3);
+}
diff --git a/testing/test-cases/upward-links-initializers.dtest/init4.c b/testing/test-cases/upward-links-initializers.dtest/init4.c
new file mode 100644 (file)
index 0000000..7547284
--- /dev/null
@@ -0,0 +1,7 @@
+extern void checkInitOrder(int expected);
+
+__attribute__((constructor))
+static void myInit()
+{
+    checkInitOrder(4);
+}
diff --git a/testing/test-cases/upward-links-initializers.dtest/init5.c b/testing/test-cases/upward-links-initializers.dtest/init5.c
new file mode 100644 (file)
index 0000000..b81a6e4
--- /dev/null
@@ -0,0 +1,7 @@
+extern void checkInitOrder(int expected);
+
+__attribute__((constructor))
+static void myInit()
+{
+    checkInitOrder(5);
+}
diff --git a/testing/test-cases/upward-links-initializers.dtest/init6.c b/testing/test-cases/upward-links-initializers.dtest/init6.c
new file mode 100644 (file)
index 0000000..2c7eeb2
--- /dev/null
@@ -0,0 +1,7 @@
+extern void checkInitOrder(int expected);
+
+__attribute__((constructor))
+static void myInit()
+{
+    checkInitOrder(6);
+}
diff --git a/testing/test-cases/upward-links-initializers.dtest/main.c b/testing/test-cases/upward-links-initializers.dtest/main.c
new file mode 100644 (file)
index 0000000..c771f23
--- /dev/null
@@ -0,0 +1,42 @@
+// BUILD:  $CC value.c -dynamiclib -install_name $RUN_DIR/libvalue.dylib  -o $BUILD_DIR/libvalue.dylib
+// BUILD:  $CC init4.c -dynamiclib -install_name $RUN_DIR/libinit4.dylib  -o $BUILD_DIR/libinit4.dylib $BUILD_DIR/libvalue.dylib
+// BUILD:  $CC init5.c -dynamiclib -install_name $RUN_DIR/libinit5.dylib  -o $BUILD_DIR/libinit5.dylib $BUILD_DIR/libinit4.dylib $BUILD_DIR/libvalue.dylib
+// BUILD:  $CC init6.c -dynamiclib -install_name $RUN_DIR/libinit6.dylib  -o $BUILD_DIR/libinit6.dylib $BUILD_DIR/libinit5.dylib $BUILD_DIR/libvalue.dylib
+// BUILD:  $CC init1.c -dynamiclib -install_name $RUN_DIR/libinit1.dylib  -o $BUILD_DIR/libinit1.dylib -Wl,-upward_library,$BUILD_DIR/libinit6.dylib $BUILD_DIR/libvalue.dylib
+// BUILD:  $CC init2.c -dynamiclib -install_name $RUN_DIR/libinit2.dylib  -o $BUILD_DIR/libinit2.dylib $BUILD_DIR/libinit1.dylib $BUILD_DIR/libvalue.dylib
+// BUILD:  $CC init3.c -dynamiclib -install_name $RUN_DIR/libinit3.dylib  -o $BUILD_DIR/libinit3.dylib $BUILD_DIR/libinit2.dylib $BUILD_DIR/libvalue.dylib
+// BUILD:  $CC main.c -o $BUILD_DIR/upward-link-initializers.exe -DRUN_DIR="$RUN_DIR" $BUILD_DIR/libinit3.dylib
+
+
+/*
+ *     main ---> libinit3
+ *                   |
+ *                    -------|
+ *        libinit6          libinit2
+ *         |    ^            |
+ *         |    |            |
+ *         |    |            |
+ *   libinit5   -----------libinit1
+ *      |
+ *      |
+ *   libinit4
+ *
+ *   libinit1: lowest direct dependency from top level lib (libinit3)
+ *   libinit6: only ever brought via upward link dependency, and should not be initialized before libinit1
+ *   libinit4: lowest direct dependency from dangling upward (libinit6)
+ */
+
+// RUN:  ./upward-link-initializers.exe
+
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+
+#include "test_support.h"
+
+int main(int argc, const char* argv[], const char* envp[]) {
+    // Initializers tests passed
+    PASS("Success");
+}
+
diff --git a/testing/test-cases/upward-links-initializers.dtest/value.c b/testing/test-cases/upward-links-initializers.dtest/value.c
new file mode 100644 (file)
index 0000000..eccbb6f
--- /dev/null
@@ -0,0 +1,10 @@
+#include "test_support.h"
+
+static int initValue = 0;
+
+void checkInitOrder(int expected)
+{
+    initValue++;
+    if ( initValue != expected)
+            FAIL("Wrong init value in bar: %d, should have been %d.", initValue, expected);
+}
index 780d0874ed058d09e81bc1a19c2ae3ec8f6747c5..e4fbc9d8acd91dc43b2573853f441bca62f47cd9 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 -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
-// BUILD:           $CC main.c -o $BUILD_DIR/weak-def-bind-old-format.exe $BUILD_DIR/libfoo.dylib $BUILD_DIR/libbar.dylib -L$BUILD_DIR
+// BUILD(macos|x86_64):           $CC bar.c -mmacosx-version-min=10.5 -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib
+// BUILD(macos|x86_64):           $CC foo.c -mmacosx-version-min=10.5 -dynamiclib $BUILD_DIR/libbar.dylib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD(macos|x86_64):           $CC main.c -mmacosx-version-min=10.5 -o $BUILD_DIR/weak-def-bind-old-format.exe $BUILD_DIR/libfoo.dylib $BUILD_DIR/libbar.dylib -L$BUILD_DIR
+
+// BUILD(ios,tvos,watchos,bridgeos):
 
 // RUN:  ./weak-def-bind-old-format.exe
 
diff --git a/testing/test-cases/weak-def-unload.dtest/bar.c b/testing/test-cases/weak-def-unload.dtest/bar.c
new file mode 100644 (file)
index 0000000..e425999
--- /dev/null
@@ -0,0 +1 @@
+void bar() {}
diff --git a/testing/test-cases/weak-def-unload.dtest/foo.c b/testing/test-cases/weak-def-unload.dtest/foo.c
new file mode 100644 (file)
index 0000000..bf054b7
--- /dev/null
@@ -0,0 +1,7 @@
+
+__attribute__((weak))
+void weak1() { }
+
+__attribute__((weak))
+void weak2() { }
+
diff --git a/testing/test-cases/weak-def-unload.dtest/main.c b/testing/test-cases/weak-def-unload.dtest/main.c
new file mode 100644 (file)
index 0000000..a04e288
--- /dev/null
@@ -0,0 +1,34 @@
+// BUILD_ONLY: MacOSX
+
+// BUILD:           $CC bar.c -dynamiclib -install_name $RUN_DIR/libmissing.dylib -o $BUILD_DIR/libbar.dylib
+// BUILD:           $CC foo.c -dynamiclib $BUILD_DIR/libbar.dylib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib
+// BUILD:           $CC main.c -o $BUILD_DIR/MATLAB -DRUN_DIR="$RUN_DIR"
+
+// RUN:  ./MATLAB
+
+// libfoo.dylib was weak-def exported symbols.  This tests that it can fail to load cleanly.
+
+#include <stdio.h>
+#include <dlfcn.h>
+
+#include "test_support.h"
+
+
+
+int main(int argc, const char* argv[])
+{
+    // force weak-def table to be generated
+    void* handle = dlopen("/usr/lib/libc++.dylib", 0);
+    if ( handle == NULL ) {
+        FAIL("weak-def-unload, expected dlopen(libc++.dylib) to succeed");
+       }
+
+    // try to dlopen something with weak symbols that fails to load
+    handle = dlopen(RUN_DIR "/libfoo.dylib", 0);
+    if ( handle != NULL ) {
+        FAIL("weak-def-unload, expected dlopen to fail");
+       }
+
+    PASS("weak-def-unload");
+}
+